diff --git a/.beads/.gitignore b/.beads/.gitignore new file mode 100644 index 00000000..eb82c48f --- /dev/null +++ b/.beads/.gitignore @@ -0,0 +1,72 @@ +# Dolt database (managed by Dolt, not git) +dolt/ + +# Runtime files +bd.sock +bd.sock.startlock +sync-state.json +last-touched +.exclusive-lock + +# Daemon runtime (lock, log, pid) +daemon.* + +# Interactions log (runtime, not versioned) +interactions.jsonl + +# Push state (runtime, per-machine) +push-state.json + +# Lock files (various runtime locks) +*.lock + +# Credential key (encryption key for federation peer auth β€” never commit) +.beads-credential-key + +# Local version tracking (prevents upgrade notification spam after git ops) +.local_version + +# Worktree redirect file (contains relative path to main repo's .beads/) +# Must not be committed as paths would be wrong in other clones +redirect + +# Sync state (local-only, per-machine) +# These files are machine-specific and should not be shared across clones +.sync.lock +export-state/ +export-state.json + +# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned) +ephemeral.sqlite3 +ephemeral.sqlite3-journal +ephemeral.sqlite3-wal +ephemeral.sqlite3-shm + +# Dolt server management (auto-started by bd) +dolt-server.pid +dolt-server.log +dolt-server.lock +dolt-server.port +dolt-server.activity + +# Corrupt backup directories (created by bd doctor --fix recovery) +*.corrupt.backup/ + +# Backup data (auto-exported JSONL, local-only) +backup/ + +# Per-project environment file (Dolt connection config, GH#2520) +.env + +# Legacy files (from pre-Dolt versions) +*.db +*.db?* +*.db-journal +*.db-wal +*.db-shm +db.sqlite +bd.db +# NOTE: Do NOT add negation patterns here. +# They would override fork protection in .git/info/exclude. +# Config files (metadata.json, config.yaml) are tracked by git by default +# since no pattern above ignores them. diff --git a/.beads/README.md b/.beads/README.md new file mode 100644 index 00000000..dbfe3631 --- /dev/null +++ b/.beads/README.md @@ -0,0 +1,81 @@ +# Beads - AI-Native Issue Tracking + +Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code. + +## What is Beads? + +Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git. + +**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads) + +## Quick Start + +### Essential Commands + +```bash +# Create new issues +bd create "Add user authentication" + +# View all issues +bd list + +# View issue details +bd show + +# Update issue status +bd update --claim +bd update --status done + +# Sync with Dolt remote +bd dolt push +``` + +### Working with Issues + +Issues in Beads are: +- **Git-native**: Stored in Dolt database with version control and branching +- **AI-friendly**: CLI-first design works perfectly with AI coding agents +- **Branch-aware**: Issues can follow your branch workflow +- **Always in sync**: Auto-syncs with your commits + +## Why Beads? + +✨ **AI-Native Design** +- Built specifically for AI-assisted development workflows +- CLI-first interface works seamlessly with AI coding agents +- No context switching to web UIs + +πŸš€ **Developer Focused** +- Issues live in your repo, right next to your code +- Works offline, syncs when you push +- Fast, lightweight, and stays out of your way + +πŸ”§ **Git Integration** +- Automatic sync with git commits +- Branch-aware issue tracking +- Dolt-native three-way merge resolution + +## Get Started with Beads + +Try Beads in your own projects: + +```bash +# Install Beads +curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash + +# Initialize in your repo +bd init + +# Create your first issue +bd create "Try out Beads" +``` + +## Learn More + +- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs) +- **Quick Start Guide**: Run `bd quickstart` +- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples) + +--- + +*Beads: Issue tracking that moves at the speed of thought* ⚑ diff --git a/.beads/config.yaml b/.beads/config.yaml new file mode 100644 index 00000000..232b1511 --- /dev/null +++ b/.beads/config.yaml @@ -0,0 +1,54 @@ +# Beads Configuration File +# This file configures default behavior for all bd commands in this repository +# All settings can also be set via environment variables (BD_* prefix) +# or overridden with command-line flags + +# Issue prefix for this repository (used by bd init) +# If not set, bd init will auto-detect from directory name +# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. +# issue-prefix: "" + +# Use no-db mode: JSONL-only, no Dolt database +# When true, bd will use .beads/issues.jsonl as the source of truth +# no-db: false + +# Enable JSON output by default +# json: false + +# Feedback title formatting for mutating commands (create/update/close/dep/edit) +# 0 = hide titles, N > 0 = truncate to N characters +# output: +# title-length: 255 + +# Default actor for audit trails (overridden by BEADS_ACTOR or --actor) +# actor: "" + +# Export events (audit trail) to .beads/events.jsonl on each flush/sync +# When enabled, new events are appended incrementally using a high-water mark. +# Use 'bd export --events' to trigger manually regardless of this setting. +# events-export: false + +# Multi-repo configuration (experimental - bd-307) +# Allows hydrating from multiple repositories and routing writes to the correct database +# repos: +# primary: "." # Primary repo (where this database lives) +# additional: # Additional repos to hydrate from (read-only) +# - ~/beads-planning # Personal planning repo +# - ~/work-planning # Work planning repo + +# JSONL backup (periodic export for off-machine recovery) +# Auto-enabled when a git remote exists. Override explicitly: +# backup: +# enabled: false # Disable auto-backup entirely +# interval: 15m # Minimum time between auto-exports +# git-push: false # Disable git push (export locally only) +# git-repo: "" # Separate git repo for backups (default: project repo) + +# Integration settings (access with 'bd config get/set') +# These are stored in the database, not in this file: +# - jira.url +# - jira.project +# - linear.url +# - linear.api-key +# - github.org +# - github.repo diff --git a/.beads/hooks/post-checkout b/.beads/hooks/post-checkout new file mode 100755 index 00000000..67ad3276 --- /dev/null +++ b/.beads/hooks/post-checkout @@ -0,0 +1,24 @@ +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.0.0 --- +# This section is managed by beads. Do not remove these markers. +if command -v bd >/dev/null 2>&1; then + export BD_GIT_HOOK=1 + _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} + if command -v timeout >/dev/null 2>&1; then + timeout "$_bd_timeout" bd hooks run post-checkout "$@" + _bd_exit=$? + if [ $_bd_exit -eq 124 ]; then + echo >&2 "beads: hook 'post-checkout' timed out after ${_bd_timeout}s β€” continuing without beads" + _bd_exit=0 + fi + else + bd hooks run post-checkout "$@" + _bd_exit=$? + fi + if [ $_bd_exit -eq 3 ]; then + echo >&2 "beads: database not initialized β€” skipping hook 'post-checkout'" + _bd_exit=0 + fi + if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi +fi +# --- END BEADS INTEGRATION v1.0.0 --- diff --git a/.beads/hooks/post-merge b/.beads/hooks/post-merge new file mode 100755 index 00000000..a731aec6 --- /dev/null +++ b/.beads/hooks/post-merge @@ -0,0 +1,24 @@ +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.0.0 --- +# This section is managed by beads. Do not remove these markers. +if command -v bd >/dev/null 2>&1; then + export BD_GIT_HOOK=1 + _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} + if command -v timeout >/dev/null 2>&1; then + timeout "$_bd_timeout" bd hooks run post-merge "$@" + _bd_exit=$? + if [ $_bd_exit -eq 124 ]; then + echo >&2 "beads: hook 'post-merge' timed out after ${_bd_timeout}s β€” continuing without beads" + _bd_exit=0 + fi + else + bd hooks run post-merge "$@" + _bd_exit=$? + fi + if [ $_bd_exit -eq 3 ]; then + echo >&2 "beads: database not initialized β€” skipping hook 'post-merge'" + _bd_exit=0 + fi + if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi +fi +# --- END BEADS INTEGRATION v1.0.0 --- diff --git a/.beads/hooks/pre-commit b/.beads/hooks/pre-commit new file mode 100755 index 00000000..37ec2aa2 --- /dev/null +++ b/.beads/hooks/pre-commit @@ -0,0 +1,29 @@ +#!/usr/bin/env sh +set -e + +echo "β†’ lint-staged..." +pnpm lint-staged + +# --- BEGIN BEADS INTEGRATION v1.0.0 --- +# This section is managed by beads. Do not remove these markers. +if command -v bd >/dev/null 2>&1; then + export BD_GIT_HOOK=1 + _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} + if command -v timeout >/dev/null 2>&1; then + timeout "$_bd_timeout" bd hooks run pre-commit "$@" + _bd_exit=$? + if [ $_bd_exit -eq 124 ]; then + echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s β€” continuing without beads" + _bd_exit=0 + fi + else + bd hooks run pre-commit "$@" + _bd_exit=$? + fi + if [ $_bd_exit -eq 3 ]; then + echo >&2 "beads: database not initialized β€” skipping hook 'pre-commit'" + _bd_exit=0 + fi + if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi +fi +# --- END BEADS INTEGRATION v1.0.0 --- diff --git a/.beads/hooks/pre-push b/.beads/hooks/pre-push new file mode 100755 index 00000000..100dedd0 --- /dev/null +++ b/.beads/hooks/pre-push @@ -0,0 +1,46 @@ +#!/usr/bin/env sh +set -e + +echo "β†’ TypeScript..." +pnpm tsc + +echo "β†’ ESLint..." +pnpm lint + +echo "β†’ Drizzle schema validation..." +pnpm drizzle-kit check +pnpm drizzle-kit check --config=drizzle.biometrics.config.ts + +CHANGED=$(git diff --name-only @{push}.. -- '*.ts' '*.tsx' 2>/dev/null || git diff --name-only origin/dev...HEAD -- '*.ts' '*.tsx' 2>/dev/null || true) +if [ -n "$CHANGED" ]; then + echo "β†’ Tests (changed files)..." + pnpm vitest related --run $CHANGED +else + echo "β†’ Tests skipped (no TS/TSX changes)" +fi + +echo "βœ“ All pre-push checks passed" + +# --- BEGIN BEADS INTEGRATION v1.0.0 --- +# This section is managed by beads. Do not remove these markers. +if command -v bd >/dev/null 2>&1; then + export BD_GIT_HOOK=1 + _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} + if command -v timeout >/dev/null 2>&1; then + timeout "$_bd_timeout" bd hooks run pre-push "$@" + _bd_exit=$? + if [ $_bd_exit -eq 124 ]; then + echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s β€” continuing without beads" + _bd_exit=0 + fi + else + bd hooks run pre-push "$@" + _bd_exit=$? + fi + if [ $_bd_exit -eq 3 ]; then + echo >&2 "beads: database not initialized β€” skipping hook 'pre-push'" + _bd_exit=0 + fi + if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi +fi +# --- END BEADS INTEGRATION v1.0.0 --- diff --git a/.beads/hooks/prepare-commit-msg b/.beads/hooks/prepare-commit-msg new file mode 100755 index 00000000..c0c3ce1f --- /dev/null +++ b/.beads/hooks/prepare-commit-msg @@ -0,0 +1,24 @@ +#!/usr/bin/env sh +# --- BEGIN BEADS INTEGRATION v1.0.0 --- +# This section is managed by beads. Do not remove these markers. +if command -v bd >/dev/null 2>&1; then + export BD_GIT_HOOK=1 + _bd_timeout=${BEADS_HOOK_TIMEOUT:-300} + if command -v timeout >/dev/null 2>&1; then + timeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@" + _bd_exit=$? + if [ $_bd_exit -eq 124 ]; then + echo >&2 "beads: hook 'prepare-commit-msg' timed out after ${_bd_timeout}s β€” continuing without beads" + _bd_exit=0 + fi + else + bd hooks run prepare-commit-msg "$@" + _bd_exit=$? + fi + if [ $_bd_exit -eq 3 ]; then + echo >&2 "beads: database not initialized β€” skipping hook 'prepare-commit-msg'" + _bd_exit=0 + fi + if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi +fi +# --- END BEADS INTEGRATION v1.0.0 --- diff --git a/.beads/metadata.json b/.beads/metadata.json new file mode 100644 index 00000000..5eb46bf3 --- /dev/null +++ b/.beads/metadata.json @@ -0,0 +1,7 @@ +{ + "database": "dolt", + "backend": "dolt", + "dolt_mode": "embedded", + "dolt_database": "sleepypod_core", + "project_id": "5a4f3fbf-e881-4cce-9028-6182a505b21b" +} \ No newline at end of file diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..963a5382 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,26 @@ +{ + "hooks": { + "PreCompact": [ + { + "hooks": [ + { + "command": "bd prime", + "type": "command" + } + ], + "matcher": "" + } + ], + "SessionStart": [ + { + "hooks": [ + { + "command": "bd prime", + "type": "command" + } + ], + "matcher": "" + } + ] + } +} \ No newline at end of file diff --git a/.env.dev b/.env.dev index 4e2e010a..522fb3de 100644 --- a/.env.dev +++ b/.env.dev @@ -1 +1,2 @@ -DATABASE_URL="file:./db/sleepypod.core.dev.db" \ No newline at end of file +DATABASE_URL="file:./db/sleepypod.core.dev.db" +BIOMETRICS_DATABASE_URL="file:./biometrics.dev.db" \ No newline at end of file diff --git a/.env.prod b/.env.prod index ca836370..c5039f71 100644 --- a/.env.prod +++ b/.env.prod @@ -1 +1,2 @@ -DATABASE_URL="file:./db/sleepypod.core.prod.db" \ No newline at end of file +DATABASE_URL="file:./db/sleepypod.core.prod.db" +BIOMETRICS_DATABASE_URL="file:./biometrics.prod.db" \ No newline at end of file diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 00000000..750661a8 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,5 @@ +#!/usr/bin/env sh +set -e + +echo "β†’ lint-staged..." +pnpm lint-staged diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 00000000..6978f523 --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,22 @@ +#!/usr/bin/env sh +set -e + +echo "β†’ TypeScript..." +pnpm tsc + +echo "β†’ ESLint..." +pnpm lint + +echo "β†’ Drizzle schema validation..." +pnpm drizzle-kit check +pnpm drizzle-kit check --config=drizzle.biometrics.config.ts + +CHANGED=$(git diff --name-only @{push}.. -- '*.ts' '*.tsx' 2>/dev/null || git diff --name-only origin/dev...HEAD -- '*.ts' '*.tsx' 2>/dev/null || true) +if [ -n "$CHANGED" ]; then + echo "β†’ Tests (changed files)..." + pnpm vitest related --run $CHANGED +else + echo "β†’ Tests skipped (no TS/TSX changes)" +fi + +echo "βœ“ All pre-push checks passed" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c2bddb08..f0554aaa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,15 +14,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v4 with: - node-version: '24' + node-version-file: '.node-version' cache: 'pnpm' - name: Install dependencies @@ -47,7 +47,7 @@ jobs: mv /tmp/sleepypod-core.tar.gz . - name: Upload artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@v4 with: name: sleepypod-core path: sleepypod-core.tar.gz diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 83bdc798..0d22b75c 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -17,15 +17,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v4 with: - node-version: 'lts/*' + node-version-file: '.node-version' cache: 'pnpm' - name: Install dependencies @@ -34,6 +34,9 @@ jobs: - name: Build run: pnpm build + - name: Set install script default branch + run: sed -i 's/__BRANCH__/${{ github.ref_name }}/' scripts/install + - name: Create deploy tarball run: | tar czf /tmp/sleepypod-core.tar.gz \ @@ -54,9 +57,9 @@ jobs: run: | # --cleanup-tag removed: it can delete a branch named 'dev' on older gh versions gh release delete dev --yes 2>/dev/null || true - git push -d origin refs/tags/dev 2>/dev/null || true + git push -d --no-verify origin refs/tags/dev 2>/dev/null || true git tag -f dev - git push -f origin refs/tags/dev + git push -f --no-verify origin refs/tags/dev gh release create dev sleepypod-core.tar.gz \ --target "${{ github.sha }}" \ --title "Dev (latest)" \ diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml index 1034c889..de45a725 100644 --- a/.github/workflows/openapi.yml +++ b/.github/workflows/openapi.yml @@ -12,15 +12,15 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v4 - name: Setup pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v4 with: - node-version: '24' + node-version-file: '.node-version' cache: 'pnpm' - name: Install dependencies @@ -62,7 +62,7 @@ jobs: run: kill "$SERVER_PID" 2>/dev/null || true - name: Upload artifact - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@v4 with: name: ${{ github.ref_name == 'main' && 'openapi' || 'openapi-dev' }} path: openapi.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 24f71936..393606dc 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,19 +18,19 @@ jobs: id-token: write # to enable use of OIDC for trusted publishing and npm provenance steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v5 with: run_install: false - name: Set up Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v4 with: - node-version: "lts/*" + node-version-file: '.node-version' cache: pnpm - name: Install dependencies @@ -39,6 +39,9 @@ jobs: - name: Build run: pnpm build + - name: Set install script default branch + run: sed -i 's/__BRANCH__/${{ github.ref_name }}/' scripts/install + - name: Create deploy tarball run: | tar czf /tmp/sleepypod-core.tar.gz \ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e83adc3..331fd519 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,25 +16,25 @@ jobs: fail-fast: false matrix: task: - - { name: "Lint", cmd: "CHANGED=$(git diff --name-only --diff-filter=d origin/${{ github.event.pull_request.base.ref }}...HEAD -- '*.ts' '*.tsx' | head -200); if [ -n \"$CHANGED\" ]; then echo \"$CHANGED\" | xargs pnpm eslint; else echo 'No TS/TSX files changed'; fi" } + - { name: "Lint", cmd: "CHANGED=$(git diff --name-only --diff-filter=d origin/${{ github.event.pull_request.base.ref }}...HEAD -- '*.ts' '*.tsx' | head -200); if [ -n \"$CHANGED\" ]; then echo \"$CHANGED\" | xargs -d '\\n' pnpm eslint --; else echo 'No TS/TSX files changed'; fi" } - { name: "Typecheck", cmd: "pnpm tsc" } - { name: "Unit Tests", cmd: "pnpm test run --coverage --passWithNoTests" } steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Install pnpm - uses: pnpm/action-setup@v4 + uses: pnpm/action-setup@v5 with: run_install: false - name: Setup Node - uses: actions/setup-node@v6 + uses: actions/setup-node@v4 with: - node-version: "lts/*" + node-version-file: '.node-version' cache: 'pnpm' registry-url: 'https://npm.pkg.github.com' diff --git a/.gitignore b/.gitignore index 4f2d5316..ca5d5aa0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ node_modules !.yarn/sdks !.yarn/versions +# Python (uv) +.venv + # Build artifacts .git-info @@ -39,4 +42,9 @@ sleepypod.local.db-wal # Claude Code .claude/worktrees .claude/reviews -.reviews \ No newline at end of file +.reviews + +# Beads / Dolt files (added by bd init) +.dolt/ +*.db +.beads-credential-key diff --git a/.node-version b/.node-version new file mode 100644 index 00000000..2bd5a0a9 --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +22 diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..1c85d090 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +public-hoist-pattern[]=better-sqlite3 diff --git a/.serena/project.yml b/.serena/project.yml index c1fbd7c3..d6f9e9f9 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -136,3 +136,17 @@ symbol_info_budget: # list of regex patterns which, when matched, mark a memory entry as read‑only. # Extends the list from the global configuration, merging the two lists. read_only_memory_patterns: [] + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} diff --git a/.wiki-compiler.json b/.wiki-compiler.json new file mode 100644 index 00000000..cd5a8927 --- /dev/null +++ b/.wiki-compiler.json @@ -0,0 +1,11 @@ +{ + "version": 1, + "name": "sleepypod core", + "sources": [ + { "path": "docs/", "exclude": ["wiki/"] } + ], + "output": "docs/wiki/", + "mode": "staging", + "topic_hints": [], + "link_style": "obsidian" +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..a23a2bfa --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,64 @@ +## Knowledge Base + +A compiled knowledge wiki is available at `docs/wiki/`. + +**Session startup:** Read `wiki/INDEX.md` for a topic overview, then read +specific topic articles relevant to your current task. + +**Using coverage indicators:** Each section has a coverage tag: +- `[coverage: high]` -- trust this section, skip the raw files. +- `[coverage: medium]` -- good overview, check raw sources for granular questions. +- `[coverage: low]` -- read the raw sources listed in that section directly. + +**When you need depth:** Check the article's Sources section for links to +raw files. Only read raw sources for medium/low coverage sections or when +you need very specific detail. + +**Never modify wiki files directly** -- they are regenerated by `/wiki-compile`. + + +## Beads Issue Tracker + +This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands. + +### Quick Reference + +```bash +bd ready # Find available work +bd show # View issue details +bd update --claim # Claim work +bd close # Complete work +``` + +### Rules + +- Use `bd` for ALL task tracking β€” do NOT use TodoWrite, TaskCreate, or markdown TODO lists +- Run `bd prime` for detailed command reference and session close protocol +- Use `bd remember` for persistent knowledge β€” do NOT use MEMORY.md files + +## Session Completion + +**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds. + +**MANDATORY WORKFLOW:** + +1. **File issues for remaining work** - Create issues for anything that needs follow-up +2. **Run quality gates** (if code changed) - Tests, linters, builds +3. **Update issue status** - Close finished work, update in-progress items +4. **PUSH TO REMOTE** - This is MANDATORY: + ```bash + git pull --rebase + bd dolt push + git push + git status # MUST show "up to date with origin" + ``` +5. **Clean up** - Clear stashes, prune remote branches +6. **Verify** - All changes committed AND pushed +7. **Hand off** - Provide context for next session + +**CRITICAL RULES:** +- Work is NOT complete until `git push` succeeds +- NEVER stop before pushing - that leaves work stranded locally +- NEVER say "ready to push when you are" - YOU must push +- If push fails, resolve and retry until it succeeds + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..50af487d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,69 @@ +# Project Instructions for AI Agents + +This file provides instructions and context for AI coding agents working on this project. + + +## Beads Issue Tracker + +This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands. + +### Quick Reference + +```bash +bd ready # Find available work +bd show # View issue details +bd update --claim # Claim work +bd close # Complete work +``` + +### Rules + +- Use `bd` for ALL task tracking β€” do NOT use TodoWrite, TaskCreate, or markdown TODO lists +- Run `bd prime` for detailed command reference and session close protocol +- Use `bd remember` for persistent knowledge β€” do NOT use MEMORY.md files + +## Session Completion + +**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds. + +**MANDATORY WORKFLOW:** + +1. **File issues for remaining work** - Create issues for anything that needs follow-up +2. **Run quality gates** (if code changed) - Tests, linters, builds +3. **Update issue status** - Close finished work, update in-progress items +4. **PUSH TO REMOTE** - This is MANDATORY: + ```bash + git pull --rebase + bd dolt push + git push + git status # MUST show "up to date with origin" + ``` +5. **Clean up** - Clear stashes, prune remote branches +6. **Verify** - All changes committed AND pushed +7. **Hand off** - Provide context for next session + +**CRITICAL RULES:** +- Work is NOT complete until `git push` succeeds +- NEVER stop before pushing - that leaves work stranded locally +- NEVER say "ready to push when you are" - YOU must push +- If push fails, resolve and retry until it succeeds + + + +## Build & Test + +_Add your build and test commands here_ + +```bash +# Example: +# npm install +# npm test +``` + +## Architecture Overview + +_Add a brief overview of your project architecture_ + +## Conventions & Patterns + +_Add your project-specific conventions here_ diff --git a/app/[lang]/layout.tsx b/app/[lang]/layout.tsx index 63dc7b1c..0c831d11 100644 --- a/app/[lang]/layout.tsx +++ b/app/[lang]/layout.tsx @@ -7,12 +7,14 @@ import { LinguiClientProvider } from '@/src/providers/LinguiClientProvider' import { SideProvider } from '@/src/providers/SideProvider' import { TRPCProvider } from '@/src/providers/TRPCProvider' import { setI18n } from '@lingui/react/server' -import linguiConfig from 'lingui.config' +// Locales inlined to avoid importing lingui.config.ts at SSG time. +// Turbopack's module resolution fails for this import on cross-platform deploys. +const LOCALES = ['en', 'es', 'pseudo'] export const dynamicParams = false export function generateStaticParams() { - return linguiConfig.locales.map((lang: string) => ({ lang })) + return LOCALES.map(lang => ({ lang })) } export default async function LangLayout({ diff --git a/biometrics.dev.db-shm b/biometrics.dev.db-shm index fe9ac284..caf71c2c 100644 Binary files a/biometrics.dev.db-shm and b/biometrics.dev.db-shm differ diff --git a/biometrics.dev.db-wal b/biometrics.dev.db-wal index e69de29b..8bd23f71 100644 Binary files a/biometrics.dev.db-wal and b/biometrics.dev.db-wal differ diff --git a/docs/adr/0017-uv-python-package-management.md b/docs/adr/0017-uv-python-package-management.md new file mode 100644 index 00000000..f694638a --- /dev/null +++ b/docs/adr/0017-uv-python-package-management.md @@ -0,0 +1,61 @@ +# ADR: Use uv for Python package management + +## Context + +Pod 3 (Python 3.9) and Pod 4 (Python 3.10) run Yocto-based Linux with an incomplete Python stdlib. Key missing modules: + +- **`pyexpat`** β€” C extension for XML parsing, cannot be fixed by copying `.py` files +- **`plistlib`** β€” depends on `pyexpat`, also unfixable +- **`ensurepip`** β€” partially fixable via stdlib patching, but unreliable + +This broke the entire Python venv+pip chain: +1. `python3 -m venv` fails (needs `ensurepip`) +2. `python3 -m venv --without-pip` + `get-pip.py` fails (`get-pip.py` needs `pyexpat` for XML parsing) +3. No pip β†’ no package installation β†’ biometrics modules can't be set up + +We maintained two fragile scripts to work around this: +- `scripts/patch-python-stdlib` β€” downloads matching CPython source, copies missing `.py` files into system lib dir +- `scripts/setup-python-venv` β€” multi-fallback venv creation (normal β†’ `--without-pip` + `get-pip.py`) + +Both scripts failed on Pod 3 because the root cause (`pyexpat` being a missing C extension) is unfixable via `.py` patching. See [#380](https://github.com/sleepypod/core/issues/380). + +## Decision + +Replace `venv` + `pip` + `patch-python-stdlib` + `setup-python-venv` with [uv](https://docs.astral.sh/uv/). + +uv is a Rust-based Python package manager from Astral. It creates virtualenvs and installs packages without using Python's stdlib β€” no `ensurepip`, `pyexpat`, or `pip` needed. It ships as a single static binary. + +Each biometrics module gets a `pyproject.toml` (replacing `requirements.txt`) and a `uv.lock` for reproducible installs. The install script runs `uv sync` per module, which creates `.venv/` and installs locked dependencies. + +### What changes + +| Before | After | +|--------|-------| +| `scripts/patch-python-stdlib` | Deleted | +| `scripts/setup-python-venv` | Deleted | +| `requirements.txt` per module | `pyproject.toml` + `uv.lock` per module | +| `venv/` directory | `.venv/` directory (uv default) | +| `venv/bin/pip install -r requirements.txt` | `uv sync` | + +### Why uv specifically + +- **Bypasses broken stdlib entirely** β€” venv creation and package installation are implemented in Rust, not delegated to Python +- **Single static binary** β€” works on Yocto/musl without dependencies, installed via `curl | sh` +- **Lockfile support** β€” `uv.lock` provides reproducible builds with hashed dependencies +- **Fast** β€” 10-100x faster than pip for dependency resolution and installation +- **Widely adopted** β€” backed by Astral (creators of ruff), active maintenance + +### Why not alternatives + +- **pip + venv**: The approach we're replacing. Fundamentally broken on Yocto without `pyexpat`. +- **pipx**: Still delegates to pip/venv internally. +- **conda/mamba**: Heavy runtime, not suited for embedded deployment. +- **Poetry**: Uses pip under the hood for installation. + +## Consequences + +- uv (~30MB static binary) is downloaded during install β€” adds ~2s to install time +- `astral.sh` becomes an install-time dependency (alongside `github.com`, `nodejs.org`, `npmjs.org`) +- Existing installs with `venv/` directories will have orphaned dirs after update (harmless, can be cleaned manually) +- The biometrics modules' Python code is unchanged β€” only the packaging and environment setup changes +- Works identically on Pod 3, 4, and 5 β€” no pod-generation-specific branching needed diff --git a/docs/snoo-pentest-plan.md b/docs/snoo-pentest-plan.md new file mode 100644 index 00000000..fa68b9ed --- /dev/null +++ b/docs/snoo-pentest-plan.md @@ -0,0 +1,768 @@ +# Snoo Smart Bassinet β€” Pentest Methodology & Reconnaissance Plan + +**Date:** 2026-04-10 +**Target:** Happiest Baby Snoo Smart Sleeper Bassinet +**Objective:** Understand device-side protocol sufficiently to build a fully local server replacement (no cloud dependency) +**Authorization:** Owner-authorized security research on own device + +--- + +## Table of Contents + +1. [Hardware Teardown Research](#1-hardware-teardown-research) +2. [Network Reconnaissance Plan](#2-network-reconnaissance-plan) +3. [Protocol Analysis Plan](#3-protocol-analysis-plan) +4. [pysnoo2 / python-snoo Deep Dive](#4-pysnoo2--python-snoo-deep-dive) +5. [Local Server Replacement Architecture](#5-local-server-replacement-architecture) + +--- + +## 1. Hardware Teardown Research + +### FCC Filings + +The Snoo has multiple FCC registrations under grantee code **2AH7Y** (Happiest Baby, Inc.): + +| FCC ID | Model | Notes | +|--------|-------|-------| +| **2AH7Y-101067400** | Original Snoo | Filed ~2016, internal photos available | +| **2AH7YS1000-01** | Snoo (newer revision) | Separate filing, likely hardware refresh | +| **2AH7YSPHUB01** | Snoo Power Hub | Accessory device | + +**Key FCC documents to retrieve:** +- Internal photos: https://fccid.io/2AH7Y-101067400/Internal-Photos/Internal-photos-3181230 +- Test report: https://fccid.io/2AH7Y-101067400/Test-Report/Test-Report-3181229 +- User manual: https://fccid.io/2AH7Y-101067400/User-Manual/User-Manual-3181236 +- Newer model: https://fccid.io/2AH7YS1000-01 + +### Firmware & OS + +Per Softeq (the development firm that built the Snoo software): + +- **OS:** Linux-based +- **Test framework:** Python (automated testing) +- **Cloud backend:** AWS +- **Real-time comms:** PubNub (legacy), now AWS IoT Core MQTT +- **Mobile app:** Xamarin (cross-platform iOS/Android) +- **Auth provider:** AWS Cognito (current), previously custom OAuth +- **OTA updates:** Firmware updates are pushed OTA from the backend. When a new major firmware update is released, the backend automatically finds bassinets with previous versions and updates them remotely. + +### Known Firmware Versions (from pysnoo2 README examples) + +- `v1.14.12` β€” seen in 2021 examples +- `v1.14.22` β€” seen in 2023 examples + +### Hardware Reconnaissance Commands + +Once device is on the network, identify the WiFi module from OUI: + +```bash +# After identifying device IP (see Section 2), check MAC vendor +# The first 3 octets of MAC identify the manufacturer +# Cross-reference at https://maclookup.app/ or https://hwaddress.com/ +``` + +### Prior Security Research + +**Red Balloon Security** (2019) found vulnerabilities allowing: +- Remote control of motor and speaker from same WiFi network +- Bypass of built-in safety limiters (motor speed, speaker volume) +- No authentication required for local commands at the time +- Vulnerabilities were patched via OTA update + +This suggests the device historically had an open local attack surface on the WiFi network, which may or may not still be present after patching. + +### Physical Debug Access + +No UART/JTAG/serial debug header documentation has been found in public teardowns. The FCC internal photos are the best bet for identifying test points. Download the internal photos from the FCC filing and look for: + +- Unpopulated pin headers on the main PCB +- UART pads (usually 4-pin: VCC, TX, RX, GND) +- JTAG headers (usually 10 or 20 pin) +- SWD pads (2-pin: SWDIO, SWCLK) + +--- + +## 2. Network Reconnaissance Plan + +### Phase 1: Device Discovery + +```bash +# Find the Snoo on the local network + +# ARP scan β€” find all devices +sudo arp-scan -l + +# mDNS/Bonjour discovery β€” check if Snoo advertises services +dns-sd -B _tcp . # macOS +# or +avahi-browse -art # Linux + +# Nmap host discovery on local subnet +nmap -sn 192.168.1.0/24 + +# Once you have the IP, get the MAC for OUI lookup +arp -a | grep +``` + +### Phase 2: Port Scanning + +```bash +SNOO_IP="" + +# Full TCP port scan with service detection +nmap -sV -sC -p- -O $SNOO_IP -oN snoo-full-scan.txt + +# UDP scan (top 1000 ports β€” UDP scans are slow) +sudo nmap -sU --top-ports 1000 $SNOO_IP -oN snoo-udp-scan.txt + +# Aggressive scan with OS fingerprinting +nmap -A -T4 $SNOO_IP -oN snoo-aggressive-scan.txt + +# Check for specific IoT ports +nmap -p 80,443,8080,8443,1883,8883,5353,5683,22,23,554 -sV $SNOO_IP +``` + +**Ports of interest:** +- **1883/8883** β€” MQTT (unencrypted/TLS) +- **443** β€” HTTPS / WSS (AWS IoT MQTT over WebSocket) +- **80/8080** β€” HTTP (config interface?) +- **22** β€” SSH (debug access?) +- **23** β€” Telnet (debug access?) +- **5353** β€” mDNS +- **5683** β€” CoAP + +### Phase 3: Traffic Capture + +```bash +SNOO_IP="" +INTERFACE="en0" # or your network interface + +# Capture all Snoo traffic +sudo tcpdump -i $INTERFACE host $SNOO_IP -w snoo-traffic.pcap -v + +# DNS queries only (what domains does it resolve?) +sudo tcpdump -i $INTERFACE host $SNOO_IP and port 53 -w snoo-dns.pcap -v + +# TLS traffic (for JA3/JA4 fingerprinting later) +sudo tcpdump -i $INTERFACE host $SNOO_IP and port 443 -w snoo-tls.pcap -v + +# MQTT traffic specifically +sudo tcpdump -i $INTERFACE host $SNOO_IP and '(port 1883 or port 8883)' -w snoo-mqtt.pcap -v +``` + +### Phase 4: DNS Analysis + +```bash +# Option A: Run a local DNS server that logs queries, point Snoo at it +# (Requires DHCP change or static DNS on Snoo's VLAN) + +# Option B: Monitor DNS at the router level +sudo tcpdump -i $INTERFACE host $SNOO_IP and port 53 -l | tee snoo-dns-queries.txt + +# Option C: Use tshark for parsed DNS output +tshark -i $INTERFACE -f "host $SNOO_IP and port 53" -T fields \ + -e frame.time -e dns.qry.name -e dns.resp.addr +``` + +**Expected DNS lookups based on source code analysis:** +- `cognito-idp.us-east-1.amazonaws.com` β€” AWS Cognito auth +- `api-us-east-1-prod.happiestbaby.com` β€” Snoo REST API +- `happiestbaby.pubnubapi.com` β€” PubNub (legacy) +- `*.iot.us-east-1.amazonaws.com` β€” AWS IoT Core MQTT endpoint (per-account) +- Various AWS infrastructure domains + +### Phase 5: TLS Fingerprinting + +```bash +# Capture TLS Client Hello for JA3/JA4 fingerprinting +# This identifies the TLS library the device uses + +# Using tshark +tshark -i $INTERFACE -f "host $SNOO_IP and port 443" \ + -T fields -e ip.src -e tls.handshake.ja3 -e tls.handshake.extensions.server_name + +# Or use ja3 tool: https://github.com/salesforce/ja3 +# Fingerprint reveals if it's using OpenSSL, mbedTLS, WolfSSL, etc. +``` + +--- + +## 3. Protocol Analysis Plan + +### Architecture Overview + +The Snoo uses a **dual-protocol** cloud architecture: + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Snoo App │───────▢│ AWS Cognito (Auth) β”‚ β”‚ β”‚ +β”‚ (Xamarin) β”‚ β”‚ us-east-1 β”‚ β”‚ Snoo β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Bassinet β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ (Linux) β”‚ +β”‚ │───────▢│ Snoo REST API β”‚ β”‚ β”‚ +β”‚ β”‚ β”‚ api-us-east-1-prod... β”‚ β”‚ β”‚ +β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ +β”‚ │◀══════▢│ AWS IoT Core (MQTT/WSS) │◀═════▢│ β”‚ +β”‚ β”‚ β”‚ {thing}.iot.us-east-1... β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β•‘ + β”Œβ”€β”€β”€β”€β”€β•¨β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ PubNub (Legacy) β”‚ + β”‚ happiestbaby.pubnubapi β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Authentication Flow (Current β€” AWS Cognito + AWS IoT) + +Derived from `python-snoo` (Lash-L/python-snoo, used by HA integration): + +**Step 1: AWS Cognito authentication** +```bash +curl -X POST https://cognito-idp.us-east-1.amazonaws.com/ \ + -H "x-amz-target: AWSCognitoIdentityProviderService.InitiateAuth" \ + -H "content-type: application/x-amz-json-1.1" \ + -H "user-agent: okhttp/4.12.0" \ + -d '{ + "AuthParameters": { + "PASSWORD": "", + "USERNAME": "" + }, + "AuthFlow": "USER_PASSWORD_AUTH", + "ClientId": "6kqofhc8hm394ielqdkvli0oea" + }' +``` + +Response yields: `AccessToken`, `IdToken`, `RefreshToken`, `ExpiresIn` + +**Step 2: Snoo PubNub token (still needed even for MQTT path)** +```bash +curl -X POST https://api-us-east-1-prod.happiestbaby.com/us/me/v10/pubnub/authorize \ + -H "authorization: Bearer " \ + -H "content-type: application/json; charset=UTF-8" \ + -H "user-agent: okhttp/4.12.0" \ + -d '{ + "advertiserId": "", + "appVersion": "1.8.7", + "device": "panther", + "deviceHasGSM": true, + "locale": "en", + "os": "Android", + "osVersion": "14", + "platform": "Android", + "timeZone": "America/New_York", + "userCountry": "US", + "vendorId": "eyqurgwYQSqmnExnzyiLO5" + }' +``` + +Response: `{"snoo": {"token": ""}}` + +**Step 3: Get devices (includes AWS IoT endpoint)** +```bash +curl https://api-us-east-1-prod.happiestbaby.com/hds/me/v11/devices \ + -H "authorization: Bearer " +``` + +Response includes: +```json +{ + "snoo": [{ + "serialNumber": "...", + "firmwareVersion": "v1.14.22", + "awsIoT": { + "awsRegion": "us-east-1", + "clientEndpoint": ".iot.us-east-1.amazonaws.com", + "clientReady": true, + "thingName": "" + }, + "lastSSID": {"name": "...", "updatedAt": "..."}, + "babyIds": ["..."] + }] +} +``` + +**Step 4: Token refresh** +```bash +curl -X POST https://cognito-idp.us-east-1.amazonaws.com/ \ + -H "x-amz-target: AWSCognitoIdentityProviderService.InitiateAuth" \ + -H "content-type: application/x-amz-json-1.1" \ + -d '{ + "AuthParameters": {"REFRESH_TOKEN": ""}, + "AuthFlow": "REFRESH_TOKEN_AUTH", + "ClientId": "6kqofhc8hm394ielqdkvli0oea" + }' +``` + +### MQTT Protocol (Current) + +**Connection details** (from `python-snoo` source): +``` +Host: {device.awsIoT.clientEndpoint} (e.g., .iot.us-east-1.amazonaws.com) +Port: 443 +Transport: WebSocket +Path: /mqtt +Protocol: MQTT v3.1 +TLS: Standard SSL context +Username: ?SDK=iOS&Version=2.40.1 +Password: (none) +Headers: {"token": ""} +Client ID: HA_{uuid4} (or any unique identifier) +``` + +**Topics:** + +| Direction | Topic Pattern | Purpose | +|-----------|--------------|---------| +| Subscribe (device β†’ client) | `{thingName}/state_machine/activity_state` | Real-time device state updates | +| Publish (client β†’ device) | `{thingName}/state_machine/control` | Send commands to device | + +**Command message format** (published to control topic): + +```json +// Start the Snoo +{"ts": 17128500000000, "command": "start_snoo"} + +// Stop the Snoo +{"ts": 17128500000000, "command": "go_to_state", "state": "ONLINE", "hold": "off"} + +// Set to specific level with hold +{"ts": 17128500000000, "command": "go_to_state", "state": "LEVEL2", "hold": "on"} + +// Enable sticky white noise +{"ts": 17128500000000, "command": "set_sticky_white_noise", "state": "on", "timeout_min": 15} + +// Request current status +{"ts": 17128500000000, "command": "send_status"} +``` + +Note: `ts` is `int(datetime.now().timestamp() * 10_000_000)` (epoch in units of 100 nanoseconds). + +**ActivityState message** (received on activity_state topic): + +```json +{ + "left_safety_clip": 1, + "rx_signal": {"rssi": -45, "strength": 99}, + "right_safety_clip": 1, + "sw_version": "v1.14.22", + "event_time_ms": 1698816595667, + "state_machine": { + "up_transition": "NONE", + "since_session_start_ms": -1, + "sticky_white_noise": "off", + "weaning": "off", + "time_left": -1, + "session_id": "0", + "state": "ONLINE", + "is_active_session": false, + "down_transition": "NONE", + "hold": "off", + "audio": "on" + }, + "system_state": "normal", + "event": "status_requested" +} +``` + +**Session states** (state machine values): +- `ONLINE` β€” idle/off +- `BASELINE` β€” lowest soothing level +- `WEANING_BASELINE` β€” weaning mode baseline +- `LEVEL1` through `LEVEL4` β€” increasing soothing intensity +- `PRETIMEOUT` β€” about to time out +- `TIMEOUT` β€” timed out +- `SUSPENDED` β€” safety suspension +- `UNRECOVERABLE_SUSPENDED` β€” hardware fault +- `UNRECOVERABLE_ERROR` β€” error state + +**Event types:** +- `activity` β€” motion/state change +- `cry` β€” cry detected +- `timer` β€” timer event +- `command` β€” command acknowledgment +- `safety_clip` β€” clip attached/detached +- `status_requested` β€” response to status query +- `sticky_white_noise_updated` β€” noise setting changed +- `long_activity_press` β€” button held +- `power` β€” power event +- `config_change` β€” settings changed +- `restart` β€” device restart + +### PubNub Protocol (Legacy β€” may still work) + +**Connection details:** +``` +Subscribe Key: sub-c-97bade2a-483d-11e6-8b3b-02ee2ddab7fe +Publish Key: pub-c-699074b0-7664-4be2-abf8-dcbb9b6cd2bf +Origin: happiestbaby.pubnubapi.com +Auth Key: +UUID: pn-pysnoo-{serial_number} (or any unique ID) +``` + +**Channels:** +- `ActivityState.{serialNumber}` β€” device publishes state here +- `ControlCommand.{serialNumber}` β€” app publishes commands here + +**PubNub command format** (simpler, no timestamp): +```json +{"command": "start_snoo"} +{"command": "go_to_state", "state": "LEVEL1"} +{"command": "go_to_state", "state": "LEVEL1", "hold": "on"} +{"command": "go_to_state", "state": "ONLINE"} +``` + +**PubNub REST API for history** (useful for passive recon without library): +```bash +SERIAL="" +TOKEN="" +curl "https://happiestbaby.pubnubapi.com/v2/history/sub-key/sub-c-97bade2a-483d-11e6-8b3b-02ee2ddab7fe/channel/ActivityState.${SERIAL}?auth=${TOKEN}&count=1&include_token=true&include_meta=false&reverse=false&pnsdk=PubNub-Kotlin/7.4.0&uuid=recon_$(uuidgen)" +``` + +### MITM Setup + +**Option A: mitmproxy for HTTPS REST API traffic** +```bash +# Start mitmproxy +mitmproxy --mode transparent --listen-port 8080 + +# Or for automated capture +mitmdump --mode transparent --listen-port 8080 -w snoo-capture.flow + +# Configure your router/firewall to redirect Snoo traffic through proxy +# On macOS with pf: +# echo "rdr pass on en0 proto tcp from $SNOO_IP to any port 443 -> 127.0.0.1 port 8080" | sudo pfctl -ef - +``` + +Note: The Snoo likely does TLS certificate pinning. MITM may fail for direct device traffic. The REST API calls from the mobile app may be easier to intercept. + +**Option B: Intercept the mobile app instead** +```bash +# Install mitmproxy CA cert on phone +# Use mitmproxy in regular mode +mitmproxy --listen-port 8080 + +# Set phone's proxy to :8080 +# Open Snoo app, capture traffic +``` + +**Option C: DNS redirect + custom endpoint** +```bash +# Run a local DNS server that redirects Snoo domains +# Point the Snoo's DNS (via DHCP) to your machine +# This reveals what domains the device queries and in what order +# See Section 5 for the full local server approach +``` + +--- + +## 4. pysnoo2 / python-snoo Deep Dive + +### Library Ecosystem + +There are several Python libraries for Snoo interaction: + +| Library | Author | Protocol | Status | +|---------|--------|----------|--------| +| `python-snoo` | Lash-L | MQTT (AWS IoT) + PubNub | **Active** β€” used by HA core integration | +| `pysnoo2` | DanPatten | PubNub only | Maintained, PubNub-only | +| `pysnoo` | rado0x54 | PubNub only | Original, less active | +| `pysnooapi` | sanghviharshit | REST API only | REST-only, no real-time | + +### python-snoo (Lash-L) β€” The Reference Implementation + +This is the library used by the official Home Assistant Snoo integration (`python-snoo==0.8.3`). It has **both PubNub and MQTT support**, with MQTT being the current primary path. + +**Key files:** +- `python_snoo/snoo.py` β€” Main API class with auth, MQTT subscribe, command sending +- `python_snoo/containers.py` β€” Data models including `AwsIOT`, `SnooDevice`, `SnooData` +- `python_snoo/commands.py` β€” Command enum (`start_snoo`, `go_to_state`, etc.) +- `python_snoo/pubnub_async.py` β€” Legacy PubNub subscription handler + +**Auth flow in code:** +1. `authorize()` β†’ calls `auth_amazon()` then `auth_snoo()` +2. Schedules token refresh 5 minutes before expiry +3. On refresh: cancels MQTT tasks, refreshes AWS Cognito tokens, re-subscribes + +**MQTT subscription in code:** +1. `start_subscribe(device, callback)` β†’ creates asyncio task for `subscribe_mqtt()` +2. `subscribe_mqtt()` connects via `aiomqtt.Client` with WebSocket transport +3. Subscribes to `{thingName}/state_machine/activity_state` +4. Messages deserialized to `SnooData` and passed to callback + +**Command sending in code:** +1. `send_command(command, device, **kwargs)` waits for MQTT client connection +2. Publishes to `{thingName}/state_machine/control` +3. Payload: `{"ts": , "command": "", ...kwargs}` + +### pysnoo2 (DanPatten) β€” PubNub-only Reference + +Useful for understanding the older PubNub protocol: + +**Auth flow:** +1. OAuth login via `https://api-us-east-1-prod.happiestbaby.com/us/v3/login` (Legacy OAuth, not Cognito) +2. Client ID: `snoo_client` +3. User-Agent: `okhttp/4.7.2` +4. Token refresh via `/us/v2/refresh/` + +**PubNub auth:** +1. POST to `/us/me/v10/pubnub/authorize` with bearer token +2. Returns PubNub-specific auth token + +**Channel naming:** +- `ActivityState.{serialNumber}` β€” device state (subscribe) +- `ControlCommand.{serialNumber}` β€” commands (publish) + +### REST API Endpoints + +``` +Base URL: https://api-us-east-1-prod.happiestbaby.com + +GET /us/me/v10/me β†’ User info +GET /hds/me/v11/devices β†’ Device list (includes awsIoT endpoint) +GET /us/me/v10/baby β†’ Baby info + settings +GET /us/me/v10/babies β†’ Baby list +POST /us/me/v10/pubnub/authorize β†’ PubNub token +GET /ss/me/v10/babies/{id}/sessions/last β†’ Last sleep session +GET /ss/v2/babies/{id}/sessions/aggregated/avg/ β†’ Aggregated session averages +GET /ss/v2/babies/{id}/sessions/total-time/ β†’ Total usage time +PATCH /us/me/v10/baby β†’ Update baby settings +POST /us/v3/login β†’ OAuth login (legacy) +POST /us/v2/refresh/ β†’ Token refresh (legacy) +``` + +### Baby Settings (configurable via PATCH) + +```json +{ + "settings": { + "responsivenessLevel": "lvl0", // lvl-2 to lvl+2 + "minimalLevelVolume": "lvl0", // lvl-2 to lvl+2 + "soothingLevelVolume": "lvl0", // lvl0 to lvl+2 + "minimalLevel": "baseline", // baseline, level1, level2 + "motionLimiter": true, // boolean + "weaning": false, // boolean + "carRideMode": false, // boolean + "daytimeStart": 7, // hour (5-12) + "stickyWhiteNoiseTimeout": 0 // minutes + } +} +``` + +--- + +## 5. Local Server Replacement Architecture + +### Approach A: DNS Redirect + MQTT Broker (Most Promising) + +**Concept:** Redirect the Snoo's cloud connections to a local server that speaks the same protocol. + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Local App │────▢│ Mosquitto │◀────│ Snoo β”‚ +β”‚ / HA β”‚ β”‚ MQTT Broker β”‚ β”‚ Bassinet β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β–² + β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β” + β”‚ Local DNS β”‚ + β”‚ (redirect) β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +**Steps:** +1. Capture the Snoo's DNS queries to identify the AWS IoT endpoint it connects to +2. Set up a local DNS server (dnsmasq) that redirects the AWS IoT endpoint to your local machine +3. Run Mosquitto MQTT broker with WebSocket support on port 443 +4. Generate TLS certs (challenge: the Snoo likely validates server certs) + +**DNS redirect setup:** +```bash +# Install dnsmasq +brew install dnsmasq # macOS +# or +sudo apt install dnsmasq # Linux + +# Configure redirect β€” add to /etc/dnsmasq.conf: +# address=//192.168.1.100 + +# Point Snoo's DNS to your dnsmasq server via DHCP options on your router +``` + +**Mosquitto setup:** +```bash +# Install Mosquitto +brew install mosquitto # macOS +# or +sudo apt install mosquitto # Linux + +# Configure for WebSocket on 443 β€” add to mosquitto.conf: +# listener 443 +# protocol websockets +# cafile /path/to/ca.crt +# certfile /path/to/server.crt +# keyfile /path/to/server.key +# allow_anonymous true + +# Start +mosquitto -c /path/to/mosquitto.conf +``` + +**Challenge: TLS certificate validation.** The Snoo connects to AWS IoT Core which has its own CA. Options: +- If the device validates certs strictly (likely), you'd need to install a custom CA cert on the device (requires root) +- If the device uses a system CA store, you'd need a cert signed by a trusted CA for the AWS IoT hostname +- If cert validation can be bypassed at the firmware level (e.g., by modifying the firmware), this becomes feasible + +### Approach B: Cloud Proxy (Easier, Partial Local) + +**Concept:** Run a local proxy that authenticates with the real cloud, then exposes a local MQTT interface. + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Local App │────▢│ Local MQTT │────▢│ Cloud │◀────│ Snoo β”‚ +β”‚ β”‚ β”‚ Proxy β”‚ β”‚ AWS IoT β”‚ β”‚ Bassinet β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +This is essentially what `python-snoo` already does. You could: +1. Run a local service that authenticates via AWS Cognito +2. Subscribes to MQTT activity_state topic +3. Exposes a local API/MQTT for your own clients +4. Forwards commands to the cloud + +**This does NOT eliminate cloud dependency** but gives you a local control plane. + +### Approach C: Firmware Modification (Hardest, True Local) + +**Concept:** Modify the Snoo's firmware to connect to a local MQTT broker instead of AWS IoT. + +**Requirements:** +- Root access to the Snoo's Linux OS (via UART/serial, if available) +- Identify the MQTT client binary/config on the device +- Change the endpoint from `*.iot.us-east-1.amazonaws.com` to your local broker +- Disable cert pinning if present +- Risk: bricking the device, voiding warranty + +**Investigation needed:** +- Get UART access (check FCC internal photos for debug pads) +- Dump the filesystem +- Find the MQTT/PubNub client configuration +- Identify if there's a config file vs. compiled-in endpoints + +### Approach D: AWS IoT Core Shim (Clever Middle Ground) + +**Concept:** Set up your own AWS IoT Core instance that the Snoo connects to. + +- Create an AWS account with IoT Core +- Register the Snoo as a "thing" in your IoT Core +- DNS redirect the Snoo to your IoT Core endpoint +- The Snoo's TLS cert validation might work since it's still "real" AWS +- Forward MQTT messages however you want + +**Challenge:** The Snoo's auth flow involves Cognito client ID `6kqofhc8hm394ielqdkvli0oea` which is specific to Happiest Baby's AWS account. The device may validate the Cognito pool or IoT policy. + +### Feasibility Assessment + +| Approach | Difficulty | Cloud-Free? | Risk | +|----------|-----------|-------------|------| +| A: DNS + Local MQTT | Medium-High | Yes | TLS cert pinning likely blocks this | +| B: Cloud Proxy | Low | No (still needs cloud) | None, this works today with python-snoo | +| C: Firmware Mod | Very High | Yes | Brick risk, needs hardware access | +| D: AWS IoT Shim | High | Partial (your own AWS) | Auth chain may not transfer | + +### Recommended Path + +1. **Start with Approach B** (cloud proxy via python-snoo) β€” this works immediately +2. **Run network recon** (Section 2) to understand what the device actually does on the network +3. **Check for open local ports** β€” Red Balloon found local attack surface previously; it may still exist +4. **If local ports exist**, determine if local commands are still possible without cloud auth +5. **If you get UART access**, investigate firmware for configurable endpoints +6. **Long-term**: If the device uses standard MQTT and the endpoint is configurable in a config file, firmware mod becomes viable + +### Prior Art: Eight Sleep Pod Comparison + +The Eight Sleep pod was successfully rooted and a local server replacement was built. Key differences: + +- Eight Sleep: used standard MQTT with discoverable endpoints +- Snoo: uses AWS IoT Core with Cognito auth (more locked down) +- Eight Sleep: had accessible debug interfaces +- Snoo: debug access is unknown (pending FCC photo analysis) + +The Snoo's tighter integration with AWS makes a fully local replacement harder, but the protocol is well-documented thanks to `python-snoo`. + +--- + +## Appendix A: Tool Installation + +```bash +# Network scanning +brew install nmap # Port scanning +brew install arp-scan # ARP scanning +brew install wireshark # GUI packet analysis (includes tshark) + +# Traffic interception +brew install mitmproxy # HTTPS MITM proxy + +# MQTT tools +brew install mosquitto # MQTT broker + client tools +pip install aiomqtt # Python MQTT client + +# DNS +brew install dnsmasq # Local DNS server + +# Snoo libraries +pip install python-snoo # Current HA integration library (MQTT + PubNub) +pip install pysnoo2 # PubNub-only library (useful reference) + +# Quick MQTT test (once you have credentials) +pip install aiomqtt +``` + +## Appendix B: Quick Start β€” Get Snoo Status via python-snoo + +```python +import asyncio +import aiohttp +from python_snoo.snoo import Snoo + +async def main(): + async with aiohttp.ClientSession() as session: + snoo = Snoo("your@email.com", "your_password", session) + tokens = await snoo.authorize() + devices = await snoo.get_devices() + + for dev in devices: + print(f"Serial: {dev.serialNumber}") + print(f"Firmware: {dev.firmwareVersion}") + print(f"AWS IoT Endpoint: {dev.awsIoT.clientEndpoint}") + print(f"AWS IoT Thing: {dev.awsIoT.thingName}") + + # Subscribe to status updates + def on_update(data): + print(f"State: {data.state_machine.state}") + print(f"Event: {data.event}") + + snoo.start_subscribe(devices[0], on_update) + + # Keep running + await asyncio.sleep(60) + await snoo.disconnect() + +asyncio.run(main()) +``` + +## Appendix C: Source References + +- python-snoo (MQTT + PubNub): https://github.com/Lash-L/python-snoo +- pysnoo2 (PubNub): https://github.com/DanPatten/pysnoo2 +- pysnoo (original): https://github.com/rado0x54/pysnoo +- pysnooapi (REST only): https://github.com/sanghviharshit/pysnooapi +- HA Snoo integration: https://www.home-assistant.io/integrations/snoo/ +- HA MQTT PR: https://github.com/home-assistant/core/pull/150570 +- FCC filing (original): https://fccid.io/2AH7Y-101067400 +- FCC filing (newer): https://fccid.io/2AH7YS1000-01 +- PubNub case study: https://www.pubnub.com/customers/happiest-baby-snoo-smart-sleeper/ +- Softeq dev docs: https://www.softeq.com/featured_projects/full_cycle_software_development_for_smart_bassinette +- Red Balloon Security research: https://www.bgr.com/science/smart-crib-hack-snoo-security/ diff --git a/docs/wiki/.compile-state.json b/docs/wiki/.compile-state.json new file mode 100644 index 00000000..96e25b5f --- /dev/null +++ b/docs/wiki/.compile-state.json @@ -0,0 +1,39 @@ +{ + "last_compiled": "2026-04-05T00:00:00Z", + "source_hashes": { + "docs/PRIVACY.md": "compiled", + "docs/DEPLOYMENT.md": "compiled", + "docs/piezo-processor.md": "compiled", + "docs/sleep-detector.md": "compiled", + "docs/trpc-api-architecture.md": "compiled", + "docs/adr/0001-new-repository.md": "compiled", + "docs/adr/0003-core-stack.md": "compiled", + "docs/adr/0003-typescript-react.md": "compiled", + "docs/adr/0004-nextjs-unified.md": "compiled", + "docs/adr/0005-trpc.md": "compiled", + "docs/adr/0006-developer-tooling.md": "compiled", + "docs/adr/0006-eslint.md": "compiled", + "docs/adr/0010-drizzle-orm-sqlite.md": "compiled", + "docs/adr/0012-biometrics-module-system.md": "compiled", + "docs/adr/0013-yocto-deployment-toolchain.md": "compiled", + "docs/adr/0014-sensor-calibration.md": "compiled", + "docs/adr/0015-event-bus-mutation-broadcast.md": "compiled", + "docs/adr/0016-raw-command-execution.md": "compiled", + "docs/hardware/DAC-PROTOCOL.md": "compiled", + "docs/hardware/calibration-architecture.md": "compiled", + "docs/hardware/sensor-profiles.md": "compiled" + }, + "topics": [ + "architecture-and-stack", + "deployment", + "hardware-protocol", + "biometrics-system", + "piezo-processing", + "sleep-detection", + "sensor-calibration", + "sensor-hardware", + "api-architecture", + "privacy" + ], + "version": 1 +} diff --git a/docs/wiki/INDEX.md b/docs/wiki/INDEX.md new file mode 100644 index 00000000..2fcbcf82 --- /dev/null +++ b/docs/wiki/INDEX.md @@ -0,0 +1,21 @@ +# sleepypod core Wiki + +Knowledge base compiled from project documentation. 10 topics from 22 source files. + +## Topics + +### System Architecture +- [[topics/architecture-and-stack|Architecture and Stack]] β€” TypeScript, React, Next.js, tRPC, Drizzle/SQLite, tooling +- [[topics/api-architecture|API Architecture]] β€” tRPC routers, WebSocket push, event bus, frontend hooks +- [[topics/hardware-protocol|Hardware Protocol]] β€” DAC socket communication, wire protocol, commands + +### Biometrics & Sensors +- [[topics/biometrics-system|Biometrics System]] β€” Plugin/sidecar module architecture, schema contract +- [[topics/piezo-processing|Piezo Processing]] β€” Heart rate, HRV, breathing rate from BCG signals +- [[topics/sleep-detection|Sleep Detection]] β€” Bed occupancy, movement scoring, session tracking +- [[topics/sensor-calibration|Sensor Calibration]] β€” Adaptive thresholds, medical rationale, quality scoring +- [[topics/sensor-hardware|Sensor Hardware]] β€” Pod sensor profiles, CBOR record types, capSense vs capSense2 + +### Operations +- [[topics/deployment|Deployment]] β€” Three paths to the Pod, Yocto constraints, network security +- [[topics/privacy|Privacy]] β€” Local-only data, no cloud, no analytics diff --git a/docs/wiki/compile-log.md b/docs/wiki/compile-log.md new file mode 100644 index 00000000..e7844260 --- /dev/null +++ b/docs/wiki/compile-log.md @@ -0,0 +1,32 @@ +# Compile Log + +Wiki initialized on 2026-04-05. + +## 2026-04-05 β€” Full compilation (first run) + +- **Sources**: 21 markdown files from `docs/` +- **Topics created**: 10 + - architecture-and-stack (8 sources) + - deployment (2 sources) + - hardware-protocol (2 sources) + - biometrics-system (1 source) + - piezo-processing (1 source) + - sleep-detection (1 source) + - sensor-calibration (2 sources) + - sensor-hardware (1 source) + - api-architecture (2 sources) + - privacy (1 source) +- **Schema**: generated (`schema.md`) +- **Cross-references**: Obsidian `[[wiki-link]]` style between all related topics + +## 2026-04-05 β€” Incremental compilation + +- **New sources**: 1 (`docs/adr/0017-uv-python-package-management.md`) +- **Topics updated**: 1 + - deployment (+1 source: ADR 0017 uv Python package management) +- **Topics created**: 0 +- **Schema**: unchanged (ADR 0017 classified under existing `deployment` topic) + +### Note on `.compile-state.json` + +`source_hashes` in `.compile-state.json` uses the placeholder value `"compiled"` for every file. These are staging markers indicating a source was processed, not real content hashes. Future iterations may replace them with SHA-256 content hashes for incremental recompilation. diff --git a/docs/wiki/schema.md b/docs/wiki/schema.md new file mode 100644 index 00000000..0038057b --- /dev/null +++ b/docs/wiki/schema.md @@ -0,0 +1,33 @@ +# Wiki Schema + +Defines the topic taxonomy for the sleepypod core knowledge base. Used by the compiler to classify source documents into topics. + +## Topics + +| Slug | Title | Description | Key Sources | +|------|-------|-------------|-------------| +| `architecture-and-stack` | Architecture and Stack | Core technology decisions: TypeScript, React, Next.js, tRPC, Drizzle/SQLite, tooling | ADRs 0001-0010 | +| `deployment` | Deployment | Getting code to the Pod: 3 paths, Yocto constraints, network security, Python/uv setup, free-sleep coexistence | DEPLOYMENT.md, ADR 0013, ADR 0017 | +| `hardware-protocol` | Hardware Protocol | DAC socket communication: wire protocol, commands, connection lifecycle | DAC-PROTOCOL.md, ADR 0016 | +| `biometrics-system` | Biometrics System | Plugin/sidecar module architecture, schema contract, manifest discovery | ADR 0012 | +| `piezo-processing` | Piezo Processing | HR, HRV, breathing rate from 500 Hz BCG: SHS, pump gating, presence, validation | piezo-processor.md | +| `sleep-detection` | Sleep Detection | Bed occupancy, movement (PIM), sessions from capacitance: capSense/capSense2 | sleep-detector.md | +| `sensor-calibration` | Sensor Calibration | Adaptive thresholds, CalibrationStore, medical rationale, quality scoring | ADR 0014, calibration-architecture.md | +| `sensor-hardware` | Sensor Hardware | Pod sensor profiles: CBOR record types, capSense vs capSense2, channel maps | sensor-profiles.md | +| `api-architecture` | API Architecture | tRPC routers, WebSocket push, event bus, frontend hooks | trpc-api-architecture.md, ADR 0015 | +| `privacy` | Privacy | Local-only data, no cloud/analytics/telemetry | PRIVACY.md | + +## Topic Relationships + +``` +architecture-and-stack +β”œβ”€β”€ api-architecture (uses tRPC, Drizzle) +β”‚ β”œβ”€β”€ hardware-protocol (device router β†’ dac.sock) +β”‚ └── biometrics-system (biometrics router β†’ biometrics.db) +β”‚ β”œβ”€β”€ piezo-processing (writes vitals) +β”‚ β”œβ”€β”€ sleep-detection (writes sleep_records, movement) +β”‚ └── sensor-calibration (adaptive thresholds for processing modules) +β”‚ └── sensor-hardware (pod-specific sensor formats) +β”œβ”€β”€ deployment (deploys the stack to the pod) +└── privacy (design constraint across all components) +``` diff --git a/docs/wiki/topics/api-architecture.md b/docs/wiki/topics/api-architecture.md new file mode 100644 index 00000000..3ce82436 --- /dev/null +++ b/docs/wiki/topics/api-architecture.md @@ -0,0 +1,90 @@ +# API Architecture + +Type-safe API layer providing frontend access to hardware control, settings, schedules, and biometrics. Built on tRPC with WebSocket push for real-time data. + +## Router Structure + +| Router | Purpose | Transport | +|--------|---------|-----------| +| `device.*` | Real-time hardware control | HTTP mutations, WS push for status | +| `settings.*` | Configuration management | HTTP | +| `schedules.*` | Automation schedules | HTTP | +| `biometrics.*` | Health data queries | HTTP | + +## Device Status: WebSocket-First + +Device status is primarily pushed via WebSocket on port 3001. tRPC HTTP remains for initial page load, non-WS clients (iOS, CLI), and fallback. + +### Read Bus (DacMonitor) +DacMonitor polls [[hardware-protocol|dac.sock]] for device status (defaults to 2s, adapts based on activity: 1s active / 2s normal / 5s idle) and broadcasts a `deviceStatus` frame via WebSocket to all clients. + +### Write Bus (Mutation Broadcast) +After any hardware mutation (user-initiated via device router OR automated via scheduler), `broadcastMutationStatus()` overlays the mutation onto the last polled status and broadcasts immediately (~200ms). Fire-and-forget β€” DacMonitor's 2s poll is the consistency backstop. + +All hardware commands go through `dacTransport`'s `SequentialQueue`, serializing writes from concurrent sources. + +### Frontend Hooks + +```typescript +// Primary: WS push for device status +const { status, isLoading, isStreaming } = useDeviceStatus() + +// Sensor data subscriptions +const { status, latestFrames } = useSensorStream({ sensors: ['capSense', 'bedTemp'] }) + +// Historical data, settings β€” HTTP +const vitals = trpc.biometrics.getVitals.useQuery({ side: 'left', limit: 100 }) + +// Mutations β€” always HTTP +const setTemp = trpc.device.setTemperature.useMutation() +``` + +| Data type | Transport | Hook | +|-----------|-----------|------| +| Device status | WebSocket push | `useDeviceStatus()` | +| Sensor data | WebSocket push | `useSensorStream()` / `useSensorFrame()` | +| Mutations | tRPC HTTP | `trpc.device.*.useMutation()` | +| Historical data | tRPC HTTP | `trpc.biometrics.*.useQuery()` | +| Settings | tRPC HTTP | `trpc.settings.*.useQuery()` | +| Schedules | tRPC HTTP | `trpc.schedules.*.useQuery()` | + +## Device Router + +Real-time Pod control via [[hardware-protocol]]: + +- `setTemperature(side, temp, duration?)` β€” 55-110Β°F range, optional auto-off +- `setPower(side, on, temp?)` β€” level 0 = 82.5Β°F neutral (no true "off") +- `setAlarm(side, config)` β€” vibration patterns: double/rise, 1-100 intensity, 0-180s +- `clearAlarm(side)` / `snoozeAlarm(side, duration, config)` +- `startPriming()` β€” water circulation (2-5 min, loud, don't run during sleep) + +Hardware timing: commands ~100-500ms, temperature changes 4-7 min (1-2Β°F/min). + +## Schedules Router + +Temperature, power, and alarm automation executed by background scheduler (node-schedule): + +- **Temperature schedules** β€” time + target temp per day of week +- **Power schedules** β€” on/off times with target temp +- **Alarm schedules** β€” vibration + temperature wake-up + +Timezone-aware. Away mode disables schedules per side without affecting manual control. + +## Biometrics Router + +Query data from the [[biometrics-system]]: + +- `getSleepRecords` / `getLatestSleep` β€” session history +- `getVitals` / `getVitalsSummary` β€” HR, HRV, breathing rate +- `getMovement` β€” movement score per 60s epoch (0-1000) + +Data fields may be null if sensor couldn't get a reliable reading. Default limits sized for typical use (30 records β‰ˆ 1 month, 288 vitals β‰ˆ 24 hours). + +## Authentication + +All procedures currently use `publicProcedure` β€” no authentication. Protected procedure middleware is defined but not yet active. + +## Sources + +- `docs/trpc-api-architecture.md` +- `docs/adr/0015-event-bus-mutation-broadcast.md` diff --git a/docs/wiki/topics/architecture-and-stack.md b/docs/wiki/topics/architecture-and-stack.md new file mode 100644 index 00000000..2d74e4ab --- /dev/null +++ b/docs/wiki/topics/architecture-and-stack.md @@ -0,0 +1,56 @@ +# Architecture and Stack + +Core technology decisions for sleepypod-core β€” a full-stack TypeScript application targeting embedded Linux hardware. + +## Why a Separate Repository + +sleepypod-core lives in its own repository rather than as a fork or branch of free-sleep. The upstream project favors small, incremental, narrowly-scoped changes reviewed by a single maintainer with limited time. sleepypod's changes are designed, tested, and validated together as a cohesive system β€” reviewing them piecemeal would be higher risk than reviewing them as a complete working system. + +A separate repository makes the boundary explicit, sets accurate expectations, and allows an independent roadmap and release cadence. + +## Core Stack + +| Layer | Choice | Rationale | +|-------|--------|-----------| +| Language | TypeScript (strict) | Single type system from [[hardware-protocol]] client through to browser | +| UI | React 19 (App Router) | Component model maps to pod UI surfaces | +| Framework | Next.js | Unified frontend + backend, SSR/SSG, built-in API routing | +| API | tRPC | End-to-end type safety, no manual client generation. See [[api-architecture]] | +| Database | Drizzle ORM + SQLite | 30KB runtime, no code generation, SQL-transparent. See below | +| i18n | Lingui | Macro-based extraction, works with App Router, smaller than react-intl | + +## Drizzle ORM + SQLite + +SQLite is ideal for the Pod's single-node embedded environment β€” file-based, no separate server, minimal operational complexity. Drizzle was chosen over Prisma (10MB+ binary, too heavy for embedded), raw better-sqlite3 (no type safety), TypeORM (decorator-based, heavier), and Kysely (close second, but Drizzle has better schema management). + +Key configuration: +- **WAL mode** for concurrent reads during writes +- **`synchronous = NORMAL`** for performance +- **64MB cache** via `cache_size = -64000` +- **Memory-mapped I/O** via `mmap_size` + +Schema is defined in TypeScript (`src/db/schema.ts`) with types inferred directly β€” no code generation step. Migrations are SQL-based in `src/db/migrations/`, auto-run on server startup. + +The [[biometrics-system]] uses a separate `biometrics.db` file with different access patterns (append-heavy vs. read-heavy). + +## Developer Tooling + +| Tool | Purpose | +|------|---------| +| **ESLint** | `typescript-eslint` strict mode + `@stylistic` for formatting (no Prettier) | +| **Vitest** | Test runner β€” shares Vite config with Next.js build, native ESM support | +| **pnpm** | Package manager β€” content-addressable store for Pod's limited storage | +| **Conventional Commits** | `feat:`, `fix:`, `chore:` β€” drives automated versioning via semantic-release | + +Flat ESLint config (`eslint.config.js`). Tests via `pnpm test`, linting via `pnpm lint`. Merges to `main` trigger automated release if conventional commits are present. + +## Sources + +- `docs/adr/0001-new-repository.md` +- `docs/adr/0003-core-stack.md` +- `docs/adr/0003-typescript-react.md` +- `docs/adr/0004-nextjs-unified.md` +- `docs/adr/0005-trpc.md` +- `docs/adr/0006-developer-tooling.md` +- `docs/adr/0006-eslint.md` +- `docs/adr/0010-drizzle-orm-sqlite.md` diff --git a/docs/wiki/topics/biometrics-system.md b/docs/wiki/topics/biometrics-system.md new file mode 100644 index 00000000..0357296a --- /dev/null +++ b/docs/wiki/topics/biometrics-system.md @@ -0,0 +1,85 @@ +# Biometrics System + +Plugin/sidecar module architecture for processing raw sensor data into health metrics. + +## Architecture + +``` +/persistent/*.RAW ← hardware daemon writes CBOR sensor data continuously + ↓ +[module process] reads + tails RAW files, processes signals (any language) + ↓ +[biometrics.db] module writes to agreed schema tables + ↑ +[sleepypod-core] reads biometrics.db via tRPC API β†’ UI +``` + +The **database schema is the public contract**. Modules do not call into the core app. The core app does not call into modules. They share only a file. + +## Why Sidecar Processes + +- **Language agnostic** β€” Python is ideal for signal processing (scipy, numpy); Rust for performance +- **Independent lifecycle** β€” a crash in one module doesn't affect the core app or others +- **Unix philosophy** β€” each module does one thing (HR extraction, sleep detection) +- **Community replaceable** β€” swap a module without touching others + +Node.js was rejected for signal processing (FFT at 500 Hz requires native addons; a crash affects the entire app). + +## Two Databases + +| Database | Purpose | Access Pattern | +|----------|---------|----------------| +| `sleepypod.db` | Config, schedules, runtime state | Read-heavy, random access | +| `biometrics.db` | Vitals, sleep records, movement, [[sensor-calibration|calibration]] | Append-heavy, time-range queries | + +Different access patterns warrant different SQLite pragmas. Biometrics data can be cleared, backed up, or handed off independently. + +## SQLite Concurrency + +Multiple modules write to `biometrics.db` concurrently using: +- **WAL mode** β€” concurrent readers while one writer holds the lock +- **`busy_timeout = 5000ms`** β€” writers wait rather than failing + +At actual write frequencies (~60s intervals), contention is negligible. + +## Schema Contract + +### vitals +Written by [[piezo-processing]]: `heartRate` (bpm), `hrv` (ms), `breathingRate` (breaths/min). Fields may be null if sensor couldn't get a reliable reading. + +### sleep_records +Written by [[sleep-detection]]: session boundaries, duration, bed exit count, present/absent intervals. + +### movement +Written by [[sleep-detection]]: movement score per 60s epoch (0-1000, PIM delta-based). + +## Module Manifest + +Each module ships a `manifest.json`: + +```json +{ + "name": "piezo-processor", + "version": "1.0.0", + "provides": ["vitals.heartRate", "vitals.hrv", "vitals.breathingRate"], + "writes": ["vitals"], + "service": "sleepypod-piezo-processor.service", + "language": "python" +} +``` + +The core app reads manifests to populate the system health/status page. + +## Bundled Modules + +| Module | Service | Writes | Language | +|--------|---------|--------|----------| +| [[piezo-processing|piezo-processor]] | sleepypod-piezo-processor.service | vitals | Python | +| [[sleep-detection|sleep-detector]] | sleepypod-sleep-detector.service | sleep_records, movement | Python | +| [[sensor-calibration|calibrator]] | sleepypod-calibrator.service | calibration_profiles, calibration_runs | Python | + +Community modules can be installed by dropping a directory into `/opt/sleepypod/modules/` with the same structure. + +## Sources + +- `docs/adr/0012-biometrics-module-system.md` diff --git a/docs/wiki/topics/deployment.md b/docs/wiki/topics/deployment.md new file mode 100644 index 00000000..b3aca252 --- /dev/null +++ b/docs/wiki/topics/deployment.md @@ -0,0 +1,97 @@ +# Deployment + +How code gets from development to the Pod β€” a Yocto-based embedded Linux device (aarch64) with 2GB RAM, no package manager, no C compiler, no git, and WAN blocked by iptables. + +## Three Deployment Paths + +### Path 1: Mac Deploy (Development) + +Builds locally on the Mac, pushes artifacts to the Pod over LAN. No WAN needed. + +```bash +./scripts/deploy # current branch -> 192.168.1.88 +./scripts/deploy 192.168.1.50 # different pod +./scripts/deploy 192.168.1.88 feat/alarms # specific branch +``` + +Uses `tar | ssh` instead of rsync (not available on Pod). Cleans stale files while preserving `node_modules`, `.env`, and databases. Requires SSH on port 8822 with key auth. + +### Path 2: CI Release (Production) + +GitHub Actions builds on push to `main` and version tags. Produces a tarball with source + `.next` (pre-built). Tagged releases publish as GitHub Release assets. + +The Pod downloads pre-built tarballs via `sp-update`, so it never runs `next build` (which needs more RAM than the Pod has). + +### Path 3: Remote Update (Web UI / iOS) + +Self-update via `system.triggerUpdate` tRPC endpoint. Opens iptables temporarily, downloads release tarball, installs, closes iptables. + +```bash +sp-update # latest release +sp-update feat/alarms # specific branch (source only, needs build) +``` + +If update fails: iptables restored, service restarts with existing code, database restored from backup. + +## Why Build Off-Device + +Next.js with Turbopack needs more memory than the Pod's 2GB (no swap). The `.next` output is platform-independent JavaScript β€” only `better-sqlite3` requires a platform-specific binary, handled by `prebuild-install`. + +## Yocto Constraints + +The Pod runs "Eight Layer 4.0.2" (Yocto kirkstone). Key constraints and solutions: + +| Constraint | Solution | +|-----------|----------| +| No package manager | Node.js via binary tarball from nodejs.org | +| No C compiler | `prebuild-install` for native modules (better-sqlite3) | +| No rsync | `tar + ssh` pipe for file sync | +| No git | GitHub tarball API and release assets | +| 2GB RAM, no swap | Build off-device, deploy only runtime artifacts | + +## Network Security + +Two independent layers block the Pod's stock processes from phoning home: + +1. **iptables** β€” OUTPUT chain DROPs all non-LAN, non-NTP traffic +2. **/etc/hosts null routes** β€” Stock firmware domains resolve to `0.0.0.0`, preventing exfiltration even when iptables are temporarily opened for updates + +Blocked domains: `raw-api-upload.8slp.net`, `device-api-ws.8slp.net`, `api.8slp.net`, `app-api.8slp.net`, `client-api.8slp.net`. + +Why not stop frankenfirmware? It also handles sensor/hardware communication (capSense, temperatures, pump, piezo). Stopping it breaks all biometrics. See [[hardware-protocol]]. + +## Python Environment (uv) + +Pod 3 (Python 3.9) and Pod 4 (Python 3.10) run Yocto with an incomplete Python stdlib β€” `pyexpat` (C extension) and `ensurepip` are missing, which breaks the entire `venv` + `pip` chain. See [[biometrics-system]] for what the Python modules do. + +**Solution**: [uv](https://docs.astral.sh/uv/) β€” a Rust-based package manager that bypasses Python's stdlib entirely. Single static binary, no `pyexpat`/`pip`/`ensurepip` needed. + +| Before | After | +|--------|-------| +| `scripts/patch-python-stdlib` + `scripts/setup-python-venv` | Deleted | +| `requirements.txt` per module | `pyproject.toml` + `uv.lock` per module | +| `venv/bin/pip install -r requirements.txt` | `uv sync` | + +uv (~30MB) is downloaded during install. Each biometrics module gets a `.venv/` created by `uv sync` with locked dependencies. Works identically on Pod 3, 4, and 5 β€” no pod-generation-specific branching. + +## Pod Environment + +| Component | Detail | +|-----------|--------| +| OS | Eight Layer 4.0.2 (Yocto kirkstone) | +| Arch | aarch64 (ARM64) | +| RAM | 2GB (no swap) | +| Node.js | Binary tarball install | +| DAC socket | Auto-detected from frank.sh | + +Key file locations: app at `/home/dac/sleepypod-core`, config DB at `/persistent/sleepypod-data/sleepypod.db`, biometrics DB at `/persistent/sleepypod-data/biometrics.db`. + +## Coexistence with free-sleep + +Both services bind to port 3000. Switch with `sp-sleepypod` / `sp-freesleep`. Settings and data preserved β€” only changes which server handles requests. + +## Sources + +- `docs/DEPLOYMENT.md` +- `docs/adr/0013-yocto-deployment-toolchain.md` +- `docs/adr/0017-uv-python-package-management.md` diff --git a/docs/wiki/topics/hardware-protocol.md b/docs/wiki/topics/hardware-protocol.md new file mode 100644 index 00000000..74130354 --- /dev/null +++ b/docs/wiki/topics/hardware-protocol.md @@ -0,0 +1,82 @@ +# Hardware Protocol + +How sleepypod-core communicates with the Pod hardware via the DAC Unix socket. + +## Architecture + +The Pod runs three stock processes: +- **frankenfirmware** β€” controls hardware (temperature, pumps, sensors, alarms) via UART to STM32 MCUs +- **Eight.Capybara** β€” cloud connectivity (blocked by [[deployment|network security]]) +- **DAC** β€” replaced by sleepypod-core + +sleepypod-core replaces the DAC process. frankenfirmware connects TO us on `dac.sock`. + +## Connection Architecture + +``` +SocketListener (listens on dac.sock) + └── DacTransport (wraps connected socket) + └── SequentialQueue (one command at a time) + └── MessageStream (binary-split on double-newline) +``` + +All consumers share a single `DacTransport` via `getSharedHardwareClient()`: +- **DacMonitor** β€” polls every 2s for device status +- **Device Router** β€” ad-hoc API calls from [[api-architecture]] +- **Job Manager** β€” scheduled operations +- **Health Router** β€” connectivity checks +- **Gesture Handler** β€” tap actions + +### Design Principles + +- **Queue, don't replace** β€” incoming connections are queued; consumer pulls when ready +- **One transport for all consumers** β€” multiple independent connections cause each to be treated as a new frankenfirmware, destroying the real one +- **Sequential commands** β€” one command, one response; 10ms delay between write and read +- **Restart frank after deploy** β€” it only discovers `dac.sock` on startup + +## Wire Protocol + +Text-based, double-newline delimited: + +``` +Request: {command_number}\n{argument}\n\n +Response: {data}\n\n +``` + +### Commands + +| Code | Command | Argument | Description | +|------|---------|----------|-------------| +| `0` | HELLO | β€” | Ping/connectivity check | +| `5`/`6` | ALARM_LEFT/RIGHT | hex CBOR | Configure alarm | +| `8` | SET_SETTINGS | hex CBOR | LED brightness, etc. | +| `9`/`10` | TEMP_DURATION_L/R | seconds | Auto-off duration | +| `11`/`12` | TEMP_LEVEL_L/R | -100 to 100 | Set temperature level | +| `13` | PRIME | β€” | Start water priming | +| `14` | DEVICE_STATUS | β€” | Get all device status | +| `16` | ALARM_CLEAR | 0 or 1 | Clear alarm (0=left, 1=right) | + +### Temperature Scale + +| Level | Fahrenheit | Description | +|-------|-----------|-------------| +| -100 | 55Β°F | Maximum cooling | +| 0 | 82.5Β°F | Neutral (no heating/cooling) | +| +100 | 110Β°F | Maximum heating | + +Formula: `F = 82.5 + (level / 100) Γ— 27.5` + +### Raw Command Execution + +The `device.execute` tRPC mutation (OpenAPI: `POST /device/execute`) provides passthrough access to the frank command protocol. Power user feature for debugging and testing β€” no input validation beyond command name allowlisting. Not covered by standard safety/debounce mechanisms. + +### Timing + +- **10ms** delay between write and read +- **2s** DacMonitor polling interval +- **25s** timeout waiting for frankenfirmware to connect + +## Sources + +- `docs/hardware/DAC-PROTOCOL.md` +- `docs/adr/0016-raw-command-execution.md` diff --git a/docs/wiki/topics/piezo-processing.md b/docs/wiki/topics/piezo-processing.md new file mode 100644 index 00000000..f7e25d66 --- /dev/null +++ b/docs/wiki/topics/piezo-processing.md @@ -0,0 +1,106 @@ +# Piezo Processing + +Heart rate, HRV, and breathing rate extraction from raw piezoelectric ballistocardiogram (BCG) sensor data. Part of the [[biometrics-system]]. + +## Overview + +The piezo-processor tails CBOR-encoded `.RAW` files, processes dual-channel (left/right) signals at 500 Hz, and writes vitals rows to `biometrics.db` every 60 seconds per occupied side. + +## Why V2 + +V1 produced garbage under real pod conditions: +- **Pump noise** β€” no gating, so pump cycles contaminated every window +- **Harmonic locking** β€” simple autocorrelation locked onto 2nd/3rd harmonic (reporting 160 BPM instead of 80) +- **Broken presence** β€” energy threshold triggered false presence on empty side from vibration coupling +- **Locked breathing** β€” Welch PSD converged to exactly 12.0 BPM regardless of actual breathing + +## Signal Processing Pipeline + +**RAW ingestion** β†’ **Pump gating** β†’ **Per-side buffering** β†’ **Presence detection** β†’ **Vitals computation** β†’ **DB write** + +### Buffers per side + +| Buffer | Window | Samples | Purpose | +|--------|--------|---------|---------| +| HR | 30s | 15,000 | Heart rate extraction | +| BR | 60s | 30,000 | Breathing rate extraction | +| HRV | 300s | 150,000 | HR variability index | + +## Pump Gating + +The pod's air pump creates broadband high-energy vibrations on both piezo channels. Detection uses dual-channel energy correlation: + +1. Energy ratio: `min(L,R) / max(L,R)` β€” pump produces ratio > 0.5 (affects both channels equally); heartbeat is lateralized +2. Spike detection: energy > 10Γ— baseline AND ratio > 0.5 β†’ pump flagged +3. **5-second guard period** after detection for spin-down/resonance decay + +## Presence Detection + +Hysteresis state machine with dual features: + +| Feature | Entry Threshold | Exit Threshold | +|---------|----------------|----------------| +| Median std (1-10 Hz) | > 400,000 | < 150,000 | +| Autocorrelation quality | > 0.45 | < 0.225 | + +Exit requires 3 consecutive low windows (3 minutes of silence) to prevent deep-sleep dropout. Thresholds calibrated from Pod 5 data (2026-03-16). See [[sensor-calibration]] for adaptive thresholds. + +## Heart Rate Extraction + +### Bandpass: 0.8-8.5 Hz +Lower cutoff preserves fundamentals down to 48 BPM (important for athletes/deep sleep). Upper cutoff captures 5th harmonic of 100 BPM. + +### Subharmonic Summation (SHS) +Solves the harmonic locking problem. For each candidate peak at lag `L`: + +``` +score(L) = 1.0 Γ— ACR(L) + 0.8 Γ— ACR(L/2) + 0.6 Γ— ACR(L/3) +``` + +The fundamental's sub-harmonics align with actual harmonic peaks; a harmonic's sub-harmonics land on noise. Eliminates all harmonic errors in validation. + +### Inter-Window Tracking (HRTracker) +Second line of defense: maintains history of 5 accepted HR values, applies Gaussian consistency check (weight > 0.3 β‰ˆ delta < 20 BPM). Tries half/double corrections for escaped harmonics. + +## Breathing Rate (Hilbert Envelope) + +V1's direct bandpass + Welch PSD locked at 12.0 BPM. V2 uses the Hilbert envelope method: + +1. Bandpass 0.8-10 Hz (cardiac band) +2. Hilbert transform β†’ instantaneous amplitude envelope +3. Bandpass 0.1-0.7 Hz on envelope (respiratory modulation) +4. Peak counting with outlier rejection +5. Validity gate: 6-30 BPM + +Works because heartbeat amplitude is modulated by breathing (thoracic impedance changes with lung volume). + +## HRV Index + +**Not clinical RMSSD.** Computes successive differences of window-level IBI estimates (10-second sub-windows with 50% overlap), not beat-to-beat intervals. Measures HR stability across windows. + +Pipeline: SHS autocorrelation per sub-window β†’ IBI validity gate [400-1500ms] β†’ harmonic gate (18% tolerance) β†’ Hampel filter β†’ gap-aware successive differences β†’ validity gate [5-100ms]. + +Requires 300-second buffer (5 minutes). First 5 minutes after presence detection produce no HRV. + +## V1 vs V2 Validation (Pod 5, 2026-03-16) + +| Metric | V1 | V2 | +|--------|----|----| +| HR range (occupied) | 61-171 BPM | 78.7-82.6 BPM | +| Harmonic errors | 3/8 windows | 0/12 windows | +| HR std dev | ~35 BPM | ~1.2 BPM | +| Empty side false presence | 100% | 13% | +| Breathing rate | Locked 12.0 | 16-25 BPM | + +## Known Limitations + +1. Cross-side vibration coupling can cause false presence (2/15 in validation) +2. Heavy pump gating (>30% of window) degrades HR β€” minimum 10s clean data required +3. HRV cold start: 5 minutes before first output +4. Single-pod calibration (Pod 5) β€” other pods may need threshold adjustment +5. No motion artifact handling beyond presence hysteresis +6. Fixed filter parameters β€” not adaptive to individuals + +## Sources + +- `docs/piezo-processor.md` diff --git a/docs/wiki/topics/privacy.md b/docs/wiki/topics/privacy.md new file mode 100644 index 00000000..0718454f --- /dev/null +++ b/docs/wiki/topics/privacy.md @@ -0,0 +1,31 @@ +# Privacy + +sleepypod is designed for complete local data sovereignty. + +## Core Principles + +- **No cloud** β€” all data processed and stored locally on the Pod and iOS device +- **No analytics** β€” no analytics SDKs, crash reporters, or tracking code +- **No telemetry** β€” the Pod's stock firmware telemetry is actively blocked (see [[deployment|network security]]) + +## Data Handling + +| Data Type | Storage | Network | +|-----------|---------|---------| +| Sleep records, vitals, movement | `biometrics.db` on Pod | Never leaves LAN | +| Temperature settings, schedules | `sleepypod.db` on Pod | Never leaves LAN | +| Heart rate, HRV, breathing rate | Processed on-device by [[piezo-processing]] | Never transmitted | + +## Network Communication + +sleepypod communicates exclusively over local WiFi between the Pod and iOS device. No data is sent to the internet, cloud services, or third-party servers. + +Stock firmware processes (`frankenfirmware`, `Eight.Capybara`) that attempt to phone home are blocked via iptables and /etc/hosts null routes. + +## Apple Health + +If the user chooses to write sleep data to Apple Health, that data is governed by Apple's Health privacy policies. + +## Sources + +- `docs/PRIVACY.md` diff --git a/docs/wiki/topics/sensor-calibration.md b/docs/wiki/topics/sensor-calibration.md new file mode 100644 index 00000000..7198ffa4 --- /dev/null +++ b/docs/wiki/topics/sensor-calibration.md @@ -0,0 +1,92 @@ +# Sensor Calibration + +Adaptive threshold system that replaces hardcoded values with per-pod calibration profiles. Used by [[piezo-processing]] and [[sleep-detection]]. + +## Why Calibration + +Hardcoded thresholds fail across pods: +- Piezo processor reports HR 200+ on empty beds (ambient vibration) +- Sleep detector's fixed presence threshold (1500) doesn't match all mattress/sensor combinations +- Thermistor factory tolerance varies +/-0.5Β°C between pods + +free-sleep had calibration, but each module embedded its own logic with different formats and no shared library. + +## Architecture + +Single-writer/multiple-reader pattern via a shared Python library (`modules/common/calibration.py`): + +- **Calibrator module** β€” sole writer to `calibration_profiles` table +- **Processing modules** β€” read-only consumers, poll every 60s for updates +- **tRPC router** β€” exposes profiles to iOS app for inspection + +### Trigger Mechanisms + +| Trigger | Mechanism | Timing | +|---------|-----------|--------| +| Pre-prime | jobManager schedules calibration 30min before pod prime time | Primary path | +| Daily fallback | Internal 25h timer | If priming disabled | +| On-demand | Trigger file from iOS via tRPC | ~10s latency (poll interval) | + +Trigger files use atomic writes (`.tmp` then rename) with unique filenames for queuing concurrent requests. + +## Calibration Profiles + +One row per (side, sensor_type) in `calibration_profiles` table. The sensor-specific thresholds are stored as keys inside the `parameters` JSON column (not separate DB columns): + +| `parameters` key | Sensor | Purpose | +|-------------------|--------|---------| +| `noise_floor_rms` | All | Adaptive threshold computation | +| `noise_floor_p95` | All | Spike rejection | +| `baseline_mean/std` | All | DC offset and stability | +| `presence_threshold` | Capacitance | 6Γ— noise floor RMS (replaces fixed 200,000) | +| `hr_noise_floor_bpm` | Piezo | Minimum detectable HR | +| `temp_offset_c` | Temperature | Factory thermistor correction | + +## Medical-Informed Thresholds + +Key changes from free-sleep with medical rationale: + +| Parameter | free-sleep | sleepypod | Why | +|-----------|-----------|-----------|-----| +| HR max | 90 bpm | 100 bpm | AHA tachycardia definition; REM surges average 26% above baseline | +| HR min | 40 bpm | 30 bpm | Elite athletes document sleeping HR in low 30s (AHA bradycardia guidelines) | +| BR range | 8-20 | 6-22 | Deep N3 can dip near 7; mild tachypnea during REM accommodated | +| HRV max | 200 ms | 300 ms | Young adults exceed 200ms SDNN during deep NREM | +| HRV metric | SDNN | RMSSD | Parasympathetic dominance during sleep; industry standard (Oura, Garmin, Fitbit) | +| Presence | Fixed 200k | 6Γ— noise floor | Adapts to sensor drift, mattress aging, material differences | + +## Quality Scoring + +Each vitals reading gets a composite quality score (0.0-1.0) in the `vitals_quality` table: + +``` +quality = 0.35 Γ— SNR + 0.30 Γ— HR_confidence + 0.15 Γ— BR_confidence + 0.20 Γ— motion_score +``` + +| Score | Meaning | +|-------|---------| +| 0.80-1.00 | High confidence (green) | +| 0.50-0.79 | Moderate (yellow) | +| 0.20-0.49 | Low β€” values may be inaccurate (orange) | +| < 0.20 | Reject β€” treat as null (red) | + +## Module Integration Pattern + +### Startup +Load profile from DB; if none exists (fresh install), use hardcoded defaults. If profile > 48h old, use with warning. + +### Running +Poll `created_at` every 60s. If newer, hot-swap thresholds (no restart needed). + +### Graceful Degradation +| State | Behavior | +|-------|----------| +| No profile | Hardcoded defaults | +| Stale (>48h) | Use with warning log | +| Fresh (<48h) | Full adaptive thresholds | +| Calibration fails | Previous profile remains | + +## Sources + +- `docs/adr/0014-sensor-calibration.md` +- `docs/hardware/calibration-architecture.md` diff --git a/docs/wiki/topics/sensor-hardware.md b/docs/wiki/topics/sensor-hardware.md new file mode 100644 index 00000000..8a58e97b --- /dev/null +++ b/docs/wiki/topics/sensor-hardware.md @@ -0,0 +1,72 @@ +# Sensor Hardware + +CBOR record types and data shapes across pod generations. The [[biometrics-system]] modules must handle all known variants. + +## Record Type Matrix + +| Record Type | Pod 3 | Pod 5 | Notes | +|---|---|---|---| +| `piezo-dual` | Yes | Yes | Same format β€” used by [[piezo-processing]] | +| `capSense` | Yes | No | 3 named integer channels β€” used by [[sleep-detection]] | +| `capSense2` | No | Yes | 8 unnamed float channels β€” used by [[sleep-detection]] | +| `bedTemp` / `bedTemp2` | Pod 3 / Pod 5 | β€” | Generation-specific | +| `frzTemp` | Yes | Yes | Same format | +| `frzTherm` | No | Yes | Pod 5 only | +| `frzHealth` | No | Yes | Pod 5 only β€” provides pump RPM for gating | +| `log` | Yes | Yes | Same format | + +## capSense vs capSense2 + +### capSense (Pod 3) +3 named integer channels per side: `out`, `cen`, `in`. Values in hundreds/thousands. + +### capSense2 (Pod 5) +8 unnamed float channels per side in a `values` array. 4 redundant pairs (3 sensing + 1 reference). Values in tens. + +### Channel Map (Pod 5, validated 2026-03-16) + +| Index | Name | Empty | Occupied (left) | Delta | Role | +|---|---|---|---|---|---| +| 0-1 | A pair | 14.03 | 33.85 | **+19.8** | Primary presence (highest sensitivity) | +| 2-3 | B pair | 14.31 | 23.90 | **+9.6** | Secondary presence | +| 4-5 | C pair | 19.40 | 24.15 | **+4.8** | Tertiary presence | +| 6-7 | REF pair | 1.16 | 1.15 | -0.01 | Reference/ground (no body response) | + +Key observations: +- A pair is most sensitive (+140% delta) β€” best single indicator of presence +- REF pair confirmed as reference (used for drift compensation in [[sleep-detection]]) +- No cross-talk between left and right sides +- After exit, values remain slightly elevated and decay slowly (residual heat signature) +- Sampling rate: ~2 Hz + +### Comparison + +| Property | capSense (Pod 3) | capSense2 (Pod 5) | +|---|---|---| +| Channels | 3 named (`out`, `cen`, `in`) | 4 pairs (8 values), 3 sensing + 1 ref | +| Data type | Integer (hundreds/thousands) | Float (tens) | +| Reference channel | None | ch6-7 (~1.16, stable) | +| Pairing | Single-ended | Redundant pairs (r > 0.99) | + +## Impact on Calibration + +The [[sensor-calibration]] system needs separate calibrators per sensor type because of different data shapes, value ranges, and reference channel availability. See `CapCalibrator` vs future `CapSense2Calibrator`. + +## Pod Hardware + +| Field | Pod 5 | +|---|---| +| Board | MT8365 Pumpkin | +| SoC | MediaTek MT8365 (aarch64) | +| Kernel | 5.15.42 | +| OS | Eight Layer 4.0.2 (Yocto kirkstone) | + +## Discovery Notes + +- capSense2 format identified by community member Ely as specific to newest Pod 5 cover version +- Cap sense calibration has never worked on Pod 5 β€” code was only tested on Pod 3 +- Format difference is hardware-specific (different cover PCB), not a firmware update + +## Sources + +- `docs/hardware/sensor-profiles.md` diff --git a/docs/wiki/topics/sleep-detection.md b/docs/wiki/topics/sleep-detection.md new file mode 100644 index 00000000..b290a57b --- /dev/null +++ b/docs/wiki/topics/sleep-detection.md @@ -0,0 +1,72 @@ +# Sleep Detection + +Bed occupancy tracking, movement scoring, and sleep session boundaries from capacitance sensor data. Part of the [[biometrics-system]]. + +## Overview + +The sleep-detector tails CBOR-encoded `.RAW` files, processes `capSense` (Pod 3) and `capSense2` (Pod 5) records at ~2 Hz, and writes to `sleep_records` and `movement` tables in `biometrics.db`. + +## Movement Scoring (PIM) + +Movement is the sum of absolute sample-to-sample deltas across 3 sensing channel pairs, accumulated over 60-second epochs. This is the bed-sensor analog of wrist actigraphy's Proportional Integration Mode. + +### Why deltas, not z-scores + +The previous approach measured "how present" someone was, not "how much they moved." A person lying still scored 28,000-75,000 β€” nearly the same as rolling over. Delta scoring removes the DC presence offset entirely: + +| Score | State | Expected during sleep | +|-------|-------|-----------------------| +| 0-50 | Still (deep sleep) | 70-80% of epochs | +| 50-200 | Minor fidgeting | 10-15% | +| 200-500 | Limb repositioning | 5-10% | +| 500+ | Major position change | 1-3% (~1-2/hour) | + +### Processing Pipeline + +1. **Sentinel filter** β€” capSense2 firmware occasionally emits -1.0; handled via zero-order hold +2. **Pair averaging** β€” average redundant channel pairs (A1+A2)/2, etc. +3. **Reference compensation** β€” subtract ref channel drift from nominal 1.16 (common-mode rejection) +4. **Pump gate** β€” suppress deltas during pump activity (see below) +5. **Per-channel delta** β€” |current - previous| per channel +6. **60s epoch accumulation** β†’ `raw_score = min(1000, sum Γ— scale)` +7. **Baseline subtraction** β€” subtract P5 of trailing 30 epochs (removes slow-building noise) +8. **3-epoch median filter** β€” suppress isolated spike artifacts +9. **Clamp** to [0, 1000] + +Scale factor is sensor-dependent: capSense2 (Pod 5) uses Γ—10, capSense (Pod 3) uses Γ—0.5 to normalize different ADC ranges. + +## Pump Artifact Gating + +Without gating, pump vibrations accumulate to raw scores of 60-200 per pump-active epoch, escalating from ~50 to 960-990 by early morning. + +Three-signal detection: +1. **frzHealth pump RPM** (Pod 5 only, ~0.06 Hz) β€” any RPM > 0 means pump running +2. **Reference channel anomaly** β€” |ref_delta| > 0.02 AND 2+ active channels correlate +3. **3-second guard period** after pump-off (shorter than [[piezo-processing|piezo's 5s]] β€” capacitive sensors are less sensitive to mechanical vibration) + +## Presence Detection + +Uses calibrated z-score thresholds from `calibration_profiles` when available (see [[sensor-calibration]]), falling back to fixed thresholds (1500 for capSense, 60.0 for capSense2). Profiles reloaded every 60 seconds. + +## Sleep Sessions + +- **Start**: first present sample +- **End**: after 120s consecutive absence (`ABSENCE_TIMEOUT_S`) +- **Minimum**: 300s (5 min) β€” shorter periods discarded as false positives +- **Bed exits**: mid-session absences counted + +Records include entry/exit timestamps, duration, exit count, and present/absent interval arrays. + +## Known Limitations + +1. Cross-side vibration coupling causes brief spikes (200-500) on the empty side +2. Presence chattering on drifted baselines inflates `times_exited_bed` +3. No sleep stage classification (wake vs sleep only via movement density) +4. Scale calibration tuned on one Pod 5 +5. frzHealth pump signal is Pod 5 only with ~16s detection latency +6. Pump gate field names not confirmed on all firmware versions +7. Baseline subtraction cold start: first 10 minutes not baseline-subtracted + +## Sources + +- `docs/sleep-detector.md` diff --git a/instrumentation.ts b/instrumentation.ts index 8e0ffbe1..112b7f92 100644 --- a/instrumentation.ts +++ b/instrumentation.ts @@ -21,6 +21,7 @@ import { getDacMonitor, shutdownDacMonitor } from '@/src/hardware/dacMonitor.ins import { startPiezoStreamServer, shutdownPiezoStreamServer } from '@/src/streaming/piezoStream' import { startBonjourAnnouncement, stopBonjourAnnouncement } from '@/src/streaming/bonjourAnnounce' import { initializeKeepalives, shutdownKeepalives } from '@/src/services/temperatureKeepalive' +import { startAutoOffWatcher, stopAutoOffWatcher } from '@/src/services/autoOffWatcher' let isInitialized = false let isShuttingDown = false @@ -75,7 +76,15 @@ async function gracefulShutdown(signal: string): Promise { console.error('Error stopping Bonjour:', error) } - // Step 4: Shutdown DAC monitor + // Step 4: Stop auto-off watcher (await in-flight power-off calls) + try { + await stopAutoOffWatcher() + } + catch (error) { + console.error('Error stopping auto-off watcher:', error) + } + + // Step 5: Shutdown DAC monitor try { await shutdownDacMonitor() } @@ -83,7 +92,7 @@ async function gracefulShutdown(signal: string): Promise { console.error('Error shutting down DacMonitor:', error) } - // Step 5: Close database connections + // Step 6: Close database connections try { closeDatabase() closeBiometricsDatabase() @@ -275,6 +284,9 @@ export async function initializeScheduler(): Promise { ) } + // Start auto-off watcher (polls biometrics DB for bed-exit events) + startAutoOffWatcher() + // Start Bonjour/mDNS announcement (non-blocking) startBonjourAnnouncement() } diff --git a/modules/calibrator/pyproject.toml b/modules/calibrator/pyproject.toml new file mode 100644 index 00000000..c41834f9 --- /dev/null +++ b/modules/calibrator/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "calibrator" +version = "0.1.0" +requires-python = ">=3.9,<3.11" +dependencies = [ + "cbor2>=5.9.0", +] diff --git a/modules/calibrator/requirements.txt b/modules/calibrator/requirements.txt deleted file mode 100644 index 615ca8ae..00000000 --- a/modules/calibrator/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cbor2 diff --git a/modules/calibrator/sleepypod-calibrator.service b/modules/calibrator/sleepypod-calibrator.service index b04cfaf5..71160d0a 100644 --- a/modules/calibrator/sleepypod-calibrator.service +++ b/modules/calibrator/sleepypod-calibrator.service @@ -9,7 +9,7 @@ Type=simple User=dac UMask=0002 WorkingDirectory=/opt/sleepypod/modules/calibrator -ExecStart=/opt/sleepypod/modules/calibrator/venv/bin/python main.py +ExecStart=/opt/sleepypod/modules/calibrator/.venv/bin/python main.py Restart=always RestartSec=30 diff --git a/modules/calibrator/uv.lock b/modules/calibrator/uv.lock new file mode 100644 index 00000000..e527bc79 --- /dev/null +++ b/modules/calibrator/uv.lock @@ -0,0 +1,37 @@ +version = 1 +revision = 3 +requires-python = ">=3.9, <3.11" + +[[package]] +name = "calibrator" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "cbor2" }, +] + +[package.metadata] +requires-dist = [{ name = "cbor2", specifier = ">=5.9.0" }] + +[[package]] +name = "cbor2" +version = "5.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/cb/09939728be094d155b5d4ac262e39877875f5f7e36eea66beb359f647bd0/cbor2-5.9.0.tar.gz", hash = "sha256:85c7a46279ac8f226e1059275221e6b3d0e370d2bb6bd0500f9780781615bcea", size = 111231, upload-time = "2026-03-22T15:56:50.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/bf/12b337e5354e47f6378da18989480c0c1e2cc5fe9b865e6fab45d6332aa6/cbor2-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55bea0dd9a7d354e35f4e5fe58ceab393e76962713749dc3a0a64a0e5d19545e", size = 70577, upload-time = "2026-03-22T15:55:54.174Z" }, + { url = "https://files.pythonhosted.org/packages/45/7b/74c524ce81c1ddc6c44b4865028ffb7d3a8e7ae653b1061650375a28cbb1/cbor2-5.9.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095dc49e75572841a9534cbfdabc2a17487ea4ee33341436abc4a7ac7245a3a", size = 261074, upload-time = "2026-03-22T15:55:55.654Z" }, + { url = "https://files.pythonhosted.org/packages/4b/97/c496c71422b2ca18ff2acc5f49e8c45cd7294b7df1359eccec78021b428e/cbor2-5.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25bec7beb2089465382b1be72e78667fe9090598800826559c3e3008cf0db743", size = 255498, upload-time = "2026-03-22T15:55:57.256Z" }, + { url = "https://files.pythonhosted.org/packages/c2/40/9bd7e66dba7aea674a440e004faea406de42c49aeac23453954b67768532/cbor2-5.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cc5efec69055c3c470997935d95762be7e4bfd1248d88fb1a33bb7e0f45712e9", size = 255683, upload-time = "2026-03-22T15:55:58.721Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d1/fa3e158dbe4c08091495b720c604624b285bc272afdbcfac2150725d955b/cbor2-5.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:420d2490c7836c81151b4bd591c35cffc55391e33e7e333c50fda391bcea7d31", size = 250798, upload-time = "2026-03-22T15:55:59.953Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/3186084b441c4b0caebfaaa2c66a57bde82ceaacd469570c77c8030b6b95/cbor2-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d1a21c006760f95acd9509cc5a7d15d6fc82e58f721f94fa9039b4e77189a6e5", size = 69436, upload-time = "2026-03-22T15:56:01.466Z" }, + { url = "https://files.pythonhosted.org/packages/36/1f/57d00cd13e0f9391bcd616795aa78409210a7464570fe21e3b9cd42eb5a7/cbor2-5.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:08388ea54195738602b4c4999966bcaef6f0b17d293c9658658409d9fff96f57", size = 65312, upload-time = "2026-03-22T15:56:02.804Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/f052f558e29f90ed29f9a42263232f6f059fd7bbf1b5d27e3867f9356375/cbor2-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1da96ce5d852fe3d342c1eb2c202a52d1c97edfddc9230f1be7e02674662bf26", size = 70582, upload-time = "2026-03-22T15:56:40.441Z" }, + { url = "https://files.pythonhosted.org/packages/41/fb/91fa1b15b525577ec20a8904f22d8e97eb81164f6663705539e89acdde8f/cbor2-5.9.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65f8eac3268c608533f326f0fd9010ab1b2a8a917b05edaf3853116336821669", size = 260147, upload-time = "2026-03-22T15:56:41.932Z" }, + { url = "https://files.pythonhosted.org/packages/15/12/4013441f8fccca0c5bff043c6ff8b86fde03c5da8b41a22694d49af02b3e/cbor2-5.9.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f797532d13469f2193e5c16e827d8df7a8c33674b19be755790b54ab231e6a73", size = 254507, upload-time = "2026-03-22T15:56:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/d7/26/b96e357ca8554a5458939bc9a7a6f83a494ce15470c96d02f79188fa81c7/cbor2-5.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fbdcf4d74acbeb7672e6413e81cd2c1ced1a4a8cf949484ac54e9af5265c3c72", size = 254592, upload-time = "2026-03-22T15:56:44.402Z" }, + { url = "https://files.pythonhosted.org/packages/1d/06/db0fcac41763153e20aca5a096ee3727bfc591d7825daa5bad493bb99b6d/cbor2-5.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:53cfa49e0df9c639beb871d480de098eedc81eb63ff29f2dc922720d7577b676", size = 249671, upload-time = "2026-03-22T15:56:45.581Z" }, + { url = "https://files.pythonhosted.org/packages/25/9e/484d2b68f2e720add45e3d0d61976fe2abd86dea31ec13038fb630097ef1/cbor2-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:f29e5c3abcc91c1aeefecde0e057bf33f1655588d3065c6560c30ceb3be6f333", size = 69511, upload-time = "2026-03-22T15:56:46.685Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cd/b6653d28c6ebb42402c2d22044108ee59ff4d064eae435ce20894a4847ec/cbor2-5.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:d8524a8c142c3cc228e635f8a97499a6c0b18ca91382e8276565658035cdcb6d", size = 65375, upload-time = "2026-03-22T15:56:47.73Z" }, + { url = "https://files.pythonhosted.org/packages/42/ff/b83492b096fbef26e9cb62c1a4bf2d3cef579ea7b33138c6c37c4ae66f67/cbor2-5.9.0-py3-none-any.whl", hash = "sha256:27695cbd70c90b8de5c4a284642c2836449b14e2c2e07e3ffe0744cb7669a01b", size = 24627, upload-time = "2026-03-22T15:56:48.847Z" }, +] diff --git a/modules/environment-monitor/pyproject.toml b/modules/environment-monitor/pyproject.toml new file mode 100644 index 00000000..0070b745 --- /dev/null +++ b/modules/environment-monitor/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "environment-monitor" +version = "0.1.0" +requires-python = ">=3.9,<3.11" +dependencies = [ + "cbor2>=5.9.0", +] diff --git a/modules/environment-monitor/requirements.txt b/modules/environment-monitor/requirements.txt deleted file mode 100644 index 156b9bbd..00000000 --- a/modules/environment-monitor/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -cbor2==5.8.0 diff --git a/modules/environment-monitor/sleepypod-environment-monitor.service b/modules/environment-monitor/sleepypod-environment-monitor.service index c8d6700c..77f22847 100644 --- a/modules/environment-monitor/sleepypod-environment-monitor.service +++ b/modules/environment-monitor/sleepypod-environment-monitor.service @@ -9,7 +9,7 @@ Type=simple User=dac UMask=0002 WorkingDirectory=/opt/sleepypod/modules/environment-monitor -ExecStart=/opt/sleepypod/modules/environment-monitor/venv/bin/python main.py +ExecStart=/opt/sleepypod/modules/environment-monitor/.venv/bin/python main.py Restart=always RestartSec=10 diff --git a/modules/environment-monitor/uv.lock b/modules/environment-monitor/uv.lock new file mode 100644 index 00000000..dd282600 --- /dev/null +++ b/modules/environment-monitor/uv.lock @@ -0,0 +1,37 @@ +version = 1 +revision = 3 +requires-python = ">=3.9, <3.11" + +[[package]] +name = "cbor2" +version = "5.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/cb/09939728be094d155b5d4ac262e39877875f5f7e36eea66beb359f647bd0/cbor2-5.9.0.tar.gz", hash = "sha256:85c7a46279ac8f226e1059275221e6b3d0e370d2bb6bd0500f9780781615bcea", size = 111231, upload-time = "2026-03-22T15:56:50.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/bf/12b337e5354e47f6378da18989480c0c1e2cc5fe9b865e6fab45d6332aa6/cbor2-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55bea0dd9a7d354e35f4e5fe58ceab393e76962713749dc3a0a64a0e5d19545e", size = 70577, upload-time = "2026-03-22T15:55:54.174Z" }, + { url = "https://files.pythonhosted.org/packages/45/7b/74c524ce81c1ddc6c44b4865028ffb7d3a8e7ae653b1061650375a28cbb1/cbor2-5.9.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095dc49e75572841a9534cbfdabc2a17487ea4ee33341436abc4a7ac7245a3a", size = 261074, upload-time = "2026-03-22T15:55:55.654Z" }, + { url = "https://files.pythonhosted.org/packages/4b/97/c496c71422b2ca18ff2acc5f49e8c45cd7294b7df1359eccec78021b428e/cbor2-5.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25bec7beb2089465382b1be72e78667fe9090598800826559c3e3008cf0db743", size = 255498, upload-time = "2026-03-22T15:55:57.256Z" }, + { url = "https://files.pythonhosted.org/packages/c2/40/9bd7e66dba7aea674a440e004faea406de42c49aeac23453954b67768532/cbor2-5.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cc5efec69055c3c470997935d95762be7e4bfd1248d88fb1a33bb7e0f45712e9", size = 255683, upload-time = "2026-03-22T15:55:58.721Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d1/fa3e158dbe4c08091495b720c604624b285bc272afdbcfac2150725d955b/cbor2-5.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:420d2490c7836c81151b4bd591c35cffc55391e33e7e333c50fda391bcea7d31", size = 250798, upload-time = "2026-03-22T15:55:59.953Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/3186084b441c4b0caebfaaa2c66a57bde82ceaacd469570c77c8030b6b95/cbor2-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d1a21c006760f95acd9509cc5a7d15d6fc82e58f721f94fa9039b4e77189a6e5", size = 69436, upload-time = "2026-03-22T15:56:01.466Z" }, + { url = "https://files.pythonhosted.org/packages/36/1f/57d00cd13e0f9391bcd616795aa78409210a7464570fe21e3b9cd42eb5a7/cbor2-5.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:08388ea54195738602b4c4999966bcaef6f0b17d293c9658658409d9fff96f57", size = 65312, upload-time = "2026-03-22T15:56:02.804Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/f052f558e29f90ed29f9a42263232f6f059fd7bbf1b5d27e3867f9356375/cbor2-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1da96ce5d852fe3d342c1eb2c202a52d1c97edfddc9230f1be7e02674662bf26", size = 70582, upload-time = "2026-03-22T15:56:40.441Z" }, + { url = "https://files.pythonhosted.org/packages/41/fb/91fa1b15b525577ec20a8904f22d8e97eb81164f6663705539e89acdde8f/cbor2-5.9.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65f8eac3268c608533f326f0fd9010ab1b2a8a917b05edaf3853116336821669", size = 260147, upload-time = "2026-03-22T15:56:41.932Z" }, + { url = "https://files.pythonhosted.org/packages/15/12/4013441f8fccca0c5bff043c6ff8b86fde03c5da8b41a22694d49af02b3e/cbor2-5.9.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f797532d13469f2193e5c16e827d8df7a8c33674b19be755790b54ab231e6a73", size = 254507, upload-time = "2026-03-22T15:56:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/d7/26/b96e357ca8554a5458939bc9a7a6f83a494ce15470c96d02f79188fa81c7/cbor2-5.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fbdcf4d74acbeb7672e6413e81cd2c1ced1a4a8cf949484ac54e9af5265c3c72", size = 254592, upload-time = "2026-03-22T15:56:44.402Z" }, + { url = "https://files.pythonhosted.org/packages/1d/06/db0fcac41763153e20aca5a096ee3727bfc591d7825daa5bad493bb99b6d/cbor2-5.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:53cfa49e0df9c639beb871d480de098eedc81eb63ff29f2dc922720d7577b676", size = 249671, upload-time = "2026-03-22T15:56:45.581Z" }, + { url = "https://files.pythonhosted.org/packages/25/9e/484d2b68f2e720add45e3d0d61976fe2abd86dea31ec13038fb630097ef1/cbor2-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:f29e5c3abcc91c1aeefecde0e057bf33f1655588d3065c6560c30ceb3be6f333", size = 69511, upload-time = "2026-03-22T15:56:46.685Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cd/b6653d28c6ebb42402c2d22044108ee59ff4d064eae435ce20894a4847ec/cbor2-5.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:d8524a8c142c3cc228e635f8a97499a6c0b18ca91382e8276565658035cdcb6d", size = 65375, upload-time = "2026-03-22T15:56:47.73Z" }, + { url = "https://files.pythonhosted.org/packages/42/ff/b83492b096fbef26e9cb62c1a4bf2d3cef579ea7b33138c6c37c4ae66f67/cbor2-5.9.0-py3-none-any.whl", hash = "sha256:27695cbd70c90b8de5c4a284642c2836449b14e2c2e07e3ffe0744cb7669a01b", size = 24627, upload-time = "2026-03-22T15:56:48.847Z" }, +] + +[[package]] +name = "environment-monitor" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "cbor2" }, +] + +[package.metadata] +requires-dist = [{ name = "cbor2", specifier = ">=5.9.0" }] diff --git a/modules/piezo-processor/prototype_v2.py b/modules/piezo-processor/prototype_v2.py index 3c1e32a0..8bd0db4d 100644 --- a/modules/piezo-processor/prototype_v2.py +++ b/modules/piezo-processor/prototype_v2.py @@ -5,7 +5,7 @@ Run on-pod against live RAW data. Prints results to stdout. Usage (on pod): - /opt/sleepypod/modules/piezo-processor/venv/bin/python prototype_v2.py + /opt/sleepypod/modules/piezo-processor/.venv/bin/python prototype_v2.py Pipeline (per literature): - Pump gating: dual-channel energy + 5s guard (Shin et al. IEEE TBME 2009) diff --git a/modules/piezo-processor/pyproject.toml b/modules/piezo-processor/pyproject.toml new file mode 100644 index 00000000..2975bc5c --- /dev/null +++ b/modules/piezo-processor/pyproject.toml @@ -0,0 +1,10 @@ +[project] +name = "piezo-processor" +version = "0.1.0" +requires-python = ">=3.9,<3.11" +dependencies = [ + "cbor2>=5.9.0", + "heartpy>=1.2.7", + "numpy>=2.0.2", + "scipy>=1.13.1,<1.16", +] diff --git a/modules/piezo-processor/requirements.txt b/modules/piezo-processor/requirements.txt deleted file mode 100644 index 813d473d..00000000 --- a/modules/piezo-processor/requirements.txt +++ /dev/null @@ -1,4 +0,0 @@ -cbor2==5.8.0 -numpy==2.2.6 -scipy==1.15.3 # 1.16+ requires Python 3.11; pod runs 3.10 -heartpy==1.2.7 diff --git a/modules/piezo-processor/sleepypod-piezo-processor.service b/modules/piezo-processor/sleepypod-piezo-processor.service index c91413aa..ed58c935 100644 --- a/modules/piezo-processor/sleepypod-piezo-processor.service +++ b/modules/piezo-processor/sleepypod-piezo-processor.service @@ -9,7 +9,7 @@ Type=simple User=root UMask=0002 WorkingDirectory=/opt/sleepypod/modules/piezo-processor -ExecStart=/opt/sleepypod/modules/piezo-processor/venv/bin/python main.py +ExecStart=/opt/sleepypod/modules/piezo-processor/.venv/bin/python main.py Restart=always RestartSec=10 diff --git a/modules/piezo-processor/uv.lock b/modules/piezo-processor/uv.lock new file mode 100644 index 00000000..88a40a5e --- /dev/null +++ b/modules/piezo-processor/uv.lock @@ -0,0 +1,577 @@ +version = 1 +revision = 3 +requires-python = ">=3.9, <3.11" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] + +[[package]] +name = "cbor2" +version = "5.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/cb/09939728be094d155b5d4ac262e39877875f5f7e36eea66beb359f647bd0/cbor2-5.9.0.tar.gz", hash = "sha256:85c7a46279ac8f226e1059275221e6b3d0e370d2bb6bd0500f9780781615bcea", size = 111231, upload-time = "2026-03-22T15:56:50.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/bf/12b337e5354e47f6378da18989480c0c1e2cc5fe9b865e6fab45d6332aa6/cbor2-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55bea0dd9a7d354e35f4e5fe58ceab393e76962713749dc3a0a64a0e5d19545e", size = 70577, upload-time = "2026-03-22T15:55:54.174Z" }, + { url = "https://files.pythonhosted.org/packages/45/7b/74c524ce81c1ddc6c44b4865028ffb7d3a8e7ae653b1061650375a28cbb1/cbor2-5.9.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095dc49e75572841a9534cbfdabc2a17487ea4ee33341436abc4a7ac7245a3a", size = 261074, upload-time = "2026-03-22T15:55:55.654Z" }, + { url = "https://files.pythonhosted.org/packages/4b/97/c496c71422b2ca18ff2acc5f49e8c45cd7294b7df1359eccec78021b428e/cbor2-5.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25bec7beb2089465382b1be72e78667fe9090598800826559c3e3008cf0db743", size = 255498, upload-time = "2026-03-22T15:55:57.256Z" }, + { url = "https://files.pythonhosted.org/packages/c2/40/9bd7e66dba7aea674a440e004faea406de42c49aeac23453954b67768532/cbor2-5.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cc5efec69055c3c470997935d95762be7e4bfd1248d88fb1a33bb7e0f45712e9", size = 255683, upload-time = "2026-03-22T15:55:58.721Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d1/fa3e158dbe4c08091495b720c604624b285bc272afdbcfac2150725d955b/cbor2-5.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:420d2490c7836c81151b4bd591c35cffc55391e33e7e333c50fda391bcea7d31", size = 250798, upload-time = "2026-03-22T15:55:59.953Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/3186084b441c4b0caebfaaa2c66a57bde82ceaacd469570c77c8030b6b95/cbor2-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d1a21c006760f95acd9509cc5a7d15d6fc82e58f721f94fa9039b4e77189a6e5", size = 69436, upload-time = "2026-03-22T15:56:01.466Z" }, + { url = "https://files.pythonhosted.org/packages/36/1f/57d00cd13e0f9391bcd616795aa78409210a7464570fe21e3b9cd42eb5a7/cbor2-5.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:08388ea54195738602b4c4999966bcaef6f0b17d293c9658658409d9fff96f57", size = 65312, upload-time = "2026-03-22T15:56:02.804Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/f052f558e29f90ed29f9a42263232f6f059fd7bbf1b5d27e3867f9356375/cbor2-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1da96ce5d852fe3d342c1eb2c202a52d1c97edfddc9230f1be7e02674662bf26", size = 70582, upload-time = "2026-03-22T15:56:40.441Z" }, + { url = "https://files.pythonhosted.org/packages/41/fb/91fa1b15b525577ec20a8904f22d8e97eb81164f6663705539e89acdde8f/cbor2-5.9.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65f8eac3268c608533f326f0fd9010ab1b2a8a917b05edaf3853116336821669", size = 260147, upload-time = "2026-03-22T15:56:41.932Z" }, + { url = "https://files.pythonhosted.org/packages/15/12/4013441f8fccca0c5bff043c6ff8b86fde03c5da8b41a22694d49af02b3e/cbor2-5.9.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f797532d13469f2193e5c16e827d8df7a8c33674b19be755790b54ab231e6a73", size = 254507, upload-time = "2026-03-22T15:56:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/d7/26/b96e357ca8554a5458939bc9a7a6f83a494ce15470c96d02f79188fa81c7/cbor2-5.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fbdcf4d74acbeb7672e6413e81cd2c1ced1a4a8cf949484ac54e9af5265c3c72", size = 254592, upload-time = "2026-03-22T15:56:44.402Z" }, + { url = "https://files.pythonhosted.org/packages/1d/06/db0fcac41763153e20aca5a096ee3727bfc591d7825daa5bad493bb99b6d/cbor2-5.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:53cfa49e0df9c639beb871d480de098eedc81eb63ff29f2dc922720d7577b676", size = 249671, upload-time = "2026-03-22T15:56:45.581Z" }, + { url = "https://files.pythonhosted.org/packages/25/9e/484d2b68f2e720add45e3d0d61976fe2abd86dea31ec13038fb630097ef1/cbor2-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:f29e5c3abcc91c1aeefecde0e057bf33f1655588d3065c6560c30ceb3be6f333", size = 69511, upload-time = "2026-03-22T15:56:46.685Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cd/b6653d28c6ebb42402c2d22044108ee59ff4d064eae435ce20894a4847ec/cbor2-5.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:d8524a8c142c3cc228e635f8a97499a6c0b18ca91382e8276565658035cdcb6d", size = 65375, upload-time = "2026-03-22T15:56:47.73Z" }, + { url = "https://files.pythonhosted.org/packages/42/ff/b83492b096fbef26e9cb62c1a4bf2d3cef579ea7b33138c6c37c4ae66f67/cbor2-5.9.0-py3-none-any.whl", hash = "sha256:27695cbd70c90b8de5c4a284642c2836449b14e2c2e07e3ffe0744cb7669a01b", size = 24627, upload-time = "2026-03-22T15:56:48.847Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/f6/31a8f28b4a2a4fa0e01085e542f3081ab0588eff8e589d39d775172c9792/contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4", size = 13464370, upload-time = "2024-08-27T21:00:03.328Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/e0/be8dcc796cfdd96708933e0e2da99ba4bb8f9b2caa9d560a50f3f09a65f3/contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7", size = 265366, upload-time = "2024-08-27T20:50:09.947Z" }, + { url = "https://files.pythonhosted.org/packages/50/d6/c953b400219443535d412fcbbc42e7a5e823291236bc0bb88936e3cc9317/contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42", size = 249226, upload-time = "2024-08-27T20:50:16.1Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b4/6fffdf213ffccc28483c524b9dad46bb78332851133b36ad354b856ddc7c/contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7", size = 308460, upload-time = "2024-08-27T20:50:22.536Z" }, + { url = "https://files.pythonhosted.org/packages/cf/6c/118fc917b4050f0afe07179a6dcbe4f3f4ec69b94f36c9e128c4af480fb8/contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab", size = 347623, upload-time = "2024-08-27T20:50:28.806Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/30ff110a81bfe3abf7b9673284d21ddce8cc1278f6f77393c91199da4c90/contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589", size = 317761, upload-time = "2024-08-27T20:50:35.126Z" }, + { url = "https://files.pythonhosted.org/packages/99/e6/d11966962b1aa515f5586d3907ad019f4b812c04e4546cc19ebf62b5178e/contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41", size = 322015, upload-time = "2024-08-27T20:50:40.318Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e3/182383743751d22b7b59c3c753277b6aee3637049197624f333dac5b4c80/contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d", size = 1262672, upload-time = "2024-08-27T20:50:55.643Z" }, + { url = "https://files.pythonhosted.org/packages/78/53/974400c815b2e605f252c8fb9297e2204347d1755a5374354ee77b1ea259/contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223", size = 1321688, upload-time = "2024-08-27T20:51:11.293Z" }, + { url = "https://files.pythonhosted.org/packages/52/29/99f849faed5593b2926a68a31882af98afbeac39c7fdf7de491d9c85ec6a/contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f", size = 171145, upload-time = "2024-08-27T20:51:15.2Z" }, + { url = "https://files.pythonhosted.org/packages/a9/97/3f89bba79ff6ff2b07a3cbc40aa693c360d5efa90d66e914f0ff03b95ec7/contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b", size = 216019, upload-time = "2024-08-27T20:51:19.365Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e3/b9f72758adb6ef7397327ceb8b9c39c75711affb220e4f53c745ea1d5a9a/contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8", size = 265518, upload-time = "2024-08-27T20:56:01.333Z" }, + { url = "https://files.pythonhosted.org/packages/ec/22/19f5b948367ab5260fb41d842c7a78dae645603881ea6bc39738bcfcabf6/contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c", size = 249350, upload-time = "2024-08-27T20:56:05.432Z" }, + { url = "https://files.pythonhosted.org/packages/26/76/0c7d43263dd00ae21a91a24381b7e813d286a3294d95d179ef3a7b9fb1d7/contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca", size = 309167, upload-time = "2024-08-27T20:56:10.034Z" }, + { url = "https://files.pythonhosted.org/packages/96/3b/cadff6773e89f2a5a492c1a8068e21d3fccaf1a1c1df7d65e7c8e3ef60ba/contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f", size = 348279, upload-time = "2024-08-27T20:56:15.41Z" }, + { url = "https://files.pythonhosted.org/packages/e1/86/158cc43aa549d2081a955ab11c6bdccc7a22caacc2af93186d26f5f48746/contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc", size = 318519, upload-time = "2024-08-27T20:56:21.813Z" }, + { url = "https://files.pythonhosted.org/packages/05/11/57335544a3027e9b96a05948c32e566328e3a2f84b7b99a325b7a06d2b06/contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2", size = 321922, upload-time = "2024-08-27T20:56:26.983Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e3/02114f96543f4a1b694333b92a6dcd4f8eebbefcc3a5f3bbb1316634178f/contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e", size = 1258017, upload-time = "2024-08-27T20:56:42.246Z" }, + { url = "https://files.pythonhosted.org/packages/f3/3b/bfe4c81c6d5881c1c643dde6620be0b42bf8aab155976dd644595cfab95c/contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800", size = 1316773, upload-time = "2024-08-27T20:56:58.58Z" }, + { url = "https://files.pythonhosted.org/packages/f1/17/c52d2970784383cafb0bd918b6fb036d98d96bbf0bc1befb5d1e31a07a70/contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5", size = 171353, upload-time = "2024-08-27T20:57:02.718Z" }, + { url = "https://files.pythonhosted.org/packages/53/23/db9f69676308e094d3c45f20cc52e12d10d64f027541c995d89c11ad5c75/contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843", size = 211817, upload-time = "2024-08-27T20:57:06.328Z" }, + { url = "https://files.pythonhosted.org/packages/d1/09/60e486dc2b64c94ed33e58dcfb6f808192c03dfc5574c016218b9b7680dc/contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c", size = 261886, upload-time = "2024-08-27T20:57:10.863Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/b57f9f7174fcd439a7789fb47d764974ab646fa34d1790551de386457a8e/contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779", size = 311008, upload-time = "2024-08-27T20:57:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/74/fc/5040d42623a1845d4f17a418e590fd7a79ae8cb2bad2b2f83de63c3bdca4/contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4", size = 215690, upload-time = "2024-08-27T20:57:19.321Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/dc3dcd77ac7460ab7e9d2b01a618cb31406902e50e605a8d6091f0a8f7cc/contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0", size = 261894, upload-time = "2024-08-27T20:57:23.873Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/531642a01cfec39d1682e46b5457b07cf805e3c3c584ec27e2a6223f8f6c/contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102", size = 311099, upload-time = "2024-08-27T20:57:28.58Z" }, + { url = "https://files.pythonhosted.org/packages/38/1e/94bda024d629f254143a134eead69e21c836429a2a6ce82209a00ddcb79a/contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb", size = 215838, upload-time = "2024-08-27T20:57:32.913Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "fonttools" +version = "4.60.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/c4/db6a7b5eb0656534c3aa2596c2c5e18830d74f1b9aa5aa8a7dff63a0b11d/fonttools-4.60.2.tar.gz", hash = "sha256:d29552e6b155ebfc685b0aecf8d429cb76c14ab734c22ef5d3dea6fdf800c92c", size = 3562254, upload-time = "2025-12-09T13:38:11.835Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/de/9e10a99fb3070accb8884886a41a4ce54e49bf2fa4fc63f48a6cf2061713/fonttools-4.60.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4e36fadcf7e8ca6e34d490eef86ed638d6fd9c55d2f514b05687622cfc4a7050", size = 2850403, upload-time = "2025-12-09T13:35:53.14Z" }, + { url = "https://files.pythonhosted.org/packages/e4/40/d5b369d1073b134f600a94a287e13b5bdea2191ba6347d813fa3da00e94a/fonttools-4.60.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e500fc9c04bee749ceabfc20cb4903f6981c2139050d85720ea7ada61b75d5c", size = 2398629, upload-time = "2025-12-09T13:35:56.471Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b5/123819369aaf99d1e4dc49f1de1925d4edc7379114d15a56a7dd2e9d56e6/fonttools-4.60.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22efea5e784e1d1cd8d7b856c198e360a979383ebc6dea4604743b56da1cbc34", size = 4893471, upload-time = "2025-12-09T13:35:58.927Z" }, + { url = "https://files.pythonhosted.org/packages/24/29/f8f8acccb9716b899be4be45e9ce770d6aa76327573863e68448183091b0/fonttools-4.60.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:677aa92d84d335e4d301d8ba04afca6f575316bc647b6782cb0921943fcb6343", size = 4854686, upload-time = "2025-12-09T13:36:01.767Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0d/f3f51d7519f44f2dd5c9a60d7cd41185ebcee4348f073e515a3a93af15ff/fonttools-4.60.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:edd49d3defbf35476e78b61ff737ff5efea811acff68d44233a95a5a48252334", size = 4871233, upload-time = "2025-12-09T13:36:06.094Z" }, + { url = "https://files.pythonhosted.org/packages/cc/3f/4d4fd47d3bc40ab4d76718555185f8adffb5602ea572eac4bbf200c47d22/fonttools-4.60.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:126839492b69cecc5baf2bddcde60caab2ffafd867bbae2a88463fce6078ca3a", size = 4988936, upload-time = "2025-12-09T13:36:08.42Z" }, + { url = "https://files.pythonhosted.org/packages/01/6f/83bbdefa43f2c3ae206fd8c4b9a481f3c913eef871b1ce9a453069239e39/fonttools-4.60.2-cp310-cp310-win32.whl", hash = "sha256:ffcab6f5537136046ca902ed2491ab081ba271b07591b916289b7c27ff845f96", size = 2278044, upload-time = "2025-12-09T13:36:10.641Z" }, + { url = "https://files.pythonhosted.org/packages/d4/04/7d9a137e919d6c9ef26704b7f7b2580d9cfc5139597588227aacebc0e3b7/fonttools-4.60.2-cp310-cp310-win_amd64.whl", hash = "sha256:9c68b287c7ffcd29dd83b5f961004b2a54a862a88825d52ea219c6220309ba45", size = 2326522, upload-time = "2025-12-09T13:36:12.981Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/a6d9446cb258d3fe87e311c2d7bacf8e8da3e5809fbdc3a8306db4f6b14e/fonttools-4.60.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a3c75b8b42f7f93906bdba9eb1197bb76aecbe9a0a7cf6feec75f7605b5e8008", size = 2857184, upload-time = "2025-12-09T13:37:49.96Z" }, + { url = "https://files.pythonhosted.org/packages/3a/f3/1b41d0b6a8b908aa07f652111155dd653ebbf0b3385e66562556c5206685/fonttools-4.60.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0f86c8c37bc0ec0b9c141d5e90c717ff614e93c187f06d80f18c7057097f71bc", size = 2401877, upload-time = "2025-12-09T13:37:52.307Z" }, + { url = "https://files.pythonhosted.org/packages/71/57/048fd781680c38b05c5463657d0d95d5f2391a51972176e175c01de29d42/fonttools-4.60.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe905403fe59683b0e9a45f234af2866834376b8821f34633b1c76fb731b6311", size = 4878073, upload-time = "2025-12-09T13:37:56.477Z" }, + { url = "https://files.pythonhosted.org/packages/45/bb/363364f052a893cebd3d449588b21244a9d873620fda03ad92702d2e1bc7/fonttools-4.60.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38ce703b60a906e421e12d9e3a7f064883f5e61bb23e8961f4be33cfe578500b", size = 4835385, upload-time = "2025-12-09T13:37:58.882Z" }, + { url = "https://files.pythonhosted.org/packages/1c/38/e392bb930b2436287e6021672345db26441bf1f85f1e98f8b9784334e41d/fonttools-4.60.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9e810c06f3e79185cecf120e58b343ea5a89b54dd695fd644446bcf8c026da5e", size = 4853084, upload-time = "2025-12-09T13:38:01.578Z" }, + { url = "https://files.pythonhosted.org/packages/65/60/0d77faeaecf7a3276a8a6dc49e2274357e6b3ed6a1774e2fdb2a7f142db0/fonttools-4.60.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:38faec8cc1d12122599814d15a402183f5123fb7608dac956121e7c6742aebc5", size = 4971144, upload-time = "2025-12-09T13:38:03.748Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c7/6d3ac3afbcd598631bce24c3ecb919e7d0644a82fea8ddc4454312fc0be6/fonttools-4.60.2-cp39-cp39-win32.whl", hash = "sha256:80a45cf7bf659acb7b36578f300231873daba67bd3ca8cce181c73f861f14a37", size = 1499411, upload-time = "2025-12-09T13:38:05.586Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/9dedf6420e23f9fa630bb97941839dddd2e1e57d1b2b85a902378dbe0bd2/fonttools-4.60.2-cp39-cp39-win_amd64.whl", hash = "sha256:c355d5972071938e1b1e0f5a1df001f68ecf1a62f34a3407dc8e0beccf052501", size = 1547943, upload-time = "2025-12-09T13:38:07.604Z" }, + { url = "https://files.pythonhosted.org/packages/79/6c/10280af05b44fafd1dff69422805061fa1af29270bc52dce031ac69540bf/fonttools-4.60.2-py3-none-any.whl", hash = "sha256:73cf92eeda67cf6ff10c8af56fc8f4f07c1647d989a979be9e388a49be26552a", size = 1144610, upload-time = "2025-12-09T13:38:09.5Z" }, +] + +[[package]] +name = "fonttools" +version = "4.62.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/08/7012b00a9a5874311b639c3920270c36ee0c445b69d9989a85e5c92ebcb0/fonttools-4.62.1.tar.gz", hash = "sha256:e54c75fd6041f1122476776880f7c3c3295ffa31962dc6ebe2543c00dca58b5d", size = 3580737, upload-time = "2026-03-13T13:54:25.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/ff/532ed43808b469c807e8cb6b21358da3fe6fd51486b3a8c93db0bb5d957f/fonttools-4.62.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ad5cca75776cd453b1b035b530e943334957ae152a36a88a320e779d61fc980c", size = 2873740, upload-time = "2026-03-13T13:52:11.822Z" }, + { url = "https://files.pythonhosted.org/packages/85/e4/2318d2b430562da7227010fb2bb029d2fa54d7b46443ae8942bab224e2a0/fonttools-4.62.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0b3ae47e8636156a9accff64c02c0924cbebad62854c4a6dbdc110cd5b4b341a", size = 2417649, upload-time = "2026-03-13T13:52:14.605Z" }, + { url = "https://files.pythonhosted.org/packages/4c/28/40f15523b5188598018e7956899fed94eb7debec89e2dd70cb4a8df90492/fonttools-4.62.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b9e288b4da2f64fd6180644221749de651703e8d0c16bd4b719533a3a7d6e3", size = 4935213, upload-time = "2026-03-13T13:52:17.399Z" }, + { url = "https://files.pythonhosted.org/packages/42/09/7dbe3d7023f57d9b580cfa832109d521988112fd59dddfda3fddda8218f9/fonttools-4.62.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7bca7a1c1faf235ffe25d4f2e555246b4750220b38de8261d94ebc5ce8a23c23", size = 4892374, upload-time = "2026-03-13T13:52:20.175Z" }, + { url = "https://files.pythonhosted.org/packages/d1/2d/84509a2e32cb925371560ef5431365d8da2183c11d98e5b4b8b4e42426a5/fonttools-4.62.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b4e0fcf265ad26e487c56cb12a42dffe7162de708762db951e1b3f755319507d", size = 4911856, upload-time = "2026-03-13T13:52:22.777Z" }, + { url = "https://files.pythonhosted.org/packages/a5/80/df28131379eed93d9e6e6fccd3bf6e3d077bebbfe98cc83f21bbcd83ed02/fonttools-4.62.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2d850f66830a27b0d498ee05adb13a3781637b1826982cd7e2b3789ef0cc71ae", size = 5031712, upload-time = "2026-03-13T13:52:25.14Z" }, + { url = "https://files.pythonhosted.org/packages/3d/03/3c8f09aad64230cd6d921ae7a19f9603c36f70930b00459f112706f6769a/fonttools-4.62.1-cp310-cp310-win32.whl", hash = "sha256:486f32c8047ccd05652aba17e4a8819a3a9d78570eb8a0e3b4503142947880ed", size = 1507878, upload-time = "2026-03-13T13:52:28.149Z" }, + { url = "https://files.pythonhosted.org/packages/dd/ec/f53f626f8f3e89f4cadd8fc08f3452c8fd182c951ad5caa35efac22b29ab/fonttools-4.62.1-cp310-cp310-win_amd64.whl", hash = "sha256:5a648bde915fba9da05ae98856987ca91ba832949a9e2888b48c47ef8b96c5a9", size = 1556766, upload-time = "2026-03-13T13:52:30.814Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ba/56147c165442cc5ba7e82ecf301c9a68353cede498185869e6e02b4c264f/fonttools-4.62.1-py3-none-any.whl", hash = "sha256:7487782e2113861f4ddcc07c3436450659e3caa5e470b27dc2177cade2d8e7fd", size = 1152647, upload-time = "2026-03-13T13:54:22.735Z" }, +] + +[[package]] +name = "heartpy" +version = "1.2.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "matplotlib", version = "3.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "matplotlib", version = "3.10.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "scipy", version = "1.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0b/20/4ecd6d0cbe00678b931e7712ba7eade122bd352d99b3932896de51c50a40/heartpy-1.2.7.tar.gz", hash = "sha256:01f154f330b7d221f79b7378fb6519e3647573c4274627f29f99bb569d74491e", size = 41058, upload-time = "2021-03-20T15:31:59.217Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/23/b8/a3abfde26b91bb50adde88b1e46b3b0a776f85c981107554787630cbec9a/heartpy-1.2.7-py3-none-any.whl", hash = "sha256:c9df4fbd427bc10b9eaa951a34958a451a586e3c7ec0d1c46488b491abcb8ab7", size = 1002722, upload-time = "2021-03-20T15:31:57.806Z" }, +] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286, upload-time = "2024-09-04T09:39:44.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/14/fc943dd65268a96347472b4fbe5dcc2f6f55034516f80576cd0dd3a8930f/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6", size = 122440, upload-time = "2024-09-04T09:03:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/1e/46/e68fed66236b69dd02fcdb506218c05ac0e39745d696d22709498896875d/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17", size = 65758, upload-time = "2024-09-04T09:03:46.582Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fa/65de49c85838681fc9cb05de2a68067a683717321e01ddafb5b8024286f0/kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9", size = 64311, upload-time = "2024-09-04T09:03:47.973Z" }, + { url = "https://files.pythonhosted.org/packages/42/9c/cc8d90f6ef550f65443bad5872ffa68f3dee36de4974768628bea7c14979/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9", size = 1637109, upload-time = "2024-09-04T09:03:49.281Z" }, + { url = "https://files.pythonhosted.org/packages/55/91/0a57ce324caf2ff5403edab71c508dd8f648094b18cfbb4c8cc0fde4a6ac/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c", size = 1617814, upload-time = "2024-09-04T09:03:51.444Z" }, + { url = "https://files.pythonhosted.org/packages/12/5d/c36140313f2510e20207708adf36ae4919416d697ee0236b0ddfb6fd1050/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599", size = 1400881, upload-time = "2024-09-04T09:03:53.357Z" }, + { url = "https://files.pythonhosted.org/packages/56/d0/786e524f9ed648324a466ca8df86298780ef2b29c25313d9a4f16992d3cf/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05", size = 1512972, upload-time = "2024-09-04T09:03:55.082Z" }, + { url = "https://files.pythonhosted.org/packages/67/5a/77851f2f201e6141d63c10a0708e996a1363efaf9e1609ad0441b343763b/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407", size = 1444787, upload-time = "2024-09-04T09:03:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/06/5f/1f5eaab84355885e224a6fc8d73089e8713dc7e91c121f00b9a1c58a2195/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278", size = 2199212, upload-time = "2024-09-04T09:03:58.557Z" }, + { url = "https://files.pythonhosted.org/packages/b5/28/9152a3bfe976a0ae21d445415defc9d1cd8614b2910b7614b30b27a47270/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5", size = 2346399, upload-time = "2024-09-04T09:04:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/f6/453d1904c52ac3b400f4d5e240ac5fec25263716723e44be65f4d7149d13/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad", size = 2308688, upload-time = "2024-09-04T09:04:02.216Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/d4968499441b9ae187e81745e3277a8b4d7c60840a52dc9d535a7909fac3/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895", size = 2445493, upload-time = "2024-09-04T09:04:04.571Z" }, + { url = "https://files.pythonhosted.org/packages/07/c9/032267192e7828520dacb64dfdb1d74f292765f179e467c1cba97687f17d/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3", size = 2262191, upload-time = "2024-09-04T09:04:05.969Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ad/db0aedb638a58b2951da46ddaeecf204be8b4f5454df020d850c7fa8dca8/kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc", size = 46644, upload-time = "2024-09-04T09:04:07.408Z" }, + { url = "https://files.pythonhosted.org/packages/12/ca/d0f7b7ffbb0be1e7c2258b53554efec1fd652921f10d7d85045aff93ab61/kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c", size = 55877, upload-time = "2024-09-04T09:04:08.869Z" }, + { url = "https://files.pythonhosted.org/packages/97/6c/cfcc128672f47a3e3c0d918ecb67830600078b025bfc32d858f2e2d5c6a4/kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a", size = 48347, upload-time = "2024-09-04T09:04:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/88/37ea0ea64512997b13d69772db8dcdc3bfca5442cda3a5e4bb943652ee3e/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd", size = 122449, upload-time = "2024-09-04T09:05:55.311Z" }, + { url = "https://files.pythonhosted.org/packages/4e/45/5a5c46078362cb3882dcacad687c503089263c017ca1241e0483857791eb/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583", size = 65757, upload-time = "2024-09-04T09:05:56.906Z" }, + { url = "https://files.pythonhosted.org/packages/8a/be/a6ae58978772f685d48dd2e84460937761c53c4bbd84e42b0336473d9775/kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417", size = 64312, upload-time = "2024-09-04T09:05:58.384Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/18ef6f452d311e1e1eb180c9bf5589187fa1f042db877e6fe443ef10099c/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904", size = 1626966, upload-time = "2024-09-04T09:05:59.855Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/40655f6c3fa11ce740e8a964fa8e4c0479c87d6a7944b95af799c7a55dfe/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a", size = 1607044, upload-time = "2024-09-04T09:06:02.16Z" }, + { url = "https://files.pythonhosted.org/packages/fd/93/af67dbcfb9b3323bbd2c2db1385a7139d8f77630e4a37bb945b57188eb2d/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8", size = 1391879, upload-time = "2024-09-04T09:06:03.908Z" }, + { url = "https://files.pythonhosted.org/packages/40/6f/d60770ef98e77b365d96061d090c0cd9e23418121c55fff188fa4bdf0b54/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2", size = 1504751, upload-time = "2024-09-04T09:06:05.58Z" }, + { url = "https://files.pythonhosted.org/packages/fa/3a/5f38667d313e983c432f3fcd86932177519ed8790c724e07d77d1de0188a/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88", size = 1436990, upload-time = "2024-09-04T09:06:08.126Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/1520301a47326e6a6043b502647e42892be33b3f051e9791cc8bb43f1a32/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde", size = 2191122, upload-time = "2024-09-04T09:06:10.345Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c4/eb52da300c166239a2233f1f9c4a1b767dfab98fae27681bfb7ea4873cb6/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c", size = 2338126, upload-time = "2024-09-04T09:06:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/1a/cb/42b92fd5eadd708dd9107c089e817945500685f3437ce1fd387efebc6d6e/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2", size = 2298313, upload-time = "2024-09-04T09:06:14.562Z" }, + { url = "https://files.pythonhosted.org/packages/4f/eb/be25aa791fe5fc75a8b1e0c965e00f942496bc04635c9aae8035f6b76dcd/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb", size = 2437784, upload-time = "2024-09-04T09:06:16.767Z" }, + { url = "https://files.pythonhosted.org/packages/c5/22/30a66be7f3368d76ff95689e1c2e28d382383952964ab15330a15d8bfd03/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327", size = 2253988, upload-time = "2024-09-04T09:06:18.705Z" }, + { url = "https://files.pythonhosted.org/packages/35/d3/5f2ecb94b5211c8a04f218a76133cc8d6d153b0f9cd0b45fad79907f0689/kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644", size = 46980, upload-time = "2024-09-04T09:06:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/ef/17/cd10d020578764ea91740204edc6b3236ed8106228a46f568d716b11feb2/kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4", size = 55847, upload-time = "2024-09-04T09:06:21.407Z" }, + { url = "https://files.pythonhosted.org/packages/91/84/32232502020bd78d1d12be7afde15811c64a95ed1f606c10456db4e4c3ac/kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f", size = 48494, upload-time = "2024-09-04T09:06:22.648Z" }, + { url = "https://files.pythonhosted.org/packages/ac/59/741b79775d67ab67ced9bb38552da688c0305c16e7ee24bba7a2be253fb7/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643", size = 59491, upload-time = "2024-09-04T09:06:24.188Z" }, + { url = "https://files.pythonhosted.org/packages/58/cc/fb239294c29a5656e99e3527f7369b174dd9cc7c3ef2dea7cb3c54a8737b/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706", size = 57648, upload-time = "2024-09-04T09:06:25.559Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ef/2f009ac1f7aab9f81efb2d837301d255279d618d27b6015780115ac64bdd/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6", size = 84257, upload-time = "2024-09-04T09:06:27.038Z" }, + { url = "https://files.pythonhosted.org/packages/81/e1/c64f50987f85b68b1c52b464bb5bf73e71570c0f7782d626d1eb283ad620/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2", size = 80906, upload-time = "2024-09-04T09:06:28.48Z" }, + { url = "https://files.pythonhosted.org/packages/fd/71/1687c5c0a0be2cee39a5c9c389e546f9c6e215e46b691d00d9f646892083/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4", size = 79951, upload-time = "2024-09-04T09:06:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/ea/8b/d7497df4a1cae9367adf21665dd1f896c2a7aeb8769ad77b662c5e2bcce7/kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a", size = 55715, upload-time = "2024-09-04T09:06:31.489Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ce37d9b26f07ab90880923c94d12a6ff4d27447096b4c849bfc4339ccfdf/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39", size = 58666, upload-time = "2024-09-04T09:06:43.756Z" }, + { url = "https://files.pythonhosted.org/packages/b0/d3/e4b04f43bc629ac8e186b77b2b1a251cdfa5b7610fa189dc0db622672ce6/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e", size = 57088, upload-time = "2024-09-04T09:06:45.406Z" }, + { url = "https://files.pythonhosted.org/packages/30/1c/752df58e2d339e670a535514d2db4fe8c842ce459776b8080fbe08ebb98e/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608", size = 84321, upload-time = "2024-09-04T09:06:47.557Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f8/fe6484e847bc6e238ec9f9828089fb2c0bb53f2f5f3a79351fde5b565e4f/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674", size = 80776, upload-time = "2024-09-04T09:06:49.235Z" }, + { url = "https://files.pythonhosted.org/packages/9b/57/d7163c0379f250ef763aba85330a19feefb5ce6cb541ade853aaba881524/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225", size = 79984, upload-time = "2024-09-04T09:06:51.336Z" }, + { url = "https://files.pythonhosted.org/packages/8c/95/4a103776c265d13b3d2cd24fb0494d4e04ea435a8ef97e1b2c026d43250b/kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0", size = 55811, upload-time = "2024-09-04T09:06:53.078Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/f8/06549565caa026e540b7e7bab5c5a90eb7ca986015f4c48dace243cd24d9/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32cc0a5365239a6ea0c6ed461e8838d053b57e397443c0ca894dcc8e388d4374", size = 122802, upload-time = "2026-03-09T13:12:37.515Z" }, + { url = "https://files.pythonhosted.org/packages/84/eb/8476a0818850c563ff343ea7c9c05dcdcbd689a38e01aa31657df01f91fa/kiwisolver-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cc0b66c1eec9021353a4b4483afb12dfd50e3669ffbb9152d6842eb34c7e29fd", size = 66216, upload-time = "2026-03-09T13:12:38.812Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/f9c8a6b4c21aed4198566e45923512986d6cef530e7263b3a5f823546561/kiwisolver-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:86e0287879f75621ae85197b0877ed2f8b7aa57b511c7331dce2eb6f4de7d476", size = 63917, upload-time = "2026-03-09T13:12:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0e/ba4ae25d03722f64de8b2c13e80d82ab537a06b30fc7065183c6439357e3/kiwisolver-1.5.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:62f59da443c4f4849f73a51a193b1d9d258dcad0c41bc4d1b8fb2bcc04bfeb22", size = 1628776, upload-time = "2026-03-09T13:12:41.976Z" }, + { url = "https://files.pythonhosted.org/packages/8a/e4/3f43a011bc8a0860d1c96f84d32fa87439d3feedf66e672fef03bf5e8bac/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9190426b7aa26c5229501fa297b8d0653cfd3f5a36f7990c264e157cbf886b3b", size = 1228164, upload-time = "2026-03-09T13:12:44.002Z" }, + { url = "https://files.pythonhosted.org/packages/4b/34/3a901559a1e0c218404f9a61a93be82d45cb8f44453ba43088644980f033/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c8277104ded0a51e699c8c3aff63ce2c56d4ed5519a5f73e0fd7057f959a2b9e", size = 1246656, upload-time = "2026-03-09T13:12:45.557Z" }, + { url = "https://files.pythonhosted.org/packages/87/9e/f78c466ea20527822b95ad38f141f2de1dcd7f23fb8716b002b0d91bbe59/kiwisolver-1.5.0-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8f9baf6f0a6e7571c45c8863010b45e837c3ee1c2c77fcd6ef423be91b21fedb", size = 1295562, upload-time = "2026-03-09T13:12:47.562Z" }, + { url = "https://files.pythonhosted.org/packages/0a/66/fd0e4a612e3a286c24e6d6f3a5428d11258ed1909bc530ba3b59807fd980/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cff8e5383db4989311f99e814feeb90c4723eb4edca425b9d5d9c3fefcdd9537", size = 2178473, upload-time = "2026-03-09T13:12:50.254Z" }, + { url = "https://files.pythonhosted.org/packages/dc/8e/6cac929e0049539e5ee25c1ee937556f379ba5204840d03008363ced662d/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ebae99ed6764f2b5771c522477b311be313e8841d2e0376db2b10922daebbba4", size = 2274035, upload-time = "2026-03-09T13:12:51.785Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d3/9d0c18f1b52ea8074b792452cf17f1f5a56bd0302a85191f405cfbf9da16/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:d5cd5189fc2b6a538b75ae45433140c4823463918f7b1617c31e68b085c0022c", size = 2443217, upload-time = "2026-03-09T13:12:53.329Z" }, + { url = "https://files.pythonhosted.org/packages/45/2a/6e19368803a038b2a90857bf4ee9e3c7b667216d045866bf22d3439fd75e/kiwisolver-1.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f42c23db5d1521218a3276bb08666dcb662896a0be7347cba864eca45ff64ede", size = 2249196, upload-time = "2026-03-09T13:12:55.057Z" }, + { url = "https://files.pythonhosted.org/packages/75/2b/3f641dfcbe72e222175d626bacf2f72c3b34312afec949dd1c50afa400f5/kiwisolver-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:94eff26096eb5395136634622515b234ecb6c9979824c1f5004c6e3c3c85ccd2", size = 73389, upload-time = "2026-03-09T13:12:56.496Z" }, + { url = "https://files.pythonhosted.org/packages/da/88/299b137b9e0025d8982e03d2d52c123b0a2b159e84b0ef1501ef446339cf/kiwisolver-1.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:dd952e03bfbb096cfe2dd35cd9e00f269969b67536cb4370994afc20ff2d0875", size = 64782, upload-time = "2026-03-09T13:12:57.609Z" }, + { url = "https://files.pythonhosted.org/packages/17/6f/6fd4f690a40c2582fa34b97d2678f718acf3706b91d270c65ecb455d0a06/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:295d9ffe712caa9f8a3081de8d32fc60191b4b51c76f02f951fd8407253528f4", size = 59606, upload-time = "2026-03-09T13:15:40.81Z" }, + { url = "https://files.pythonhosted.org/packages/82/a0/2355d5e3b338f13ce63f361abb181e3b6ea5fffdb73f739b3e80efa76159/kiwisolver-1.5.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:51e8c4084897de9f05898c2c2a39af6318044ae969d46ff7a34ed3f96274adca", size = 57537, upload-time = "2026-03-09T13:15:42.071Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b9/1d50e610ecadebe205b71d6728fd224ce0e0ca6aba7b9cbe1da049203ac5/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b83af57bdddef03c01a9138034c6ff03181a3028d9a1003b301eb1a55e161a3f", size = 79888, upload-time = "2026-03-09T13:15:43.317Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ee/b85ffcd75afed0357d74f0e6fc02a4507da441165de1ca4760b9f496390d/kiwisolver-1.5.0-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf4679a3d71012a7c2bf360e5cd878fbd5e4fcac0896b56393dec239d81529ed", size = 77584, upload-time = "2026-03-09T13:15:44.605Z" }, + { url = "https://files.pythonhosted.org/packages/6b/dd/644d0dde6010a8583b4cd66dd41c5f83f5325464d15c4f490b3340ab73b4/kiwisolver-1.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:41024ed50e44ab1a60d3fe0a9d15a4ccc9f5f2b1d814ff283c8d01134d5b81bc", size = 73390, upload-time = "2026-03-09T13:15:45.832Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.9.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "contourpy", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "cycler", marker = "python_full_version < '3.10'" }, + { name = "fonttools", version = "4.60.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "importlib-resources", marker = "python_full_version < '3.10'" }, + { name = "kiwisolver", version = "1.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "packaging", marker = "python_full_version < '3.10'" }, + { name = "pillow", version = "11.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "pyparsing", marker = "python_full_version < '3.10'" }, + { name = "python-dateutil", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/17/1747b4154034befd0ed33b52538f5eb7752d05bb51c5e2a31470c3bc7d52/matplotlib-3.9.4.tar.gz", hash = "sha256:1e00e8be7393cbdc6fedfa8a6fba02cf3e83814b285db1c60b906a023ba41bc3", size = 36106529, upload-time = "2024-12-13T05:56:34.184Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/94/27d2e2c30d54b56c7b764acc1874a909e34d1965a427fc7092bb6a588b63/matplotlib-3.9.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fdd7abfb706dfa8d307af64a87f1a862879ec3cd8d0ec8637458f0885b9c50", size = 7885089, upload-time = "2024-12-13T05:54:24.224Z" }, + { url = "https://files.pythonhosted.org/packages/c6/25/828273307e40a68eb8e9df832b6b2aaad075864fdc1de4b1b81e40b09e48/matplotlib-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d89bc4e85e40a71d1477780366c27fb7c6494d293e1617788986f74e2a03d7ff", size = 7770600, upload-time = "2024-12-13T05:54:27.214Z" }, + { url = "https://files.pythonhosted.org/packages/f2/65/f841a422ec994da5123368d76b126acf4fc02ea7459b6e37c4891b555b83/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddf9f3c26aae695c5daafbf6b94e4c1a30d6cd617ba594bbbded3b33a1fcfa26", size = 8200138, upload-time = "2024-12-13T05:54:29.497Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/272aca07a38804d93b6050813de41ca7ab0e29ba7a9dd098e12037c919a9/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18ebcf248030173b59a868fda1fe42397253f6698995b55e81e1f57431d85e50", size = 8312711, upload-time = "2024-12-13T05:54:34.396Z" }, + { url = "https://files.pythonhosted.org/packages/98/37/f13e23b233c526b7e27ad61be0a771894a079e0f7494a10d8d81557e0e9a/matplotlib-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974896ec43c672ec23f3f8c648981e8bc880ee163146e0312a9b8def2fac66f5", size = 9090622, upload-time = "2024-12-13T05:54:36.808Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8c/b1f5bd2bd70e60f93b1b54c4d5ba7a992312021d0ddddf572f9a1a6d9348/matplotlib-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:4598c394ae9711cec135639374e70871fa36b56afae17bdf032a345be552a88d", size = 7828211, upload-time = "2024-12-13T05:54:40.596Z" }, + { url = "https://files.pythonhosted.org/packages/56/eb/501b465c9fef28f158e414ea3a417913dc2ac748564c7ed41535f23445b4/matplotlib-3.9.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3c3724d89a387ddf78ff88d2a30ca78ac2b4c89cf37f2db4bd453c34799e933c", size = 7885919, upload-time = "2024-12-13T05:55:59.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/36/236fbd868b6c91309a5206bd90c3f881f4f44b2d997cd1d6239ef652f878/matplotlib-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d5f0a8430ffe23d7e32cfd86445864ccad141797f7d25b7c41759a5b5d17cfd7", size = 7771486, upload-time = "2024-12-13T05:56:04.264Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4b/105caf2d54d5ed11d9f4335398f5103001a03515f2126c936a752ccf1461/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bb0141a21aef3b64b633dc4d16cbd5fc538b727e4958be82a0e1c92a234160e", size = 8201838, upload-time = "2024-12-13T05:56:06.792Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a7/bb01188fb4013d34d274caf44a2f8091255b0497438e8b6c0a7c1710c692/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57aa235109e9eed52e2c2949db17da185383fa71083c00c6c143a60e07e0888c", size = 8314492, upload-time = "2024-12-13T05:56:09.964Z" }, + { url = "https://files.pythonhosted.org/packages/33/19/02e1a37f7141fc605b193e927d0a9cdf9dc124a20b9e68793f4ffea19695/matplotlib-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b18c600061477ccfdd1e6fd050c33d8be82431700f3452b297a56d9ed7037abb", size = 9092500, upload-time = "2024-12-13T05:56:13.55Z" }, + { url = "https://files.pythonhosted.org/packages/57/68/c2feb4667adbf882ffa4b3e0ac9967f848980d9f8b5bebd86644aa67ce6a/matplotlib-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:ef5f2d1b67d2d2145ff75e10f8c008bfbf71d45137c4b648c87193e7dd053eac", size = 7822962, upload-time = "2024-12-13T05:56:16.358Z" }, + { url = "https://files.pythonhosted.org/packages/0c/22/2ef6a364cd3f565442b0b055e0599744f1e4314ec7326cdaaa48a4d864d7/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:44e0ed786d769d85bc787b0606a53f2d8d2d1d3c8a2608237365e9121c1a338c", size = 7877995, upload-time = "2024-12-13T05:56:18.805Z" }, + { url = "https://files.pythonhosted.org/packages/87/b8/2737456e566e9f4d94ae76b8aa0d953d9acb847714f9a7ad80184474f5be/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:09debb9ce941eb23ecdbe7eab972b1c3e0276dcf01688073faff7b0f61d6c6ca", size = 7769300, upload-time = "2024-12-13T05:56:21.315Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1f/e709c6ec7b5321e6568769baa288c7178e60a93a9da9e682b39450da0e29/matplotlib-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc53cf157a657bfd03afab14774d54ba73aa84d42cfe2480c91bd94873952db", size = 8313423, upload-time = "2024-12-13T05:56:26.719Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b6/5a1f868782cd13f053a679984e222007ecff654a9bfbac6b27a65f4eeb05/matplotlib-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ad45da51be7ad02387801fd154ef74d942f49fe3fcd26a64c94842ba7ec0d865", size = 7854624, upload-time = "2024-12-13T05:56:29.359Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "cycler", marker = "python_full_version >= '3.10'" }, + { name = "fonttools", version = "4.62.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "kiwisolver", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging", marker = "python_full_version >= '3.10'" }, + { name = "pillow", version = "12.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyparsing", marker = "python_full_version >= '3.10'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, + { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, + { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, +] + +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "piezo-processor" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "cbor2" }, + { name = "heartpy" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "scipy", version = "1.13.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "cbor2", specifier = ">=5.9.0" }, + { name = "heartpy", specifier = ">=1.2.7" }, + { name = "numpy", specifier = ">=2.0.2" }, + { name = "scipy", specifier = ">=1.13.1,<1.16" }, +] + +[[package]] +name = "pillow" +version = "11.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554, upload-time = "2025-07-01T09:13:39.342Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548, upload-time = "2025-07-01T09:13:41.835Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742, upload-time = "2025-07-03T13:09:47.439Z" }, + { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087, upload-time = "2025-07-03T13:09:51.796Z" }, + { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350, upload-time = "2025-07-01T09:13:43.865Z" }, + { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840, upload-time = "2025-07-01T09:13:46.161Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005, upload-time = "2025-07-01T09:13:47.829Z" }, + { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372, upload-time = "2025-07-01T09:13:52.145Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090, upload-time = "2025-07-01T09:13:53.915Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988, upload-time = "2025-07-01T09:13:55.699Z" }, + { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899, upload-time = "2025-07-01T09:13:57.497Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8e/9c089f01677d1264ab8648352dcb7773f37da6ad002542760c80107da816/pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f", size = 5316478, upload-time = "2025-07-01T09:15:52.209Z" }, + { url = "https://files.pythonhosted.org/packages/b5/a9/5749930caf674695867eb56a581e78eb5f524b7583ff10b01b6e5048acb3/pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081", size = 4686522, upload-time = "2025-07-01T09:15:54.162Z" }, + { url = "https://files.pythonhosted.org/packages/43/46/0b85b763eb292b691030795f9f6bb6fcaf8948c39413c81696a01c3577f7/pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4", size = 5853376, upload-time = "2025-07-03T13:11:01.066Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/1a230ec0067243cbd60bc2dad5dc3ab46a8a41e21c15f5c9b52b26873069/pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc", size = 7626020, upload-time = "2025-07-03T13:11:06.479Z" }, + { url = "https://files.pythonhosted.org/packages/63/dd/f296c27ffba447bfad76c6a0c44c1ea97a90cb9472b9304c94a732e8dbfb/pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06", size = 5956732, upload-time = "2025-07-01T09:15:56.111Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a0/98a3630f0b57f77bae67716562513d3032ae70414fcaf02750279c389a9e/pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a", size = 6624404, upload-time = "2025-07-01T09:15:58.245Z" }, + { url = "https://files.pythonhosted.org/packages/de/e6/83dfba5646a290edd9a21964da07674409e410579c341fc5b8f7abd81620/pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978", size = 6067760, upload-time = "2025-07-01T09:16:00.003Z" }, + { url = "https://files.pythonhosted.org/packages/bc/41/15ab268fe6ee9a2bc7391e2bbb20a98d3974304ab1a406a992dcb297a370/pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d", size = 6700534, upload-time = "2025-07-01T09:16:02.29Z" }, + { url = "https://files.pythonhosted.org/packages/64/79/6d4f638b288300bed727ff29f2a3cb63db054b33518a95f27724915e3fbc/pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71", size = 6277091, upload-time = "2025-07-01T09:16:04.4Z" }, + { url = "https://files.pythonhosted.org/packages/46/05/4106422f45a05716fd34ed21763f8ec182e8ea00af6e9cb05b93a247361a/pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada", size = 6986091, upload-time = "2025-07-01T09:16:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/63/c6/287fd55c2c12761d0591549d48885187579b7c257bef0c6660755b0b59ae/pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb", size = 2422632, upload-time = "2025-07-01T09:16:08.142Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556, upload-time = "2025-07-01T09:16:09.961Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625, upload-time = "2025-07-01T09:16:11.913Z" }, + { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207, upload-time = "2025-07-03T13:11:10.201Z" }, + { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939, upload-time = "2025-07-03T13:11:15.68Z" }, + { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166, upload-time = "2025-07-01T09:16:13.74Z" }, + { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482, upload-time = "2025-07-01T09:16:16.107Z" }, + { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596, upload-time = "2025-07-01T09:16:18.07Z" }, +] + +[[package]] +name = "pillow" +version = "12.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/21/c2bcdd5906101a30244eaffc1b6e6ce71a31bd0742a01eb89e660ebfac2d/pillow-12.2.0.tar.gz", hash = "sha256:a830b1a40919539d07806aa58e1b114df53ddd43213d9c8b75847eee6c0182b5", size = 46987819, upload-time = "2026-04-01T14:46:17.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/aa/d0b28e1c811cd4d5f5c2bfe2e022292bd255ae5744a3b9ac7d6c8f72dd75/pillow-12.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:a4e8f36e677d3336f35089648c8955c51c6d386a13cf6ee9c189c5f5bd713a9f", size = 5354355, upload-time = "2026-04-01T14:42:15.402Z" }, + { url = "https://files.pythonhosted.org/packages/27/8e/1d5b39b8ae2bd7650d0c7b6abb9602d16043ead9ebbfef4bc4047454da2a/pillow-12.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e589959f10d9824d39b350472b92f0ce3b443c0a3442ebf41c40cb8361c5b97", size = 4695871, upload-time = "2026-04-01T14:42:18.234Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c5/dcb7a6ca6b7d3be41a76958e90018d56c8462166b3ef223150360850c8da/pillow-12.2.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a52edc8bfff4429aaabdf4d9ee0daadbbf8562364f940937b941f87a4290f5ff", size = 6269734, upload-time = "2026-04-01T14:42:20.608Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/aa1bb13b2f4eba914e9637893c73f2af8e48d7d4023b9d3750d4c5eb2d0c/pillow-12.2.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:975385f4776fafde056abb318f612ef6285b10a1f12b8570f3647ad0d74b48ec", size = 8076080, upload-time = "2026-04-01T14:42:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/a1/2a/8c79d6a53169937784604a8ae8d77e45888c41537f7f6f65ed1f407fe66d/pillow-12.2.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd9c0c7a0c681a347b3194c500cb1e6ca9cab053ea4d82a5cf45b6b754560136", size = 6382236, upload-time = "2026-04-01T14:42:25.82Z" }, + { url = "https://files.pythonhosted.org/packages/b5/42/bbcb6051030e1e421d103ce7a8ecadf837aa2f39b8f82ef1a8d37c3d4ebc/pillow-12.2.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88d387ff40b3ff7c274947ed3125dedf5262ec6919d83946753b5f3d7c67ea4c", size = 7070220, upload-time = "2026-04-01T14:42:28.68Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e1/c2a7d6dd8cfa6b231227da096fd2d58754bab3603b9d73bf609d3c18b64f/pillow-12.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:51c4167c34b0d8ba05b547a3bb23578d0ba17b80a5593f93bd8ecb123dd336a3", size = 6493124, upload-time = "2026-04-01T14:42:31.579Z" }, + { url = "https://files.pythonhosted.org/packages/5f/41/7c8617da5d32e1d2f026e509484fdb6f3ad7efaef1749a0c1928adbb099e/pillow-12.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:34c0d99ecccea270c04882cb3b86e7b57296079c9a4aff88cb3b33563d95afaa", size = 7194324, upload-time = "2026-04-01T14:42:34.615Z" }, + { url = "https://files.pythonhosted.org/packages/2d/de/a777627e19fd6d62f84070ee1521adde5eeda4855b5cf60fe0b149118bca/pillow-12.2.0-cp310-cp310-win32.whl", hash = "sha256:b85f66ae9eb53e860a873b858b789217ba505e5e405a24b85c0464822fe88032", size = 6376363, upload-time = "2026-04-01T14:42:37.19Z" }, + { url = "https://files.pythonhosted.org/packages/e7/34/fc4cb5204896465842767b96d250c08410f01f2f28afc43b257de842eed5/pillow-12.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:673aa32138f3e7531ccdbca7b3901dba9b70940a19ccecc6a37c77d5fdeb05b5", size = 7083523, upload-time = "2026-04-01T14:42:39.62Z" }, + { url = "https://files.pythonhosted.org/packages/2d/a0/32852d36bc7709f14dc3f64f929a275e958ad8c19a6deba9610d458e28b3/pillow-12.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:3e080565d8d7c671db5802eedfb438e5565ffa40115216eabb8cd52d0ecce024", size = 2463318, upload-time = "2026-04-01T14:42:42.063Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "scipy" +version = "1.13.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ae/00/48c2f661e2816ccf2ecd77982f6605b2950afe60f60a52b4cbbc2504aa8f/scipy-1.13.1.tar.gz", hash = "sha256:095a87a0312b08dfd6a6155cbbd310a8c51800fc931b8c0b84003014b874ed3c", size = 57210720, upload-time = "2024-05-23T03:29:26.079Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/33/59/41b2529908c002ade869623b87eecff3e11e3ce62e996d0bdcb536984187/scipy-1.13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:20335853b85e9a49ff7572ab453794298bcf0354d8068c5f6775a0eabf350aca", size = 39328076, upload-time = "2024-05-23T03:19:01.687Z" }, + { url = "https://files.pythonhosted.org/packages/d5/33/f1307601f492f764062ce7dd471a14750f3360e33cd0f8c614dae208492c/scipy-1.13.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d605e9c23906d1994f55ace80e0125c587f96c020037ea6aa98d01b4bd2e222f", size = 30306232, upload-time = "2024-05-23T03:19:09.089Z" }, + { url = "https://files.pythonhosted.org/packages/c0/66/9cd4f501dd5ea03e4a4572ecd874936d0da296bd04d1c45ae1a4a75d9c3a/scipy-1.13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfa31f1def5c819b19ecc3a8b52d28ffdcc7ed52bb20c9a7589669dd3c250989", size = 33743202, upload-time = "2024-05-23T03:19:15.138Z" }, + { url = "https://files.pythonhosted.org/packages/a3/ba/7255e5dc82a65adbe83771c72f384d99c43063648456796436c9a5585ec3/scipy-1.13.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26264b282b9da0952a024ae34710c2aff7d27480ee91a2e82b7b7073c24722f", size = 38577335, upload-time = "2024-05-23T03:19:21.984Z" }, + { url = "https://files.pythonhosted.org/packages/49/a5/bb9ded8326e9f0cdfdc412eeda1054b914dfea952bda2097d174f8832cc0/scipy-1.13.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eccfa1906eacc02de42d70ef4aecea45415f5be17e72b61bafcfd329bdc52e94", size = 38820728, upload-time = "2024-05-23T03:19:28.225Z" }, + { url = "https://files.pythonhosted.org/packages/12/30/df7a8fcc08f9b4a83f5f27cfaaa7d43f9a2d2ad0b6562cced433e5b04e31/scipy-1.13.1-cp310-cp310-win_amd64.whl", hash = "sha256:2831f0dc9c5ea9edd6e51e6e769b655f08ec6db6e2e10f86ef39bd32eb11da54", size = 46210588, upload-time = "2024-05-23T03:19:35.661Z" }, + { url = "https://files.pythonhosted.org/packages/7f/29/c2ea58c9731b9ecb30b6738113a95d147e83922986b34c685b8f6eefde21/scipy-1.13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:436bbb42a94a8aeef855d755ce5a465479c721e9d684de76bf61a62e7c2b81d5", size = 39352927, upload-time = "2024-05-23T03:21:01.95Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c0/e71b94b20ccf9effb38d7147c0064c08c622309fd487b1b677771a97d18c/scipy-1.13.1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:8335549ebbca860c52bf3d02f80784e91a004b71b059e3eea9678ba994796a24", size = 30324538, upload-time = "2024-05-23T03:21:07.634Z" }, + { url = "https://files.pythonhosted.org/packages/6d/0f/aaa55b06d474817cea311e7b10aab2ea1fd5d43bc6a2861ccc9caec9f418/scipy-1.13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d533654b7d221a6a97304ab63c41c96473ff04459e404b83275b60aa8f4b7004", size = 33732190, upload-time = "2024-05-23T03:21:14.41Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/d0ad1a96f80962ba65e2ce1de6a1e59edecd1f0a7b55990ed208848012e0/scipy-1.13.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e98dcf185ba7f8e663e122ebf908c4702420477ae52a04f9908707456ba4d", size = 38612244, upload-time = "2024-05-23T03:21:21.827Z" }, + { url = "https://files.pythonhosted.org/packages/8d/02/1165905f14962174e6569076bcc3315809ae1291ed14de6448cc151eedfd/scipy-1.13.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a014c2b3697bde71724244f63de2476925596c24285c7a637364761f8710891c", size = 38845637, upload-time = "2024-05-23T03:21:28.729Z" }, + { url = "https://files.pythonhosted.org/packages/3e/77/dab54fe647a08ee4253963bcd8f9cf17509c8ca64d6335141422fe2e2114/scipy-1.13.1-cp39-cp39-win_amd64.whl", hash = "sha256:392e4ec766654852c25ebad4f64e4e584cf19820b980bc04960bca0b0cd6eaa2", size = 46227440, upload-time = "2024-05-23T03:21:35.888Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +] diff --git a/modules/sleep-detector/pyproject.toml b/modules/sleep-detector/pyproject.toml new file mode 100644 index 00000000..e9d2ad65 --- /dev/null +++ b/modules/sleep-detector/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = "sleep-detector" +version = "0.1.0" +requires-python = ">=3.9,<3.11" +dependencies = [ + "cbor2>=5.9.0", + "numpy>=2.0.2", +] diff --git a/modules/sleep-detector/requirements.txt b/modules/sleep-detector/requirements.txt deleted file mode 100644 index 8e99b295..00000000 --- a/modules/sleep-detector/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -cbor2==5.8.0 -numpy==2.2.6 diff --git a/modules/sleep-detector/sleepypod-sleep-detector.service b/modules/sleep-detector/sleepypod-sleep-detector.service index 2ea0e2eb..3941983e 100644 --- a/modules/sleep-detector/sleepypod-sleep-detector.service +++ b/modules/sleep-detector/sleepypod-sleep-detector.service @@ -9,7 +9,7 @@ Type=simple User=root UMask=0002 WorkingDirectory=/opt/sleepypod/modules/sleep-detector -ExecStart=/opt/sleepypod/modules/sleep-detector/venv/bin/python main.py +ExecStart=/opt/sleepypod/modules/sleep-detector/.venv/bin/python main.py Restart=always RestartSec=10 diff --git a/modules/sleep-detector/uv.lock b/modules/sleep-detector/uv.lock new file mode 100644 index 00000000..a2d35a3a --- /dev/null +++ b/modules/sleep-detector/uv.lock @@ -0,0 +1,106 @@ +version = 1 +revision = 3 +requires-python = ">=3.9, <3.11" +resolution-markers = [ + "python_full_version >= '3.10'", + "python_full_version < '3.10'", +] + +[[package]] +name = "cbor2" +version = "5.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bd/cb/09939728be094d155b5d4ac262e39877875f5f7e36eea66beb359f647bd0/cbor2-5.9.0.tar.gz", hash = "sha256:85c7a46279ac8f226e1059275221e6b3d0e370d2bb6bd0500f9780781615bcea", size = 111231, upload-time = "2026-03-22T15:56:50.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/bf/12b337e5354e47f6378da18989480c0c1e2cc5fe9b865e6fab45d6332aa6/cbor2-5.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:55bea0dd9a7d354e35f4e5fe58ceab393e76962713749dc3a0a64a0e5d19545e", size = 70577, upload-time = "2026-03-22T15:55:54.174Z" }, + { url = "https://files.pythonhosted.org/packages/45/7b/74c524ce81c1ddc6c44b4865028ffb7d3a8e7ae653b1061650375a28cbb1/cbor2-5.9.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3095dc49e75572841a9534cbfdabc2a17487ea4ee33341436abc4a7ac7245a3a", size = 261074, upload-time = "2026-03-22T15:55:55.654Z" }, + { url = "https://files.pythonhosted.org/packages/4b/97/c496c71422b2ca18ff2acc5f49e8c45cd7294b7df1359eccec78021b428e/cbor2-5.9.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25bec7beb2089465382b1be72e78667fe9090598800826559c3e3008cf0db743", size = 255498, upload-time = "2026-03-22T15:55:57.256Z" }, + { url = "https://files.pythonhosted.org/packages/c2/40/9bd7e66dba7aea674a440e004faea406de42c49aeac23453954b67768532/cbor2-5.9.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cc5efec69055c3c470997935d95762be7e4bfd1248d88fb1a33bb7e0f45712e9", size = 255683, upload-time = "2026-03-22T15:55:58.721Z" }, + { url = "https://files.pythonhosted.org/packages/b3/d1/fa3e158dbe4c08091495b720c604624b285bc272afdbcfac2150725d955b/cbor2-5.9.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:420d2490c7836c81151b4bd591c35cffc55391e33e7e333c50fda391bcea7d31", size = 250798, upload-time = "2026-03-22T15:55:59.953Z" }, + { url = "https://files.pythonhosted.org/packages/7c/16/3186084b441c4b0caebfaaa2c66a57bde82ceaacd469570c77c8030b6b95/cbor2-5.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:d1a21c006760f95acd9509cc5a7d15d6fc82e58f721f94fa9039b4e77189a6e5", size = 69436, upload-time = "2026-03-22T15:56:01.466Z" }, + { url = "https://files.pythonhosted.org/packages/36/1f/57d00cd13e0f9391bcd616795aa78409210a7464570fe21e3b9cd42eb5a7/cbor2-5.9.0-cp310-cp310-win_arm64.whl", hash = "sha256:08388ea54195738602b4c4999966bcaef6f0b17d293c9658658409d9fff96f57", size = 65312, upload-time = "2026-03-22T15:56:02.804Z" }, + { url = "https://files.pythonhosted.org/packages/a0/17/f052f558e29f90ed29f9a42263232f6f059fd7bbf1b5d27e3867f9356375/cbor2-5.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1da96ce5d852fe3d342c1eb2c202a52d1c97edfddc9230f1be7e02674662bf26", size = 70582, upload-time = "2026-03-22T15:56:40.441Z" }, + { url = "https://files.pythonhosted.org/packages/41/fb/91fa1b15b525577ec20a8904f22d8e97eb81164f6663705539e89acdde8f/cbor2-5.9.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:65f8eac3268c608533f326f0fd9010ab1b2a8a917b05edaf3853116336821669", size = 260147, upload-time = "2026-03-22T15:56:41.932Z" }, + { url = "https://files.pythonhosted.org/packages/15/12/4013441f8fccca0c5bff043c6ff8b86fde03c5da8b41a22694d49af02b3e/cbor2-5.9.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f797532d13469f2193e5c16e827d8df7a8c33674b19be755790b54ab231e6a73", size = 254507, upload-time = "2026-03-22T15:56:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/d7/26/b96e357ca8554a5458939bc9a7a6f83a494ce15470c96d02f79188fa81c7/cbor2-5.9.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fbdcf4d74acbeb7672e6413e81cd2c1ced1a4a8cf949484ac54e9af5265c3c72", size = 254592, upload-time = "2026-03-22T15:56:44.402Z" }, + { url = "https://files.pythonhosted.org/packages/1d/06/db0fcac41763153e20aca5a096ee3727bfc591d7825daa5bad493bb99b6d/cbor2-5.9.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:53cfa49e0df9c639beb871d480de098eedc81eb63ff29f2dc922720d7577b676", size = 249671, upload-time = "2026-03-22T15:56:45.581Z" }, + { url = "https://files.pythonhosted.org/packages/25/9e/484d2b68f2e720add45e3d0d61976fe2abd86dea31ec13038fb630097ef1/cbor2-5.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:f29e5c3abcc91c1aeefecde0e057bf33f1655588d3065c6560c30ceb3be6f333", size = 69511, upload-time = "2026-03-22T15:56:46.685Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cd/b6653d28c6ebb42402c2d22044108ee59ff4d064eae435ce20894a4847ec/cbor2-5.9.0-cp39-cp39-win_arm64.whl", hash = "sha256:d8524a8c142c3cc228e635f8a97499a6c0b18ca91382e8276565658035cdcb6d", size = 65375, upload-time = "2026-03-22T15:56:47.73Z" }, + { url = "https://files.pythonhosted.org/packages/42/ff/b83492b096fbef26e9cb62c1a4bf2d3cef579ea7b33138c6c37c4ae66f67/cbor2-5.9.0-py3-none-any.whl", hash = "sha256:27695cbd70c90b8de5c4a284642c2836449b14e2c2e07e3ffe0744cb7669a01b", size = 24627, upload-time = "2026-03-22T15:56:48.847Z" }, +] + +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "sleep-detector" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "cbor2" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "cbor2", specifier = ">=5.9.0" }, + { name = "numpy", specifier = ">=2.0.2" }, +] diff --git a/next.config.mjs b/next.config.mjs index 7a51ea69..d25e715f 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -2,17 +2,32 @@ const nextConfig = { // Source maps for debugging production crashes productionBrowserSourceMaps: true, + // Standalone output for cross-machine deploys (build on macOS, run on pod). + // Turbopack bakes RELATIVE_ROOT_PATH at build time β€” without standalone, + // the .next bundle only works on the machine that built it. + output: 'standalone', // Keep native modules external β€” resolved from node_modules at runtime // so the correct platform binary (linux-arm64 on pod) is used serverExternalPackages: ['better-sqlite3'], - // Keep Turbopack config for .po files (i18n) turbopack: { + // Lingui .po file loader (used in dev mode where Turbopack is active) rules: { '*.po': { loaders: ['@lingui/loader'], as: '*.js', }, }, + resolveAlias: { + 'better-sqlite3': 'better-sqlite3', + }, + }, + webpack: (config) => { + // Lingui .po file loader for webpack builds + config.module.rules.push({ + test: /\.po$/, + use: ['@lingui/loader'], + }) + return config }, reactCompiler: false, } diff --git a/package.json b/package.json index 54745a5b..6e6235e9 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "scripts": { "prebuild": "node scripts/generate-git-info.mjs && lingui compile", - "build": "next build", + "build": "next build && cp -r .next/static .next/standalone/.next/static && [ -d public ] && cp -r public .next/standalone/public || true", "dev": "next dev", "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", @@ -24,7 +24,13 @@ "lint": "eslint .", "start": "next start", "test": "vitest", - "tsc": "tsc --noEmit" + "tsc": "tsc --noEmit", + "prepare": "git rev-parse --git-dir >/dev/null 2>&1 && git config core.hooksPath .githooks || true" + }, + "lint-staged": { + "*.{ts,tsx}": "eslint --fix", + "*.css": "eslint --fix", + "*.json": "eslint --fix" }, "dependencies": { "@ajayche/trpc-panel": "^2.0.4", @@ -47,14 +53,14 @@ "clsx": "^2.1.1", "dotenv": "^17.3.1", "drizzle-orm": "^0.45.1", - "lucide-react": "^0.577.0", + "lucide-react": "^1.0.0", "negotiator": "^1.0.0", "next": "^16.1.6", "node-schedule": "^2.1.1", "react": "^19.2.4", "react-dom": "^19.2.4", "recharts": "^3.8.0", - "shadcn": "^3.8.5", + "shadcn": "^4.0.0", "superjson": "^2.2.6", "tailwind-merge": "^3.5.0", "trpc-to-openapi": "^3.1.0", @@ -90,7 +96,7 @@ "@types/react-dom": "^19.2.3", "@types/ws": "^8.18.1", "@vitejs/plugin-react": "^5.1.4", - "@vitest/coverage-v8": "4.1.2", + "@vitest/coverage-v8": "4.1.4", "conventional-changelog-conventionalcommits": "^9.1.0", "drizzle-kit": "^0.31.9", "eslint": "^9.39.3", @@ -98,7 +104,8 @@ "eslint-plugin-react": "^7.37.5", "globals": "^17.0.0", "jiti": "^2.6.1", - "jsdom": "^28.0.0", + "jsdom": "^29.0.0", + "lint-staged": "^16.4.0", "semantic-release": "^25.0.3", "tailwindcss": "4.2.2", "tsx": "^4.21.0", @@ -109,6 +116,13 @@ }, "packageManager": "pnpm@10.33.0", "pnpm": { - "onlyBuiltDependencies": ["cbor-extract", "better-sqlite3", "esbuild", "sharp", "msw", "unrs-resolver"] + "onlyBuiltDependencies": [ + "cbor-extract", + "better-sqlite3", + "esbuild", + "sharp", + "msw", + "unrs-resolver" + ] } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d998ef09..623ab586 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,43 +10,43 @@ importers: dependencies: '@ajayche/trpc-panel': specifier: ^2.0.4 - version: 2.0.4(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@trpc/server@11.10.0(typescript@5.9.3))(@types/react@19.2.14)(immer@11.1.4)(monaco-editor@0.55.1)(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6) + version: 2.0.4(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@trpc/server@11.16.0(typescript@5.9.3))(@types/react@19.2.14)(immer@11.1.4)(monaco-editor@0.55.1)(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6) '@base-ui/react': specifier: ^1.2.0 - version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 1.3.0(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@formatjs/intl-localematcher': specifier: ^0.8.0 version: 0.8.2 '@lingui/core': specifier: ^5.9.2 - version: 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) + version: 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) '@lingui/macro': specifier: ^5.9.2 - version: 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.4) + version: 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.5) '@lingui/react': specifier: ^5.9.2 - version: 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.4) + version: 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.5) '@tanstack/react-query': specifier: ^5.90.21 - version: 5.95.2(react@19.2.4) + version: 5.97.0(react@19.2.5) '@trpc/client': specifier: ^11.10.0 - version: 11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3) + version: 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3) '@trpc/next': specifier: ^11.10.0 - version: 11.10.0(@tanstack/react-query@5.95.2(react@19.2.4))(@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.10.0(@tanstack/react-query@5.95.2(react@19.2.4))(@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.10.0(typescript@5.9.3))(react@19.2.4)(typescript@5.9.3))(@trpc/server@11.10.0(typescript@5.9.3))(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3) + version: 11.16.0(@tanstack/react-query@5.97.0(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.16.0(@tanstack/react-query@5.97.0(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3) '@trpc/react-query': specifier: ^11.10.0 - version: 11.10.0(@tanstack/react-query@5.95.2(react@19.2.4))(@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.10.0(typescript@5.9.3))(react@19.2.4)(typescript@5.9.3) + version: 11.16.0(@tanstack/react-query@5.97.0(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3) '@trpc/server': specifier: ^11.10.0 - version: 11.10.0(typescript@5.9.3) + version: 11.16.0(typescript@5.9.3) '@xyflow/react': specifier: ^12.10.1 - version: 12.10.2(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 12.10.2(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) better-sqlite3: specifier: ^12.6.2 - version: 12.6.2 + version: 12.8.0 binary-split: specifier: ^1.0.5 version: 1.0.5 @@ -64,34 +64,34 @@ importers: version: 2.1.1 dotenv: specifier: ^17.3.1 - version: 17.3.1 + version: 17.4.1 drizzle-orm: specifier: ^0.45.1 - version: 0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.6.2) + version: 0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0) lucide-react: - specifier: ^0.577.0 - version: 0.577.0(react@19.2.4) + specifier: ^1.0.0 + version: 1.7.0(react@19.2.5) negotiator: specifier: ^1.0.0 version: 1.0.0 next: specifier: ^16.1.6 - version: 16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) node-schedule: specifier: ^2.1.1 version: 2.1.1 react: specifier: ^19.2.4 - version: 19.2.4 + version: 19.2.5 react-dom: specifier: ^19.2.4 - version: 19.2.4(react@19.2.4) + version: 19.2.5(react@19.2.5) recharts: specifier: ^3.8.0 - version: 3.8.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react-is@19.2.4)(react@19.2.4)(redux@5.0.1) + version: 3.8.1(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react-is@19.2.4)(react@19.2.5)(redux@5.0.1) shadcn: - specifier: ^3.8.5 - version: 3.8.5(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(typescript@5.9.3) + specifier: ^4.0.0 + version: 4.2.0(@types/node@25.5.2)(babel-plugin-macros@3.1.0)(typescript@5.9.3) superjson: specifier: ^2.2.6 version: 2.2.6 @@ -100,13 +100,13 @@ importers: version: 3.5.0 trpc-to-openapi: specifier: ^3.1.0 - version: 3.1.0(@trpc/server@11.10.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6) + version: 3.2.0(@trpc/server@11.16.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6) tw-animate-css: specifier: ^1.4.0 version: 1.4.0 ws: specifier: ^8.19.0 - version: 8.19.0 + version: 8.20.0 zod: specifier: ^4.3.6 version: 4.3.6 @@ -119,7 +119,7 @@ importers: version: 7.28.5(@babel/core@7.29.0) '@eslint/css': specifier: ^1.0.0 - version: 1.0.0 + version: 1.1.0 '@eslint/js': specifier: ^9.39.3 version: 9.39.4 @@ -128,19 +128,19 @@ importers: version: 1.2.0 '@lingui/babel-plugin-lingui-macro': specifier: ^5.9.2 - version: 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) + version: 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) '@lingui/cli': specifier: ^5.9.2 - version: 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) + version: 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) '@lingui/loader': specifier: ^5.9.2 - version: 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3)(webpack@5.104.1) + version: 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3)(webpack@5.104.1) '@lingui/swc-plugin': specifier: ^5.11.0 - version: 5.11.0(@lingui/core@5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0))(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + version: 5.11.0(@lingui/core@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0))(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)) '@lingui/vite-plugin': specifier: ^5.9.2 - version: 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@semantic-release/commit-analyzer': specifier: ^13.0.1 version: 13.0.1(semantic-release@25.0.3(typescript@5.9.3)) @@ -158,7 +158,7 @@ importers: version: 10.4.1 '@testing-library/react': specifier: ^16.3.2 - version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@tsconfig/next': specifier: ^2.0.5 version: 2.0.6 @@ -176,7 +176,7 @@ importers: version: 0.6.4 '@types/node': specifier: ^25.3.0 - version: 25.5.0 + version: 25.5.2 '@types/node-schedule': specifier: ^2.1.8 version: 2.1.8 @@ -191,13 +191,13 @@ importers: version: 8.18.1 '@vitejs/plugin-react': specifier: ^5.1.4 - version: 5.1.4(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 5.2.0(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) '@vitest/coverage-v8': - specifier: 4.1.2 - version: 4.1.2(vitest@4.1.2(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))) + specifier: 4.1.4 + version: 4.1.4(vitest@4.1.4) conventional-changelog-conventionalcommits: specifier: ^9.1.0 - version: 9.1.0 + version: 9.3.1 drizzle-kit: specifier: ^0.31.9 version: 0.31.10 @@ -206,7 +206,7 @@ importers: version: 9.39.4(jiti@2.6.1) eslint-config-next: specifier: ^16.1.6 - version: 16.1.6(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + version: 16.2.3(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-react: specifier: ^7.37.5 version: 7.37.5(eslint@9.39.4(jiti@2.6.1)) @@ -217,8 +217,11 @@ importers: specifier: ^2.6.1 version: 2.6.1 jsdom: - specifier: ^28.0.0 - version: 28.1.0(@noble/hashes@1.8.0) + specifier: ^29.0.0 + version: 29.0.2(@noble/hashes@1.8.0) + lint-staged: + specifier: ^16.4.0 + version: 16.4.0 semantic-release: specifier: ^25.0.3 version: 25.0.3(typescript@5.9.3) @@ -233,19 +236,16 @@ importers: version: 5.9.3 typescript-eslint: specifier: ^8.56.1 - version: 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + version: 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) vite-tsconfig-paths: specifier: ^6.1.1 - version: 6.1.1(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 6.1.1(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) vitest: specifier: ^4.0.18 - version: 4.1.2(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 4.1.4(@types/node@25.5.2)(@vitest/coverage-v8@4.1.4)(jsdom@29.0.2(@noble/hashes@1.8.0))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) packages: - '@acemir/cssom@0.9.31': - resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} - '@actions/core@2.0.1': resolution: {integrity: sha512-oBfqT3GwkvLlo1fjvhQLQxuwZCGTarTE5OuZ2Wg10hvhBj7LRIlF611WT4aZS6fDhO5ZKlY7lCAZTlpmyaHaeg==} @@ -268,16 +268,13 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@antfu/ni@25.0.0': - resolution: {integrity: sha512-9q/yCljni37pkMr4sPrI3G4jqdIk074+iukc5aFJl7kmDCCsiJrbZ6zKxnES1Gwg+i9RcDZwvktl23puGslmvA==} - hasBin: true - - '@asamuzakjp/css-color@5.0.1': - resolution: {integrity: sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==} + '@asamuzakjp/css-color@5.1.9': + resolution: {integrity: sha512-zd9c/Wdso6v1U7v6w3i/hbAr4K7NaSHImdpvmLt+Y9ea5BhilnIGNkfhOJ7FEIuPipAnE9tZeDOll05WDT0kgg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@asamuzakjp/dom-selector@6.8.1': - resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + '@asamuzakjp/dom-selector@7.0.9': + resolution: {integrity: sha512-r3ElRr7y8ucyN2KdICwGsmj19RoN13CLCa/pvGydghWK6ZzeKQ+TcDjVdtEZz2ElpndM5jXw//B9CEee0mWnVg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} @@ -290,18 +287,10 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.5': - resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} - engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} @@ -314,10 +303,6 @@ packages: resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} - engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} @@ -328,6 +313,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@7.28.6': + resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} @@ -364,12 +355,22 @@ packages: resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + '@babel/helper-replace-supers@7.27.1': resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} @@ -386,19 +387,10 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} - engines: {node: '>=6.9.0'} - '@babel/helpers@7.29.2': resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.5': - resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.29.2': resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} @@ -416,6 +408,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-modules-commonjs@7.27.1': resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} engines: {node: '>=6.9.0'} @@ -440,6 +438,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + '@babel/preset-typescript@7.28.5': resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} engines: {node: '>=6.9.0'} @@ -454,18 +458,10 @@ packages: resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} - engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} - engines: {node: '>=6.9.0'} - '@babel/traverse@7.29.0': resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} @@ -561,34 +557,39 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.28': - resolution: {integrity: sha512-1NRf1CUBjnr3K7hu8BLxjQrKCxEe8FP/xmPTenAxCRZWVLbmGotkFvG9mfNpjA6k7Bw1bw4BilZq9cu19RA5pg==} + '@csstools/css-syntax-patches-for-csstree@1.1.2': + resolution: {integrity: sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true '@csstools/css-tokenizer@4.0.0': resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} engines: {node: '>=20.19.0'} - '@dotenvx/dotenvx@1.51.2': - resolution: {integrity: sha512-+693mNflujDZxudSEqSNGpn92QgFhJlBn9q2mDQ9yGWyHuz3hZ8B5g3EXCwdAz4DMJAI+OFCIbfEFZS+YRdrEA==} + '@dotenvx/dotenvx@1.61.0': + resolution: {integrity: sha512-utL3cpZoFzflyqUkjYbxYujI6STBTmO5LFn4bbin/NZnRWN6wQ7eErhr3/Vpa5h/jicPFC6kTa42r940mQftJQ==} hasBin: true '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} - '@ecies/ciphers@0.2.5': - resolution: {integrity: sha512-GalEZH4JgOMHYYcYmVqnFirFsjZHeoGMDt9IxEnM9F7GRUUyUksJ7Ou53L83WHJq3RWKD3AcBpo0iQh0oMpf8A==} - engines: {bun: '>=1', deno: '>=2', node: '>=16'} + '@ecies/ciphers@0.2.6': + resolution: {integrity: sha512-patgsRPKGkhhoBjETV4XxD0En4ui5fbX0hzayqI3M8tvNMGUoUvmyYAIWwlxBc1KX5cturfqByYdj5bYGRpN9g==} + engines: {bun: '>=1', deno: '>=2.7.10', node: '>=16'} peerDependencies: '@noble/ciphers': ^1.0.0 - '@emnapi/core@1.7.1': - resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + '@emnapi/core@1.9.2': + resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + '@emnapi/runtime@1.9.2': + resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} - '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -664,8 +665,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.4': - resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -688,8 +689,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.4': - resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==} + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -712,8 +713,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.4': - resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==} + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -736,8 +737,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.4': - resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==} + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -760,8 +761,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.4': - resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==} + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -784,8 +785,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.4': - resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==} + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -808,8 +809,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.4': - resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==} + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -832,8 +833,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.4': - resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==} + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -856,8 +857,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.4': - resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==} + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -880,8 +881,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.4': - resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==} + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -904,8 +905,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.4': - resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==} + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -928,8 +929,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.4': - resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==} + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -952,8 +953,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.4': - resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==} + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -976,8 +977,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.4': - resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==} + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -1000,8 +1001,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.4': - resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==} + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -1024,8 +1025,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.4': - resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==} + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -1048,8 +1049,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.4': - resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==} + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -1066,8 +1067,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.4': - resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==} + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -1090,8 +1091,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.4': - resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==} + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -1108,8 +1109,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.4': - resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -1132,8 +1133,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.4': - resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==} + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -1150,8 +1151,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.4': - resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==} + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -1174,8 +1175,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.4': - resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==} + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -1198,8 +1199,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.4': - resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==} + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -1222,8 +1223,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.4': - resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==} + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -1246,8 +1247,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.4': - resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==} + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1282,8 +1283,8 @@ packages: resolution: {integrity: sha512-3D5/OHibNEGk+wKwNwMbz63NMf367EoR4mVNNpxddCHKEb2Nez7z62J2U6YjtErSsZDoY0CsccmoUpdEbkogNA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - '@eslint/css@1.0.0': - resolution: {integrity: sha512-2MjyL517F1wrLrmmfaFntKKiImudiXW6Dd80oE7VRIRu64bT5YieRs+WPgdZy9m8izs/tSUqi3j2mBnjUrqJfA==} + '@eslint/css@1.1.0': + resolution: {integrity: sha512-sNwfLcU3nKXv/v2YglqujwMU4Iv3BDhxldNUd/2FckVab0zdvc9pPlKWxjR6Ap/EU+Y8Pdu853iwvcUpemRhRw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/eslintrc@3.3.5': @@ -1310,8 +1311,8 @@ packages: resolution: {integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - '@exodus/bytes@1.14.1': - resolution: {integrity: sha512-OhkBFWI6GcRMUroChZiopRiSp2iAMvEBK47NhJooDqz1RERO4QuZIZnjP63TXX8GAiLABkYmX+fuQsdJ1dd2QQ==} + '@exodus/bytes@1.15.0': + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: '@noble/hashes': ^1.8.0 || ^2.0.0 @@ -1347,8 +1348,8 @@ packages: '@hapi/bourne@3.0.0': resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==} - '@hono/node-server@1.19.9': - resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} + '@hono/node-server@1.19.13': + resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -1373,8 +1374,8 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@img/colour@1.0.0': - resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} '@img/sharp-darwin-arm64@0.34.5': @@ -1595,12 +1596,12 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@lingui/babel-plugin-extract-messages@5.9.4': - resolution: {integrity: sha512-sFH5lufIBCOLwjM2hyByMIi7gaGjAPhU7md8XMQYgcEjUVtzjBQvZ9APGDdDQ5BB8xRDyqF2kvaJpJvWZu19zA==} + '@lingui/babel-plugin-extract-messages@5.9.5': + resolution: {integrity: sha512-XOAXMPOkpy45784q5bCNN5PizoAecxkBm8kv8CEusI/f9kR3vMCcpH4kvSchU05JkKAVE8eIsdxb2zM6eDJTeA==} engines: {node: '>=20.0.0'} - '@lingui/babel-plugin-lingui-macro@5.9.4': - resolution: {integrity: sha512-Gj+H48MQWY6rV40TBVG7U91/KETznbXOJpJsf8U4merBRPZgOMCy6VuWZGy1i+YJZJF/LiberlsCCEiiPbBRqg==} + '@lingui/babel-plugin-lingui-macro@5.9.5': + resolution: {integrity: sha512-TDIrOa2hAz8kXrZ0JfMGaIiFIE4TEdqI2he4OpkTSCfBh3ec/gSCn1kNW5HdviO7x46Gvy567YOgHNOI9/e4Fg==} engines: {node: '>=20.0.0'} peerDependencies: babel-plugin-macros: 2 || 3 @@ -1608,20 +1609,20 @@ packages: babel-plugin-macros: optional: true - '@lingui/cli@5.9.4': - resolution: {integrity: sha512-0QAsZCWu6PZhxYmeQfoa6cJbNRRsTkeNQ1jTow/GzBYpFlO9iXw8dCG5cBh5pHHjzjoX3koxiKyUTFyLBmKNiQ==} + '@lingui/cli@5.9.5': + resolution: {integrity: sha512-gonY7U75nzKic8GvEciy1/otQv1WpfwGW5wGMjmBXUMaMnIsycm/wo3t0+2hzqFp+RNfEKZcScoM7aViK3XuLQ==} engines: {node: '>=20.0.0'} hasBin: true - '@lingui/conf@5.9.4': - resolution: {integrity: sha512-crF3AQgYXg52Caz4ffJKSTXWUU/4iOGOBRnSeOkw8lsOtOYlPTaWxeSGyDTEwaGCFl6P/1aew+pvHOSCxOAyrg==} + '@lingui/conf@5.9.5': + resolution: {integrity: sha512-k5r9ssOZirhS5BlqdsK5L0rzlqnHeryoJHAQIpUpeh8g5ymgpbUN7L4+4C4hAX/tddAFiCFN8boHTiu6Wbt83Q==} engines: {node: '>=20.0.0'} - '@lingui/core@5.9.4': - resolution: {integrity: sha512-MsYYc8ue/w1C8bgAbC3h4cNik64bqZ6xGxMjsVdoGQBUe+b/ij+rOEiuJXbwvlo4GXBsvsan7EzeH7sx11IsYQ==} + '@lingui/core@5.9.5': + resolution: {integrity: sha512-Y+iZq9NqnqZOqHNgPomUFP21KH/zs4oTTizWoz0AKAkBbq9T9yb1DSz/ugtBRjF1YLtKMF9tq28v3thMHANSiQ==} engines: {node: '>=20.0.0'} peerDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.4 + '@lingui/babel-plugin-lingui-macro': 5.9.5 babel-plugin-macros: 2 || 3 peerDependenciesMeta: '@lingui/babel-plugin-lingui-macro': @@ -1629,21 +1630,21 @@ packages: babel-plugin-macros: optional: true - '@lingui/format-po@5.9.4': - resolution: {integrity: sha512-B+e8YF6S5EOUPF6i3gaSX69pPs/QkP6MIE97vYA48W9Lty7KFOHuYBk/YzCY9CSQaF7gW3GAI5ZsXX2+ZLVyZw==} + '@lingui/format-po@5.9.5': + resolution: {integrity: sha512-abawxkaEMhAUCqxrnim2NTTeu2gd55X9tkFN8jfRM0B1LE2KjZLWCA8gSD90J/DblDwej8jK8A2BynXlcQdluQ==} engines: {node: '>=20.0.0'} - '@lingui/loader@5.9.4': - resolution: {integrity: sha512-YqWkVfw7YNQ9UnDMFli3J0qGIlnuKWHOzgNJzF3HTl2VOpCSofSZiPk53iAc6V8/Bl3Zsb6HLnmddCEaCJjdgw==} + '@lingui/loader@5.9.5': + resolution: {integrity: sha512-2K0N2aZWxI3pqmXcJPd/8Y/dQeS+nzcf7aQWn63MKC3/97LC6MlaCxJb4z00yOm12gIn2fD4w+HdkvbrUhBpgw==} engines: {node: '>=20.0.0'} peerDependencies: webpack: ^5.0.0 - '@lingui/macro@5.9.4': - resolution: {integrity: sha512-p1/uPc8sQTMLdv0EJqjaFUvuFKBiwNVThdJp80GX7FayPzF570EO6wsS8U81g1p8NoCS/UY6cglK9YJeNVwKLw==} + '@lingui/macro@5.9.5': + resolution: {integrity: sha512-WZsF93jKwk0IW4xmvAGd+6S3dT+9ZzhXAasKIiJL9qB4viO9oj+oecGhXuPYTYV74mcoL7L1494hca0CsB/BLQ==} engines: {node: '>=20.0.0'} peerDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.4 + '@lingui/babel-plugin-lingui-macro': 5.9.5 babel-plugin-macros: 2 || 3 peerDependenciesMeta: '@lingui/babel-plugin-lingui-macro': @@ -1651,15 +1652,15 @@ packages: babel-plugin-macros: optional: true - '@lingui/message-utils@5.9.4': - resolution: {integrity: sha512-YzAVzILdsqdUqwHmryl7rfwZXRHYs6QY2wPLH5gxrV7wlieiCaskaKPeSk2SuN/gmC8im1GDrQHcwgKapFU3Sg==} + '@lingui/message-utils@5.9.5': + resolution: {integrity: sha512-t3dNbjb1dWkvcpXGMXIEyBDO3l4B8J2ColZXi0NTG1ioAj+sDfFxFB8fepVgd3JAk+AwARlOLvF14oS0mAdgpw==} engines: {node: '>=20.0.0'} - '@lingui/react@5.9.4': - resolution: {integrity: sha512-ev/PvJd0WNy6OqeyghQV1QCGAFYku5xHyaattN2kg0wy6RPVfGsCaM8treRUK9TLiETra79GLVY8sTjfeH/M5Q==} + '@lingui/react@5.9.5': + resolution: {integrity: sha512-jzYoA/f4jrTfpOB+jrMhlC835UwqSXJdepr7cfWsmg+Rpp3HBSREtfrogaz1LqLI/AVnkmfp10Mo6VOp/8qeOQ==} engines: {node: '>=20.0.0'} peerDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.4 + '@lingui/babel-plugin-lingui-macro': 5.9.5 babel-plugin-macros: 2 || 3 react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: @@ -1680,8 +1681,8 @@ packages: next: optional: true - '@lingui/vite-plugin@5.9.4': - resolution: {integrity: sha512-qc5AFEOJjD3AIxDhCx9RZ3ELGnn0r/l3NQb4BZifz7wnYUYfbk1B2kNHCA8OMqb/sw3q36uWr3S4vORlmxPUkQ==} + '@lingui/vite-plugin@5.9.5': + resolution: {integrity: sha512-oFlEkr4/56yWZYVJ5xfYfJUTgegSKkbQUo/t/MJVFcyxiMvtGsyzrZdm7grwbY9v8JbVyW5BdcIGN6/Z8wvEtw==} engines: {node: '>=20.0.0'} peerDependencies: vite: ^3 || ^4 || ^5.0.9 || ^6 || ^7 || ^8 @@ -1689,8 +1690,8 @@ packages: '@messageformat/parser@5.1.1': resolution: {integrity: sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg==} - '@modelcontextprotocol/sdk@1.27.0': - resolution: {integrity: sha512-qOdO524oPMkUsOJTrsH9vz/HN3B5pKyW+9zIW51A9kDMVe7ON70drz1ouoyoyOcfzc+oxhkQ6jWmbyKnlWmYqA==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -1709,8 +1710,8 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@mswjs/interceptors@0.40.0': - resolution: {integrity: sha512-EFd6cVbHsgLa6wa4RljGj6Wk75qoHxUSyc5asLyyPSyuhIcdS2Q3Phw6ImS1q+CkALthJRShiYfKANcQMuMqsQ==} + '@mswjs/interceptors@0.41.3': + resolution: {integrity: sha512-cXu86tF4VQVfwz8W1SPbhoRyHJkti6mjH/XJIxp40jhO4j2k1m4KYrEykxqWPkFF3vrK4rgQppBh//AwyGSXPA==} engines: {node: '>=18'} '@mui/core-downloads-tracker@5.18.0': @@ -1793,60 +1794,60 @@ packages: '@napi-rs/wasm-runtime@0.2.12': resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} - '@next/env@16.1.6': - resolution: {integrity: sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==} + '@next/env@16.2.3': + resolution: {integrity: sha512-ZWXyj4uNu4GCWQw9cjRxWlbD+33mcDszIo9iQxFnBX3Wmgq9ulaSJcl6VhuWx5pCWqqD+9W6Wfz7N0lM5lYPMA==} - '@next/eslint-plugin-next@16.1.6': - resolution: {integrity: sha512-/Qq3PTagA6+nYVfryAtQ7/9FEr/6YVyvOtl6rZnGsbReGLf0jZU6gkpr1FuChAQpvV46a78p4cmHOVP8mbfSMQ==} + '@next/eslint-plugin-next@16.2.3': + resolution: {integrity: sha512-nE/b9mht28XJxjTwKs/yk7w4XTaU3t40UHVAky6cjiijdP/SEy3hGsnQMPxmXPTpC7W4/97okm6fngKnvCqVaA==} - '@next/swc-darwin-arm64@16.1.6': - resolution: {integrity: sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==} + '@next/swc-darwin-arm64@16.2.3': + resolution: {integrity: sha512-u37KDKTKQ+OQLvY+z7SNXixwo4Q2/IAJFDzU1fYe66IbCE51aDSAzkNDkWmLN0yjTUh4BKBd+hb69jYn6qqqSg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.1.6': - resolution: {integrity: sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==} + '@next/swc-darwin-x64@16.2.3': + resolution: {integrity: sha512-gHjL/qy6Q6CG3176FWbAKyKh9IfntKZTB3RY/YOJdDFpHGsUDXVH38U4mMNpHVGXmeYW4wj22dMp1lTfmu/bTQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.1.6': - resolution: {integrity: sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==} + '@next/swc-linux-arm64-gnu@16.2.3': + resolution: {integrity: sha512-U6vtblPtU/P14Y/b/n9ZY0GOxbbIhTFuaFR7F4/uMBidCi2nSdaOFhA0Go81L61Zd6527+yvuX44T4ksnf8T+Q==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.1.6': - resolution: {integrity: sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==} + '@next/swc-linux-arm64-musl@16.2.3': + resolution: {integrity: sha512-/YV0LgjHUmfhQpn9bVoGc4x4nan64pkhWR5wyEV8yCOfwwrH630KpvRg86olQHTwHIn1z59uh6JwKvHq1h4QEw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.1.6': - resolution: {integrity: sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==} + '@next/swc-linux-x64-gnu@16.2.3': + resolution: {integrity: sha512-/HiWEcp+WMZ7VajuiMEFGZ6cg0+aYZPqCJD3YJEfpVWQsKYSjXQG06vJP6F1rdA03COD9Fef4aODs3YxKx+RDQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.1.6': - resolution: {integrity: sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==} + '@next/swc-linux-x64-musl@16.2.3': + resolution: {integrity: sha512-Kt44hGJfZSefebhk/7nIdivoDr3Ugp5+oNz9VvF3GUtfxutucUIHfIO0ZYO8QlOPDQloUVQn4NVC/9JvHRk9hw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.1.6': - resolution: {integrity: sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==} + '@next/swc-win32-arm64-msvc@16.2.3': + resolution: {integrity: sha512-O2NZ9ie3Tq6xj5Z5CSwBT3+aWAMW2PIZ4egUi9MaWLkwaehgtB7YZjPm+UpcNpKOme0IQuqDcor7BsW6QBiQBw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.1.6': - resolution: {integrity: sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==} + '@next/swc-win32-x64-msvc@16.2.3': + resolution: {integrity: sha512-Ibm29/GgB/ab5n7XKqlStkm54qqZE8v2FnijUPBgrd67FWrac45o/RsNlaOWjme/B5UqeWt/8KM4aWBwA1D2Kw==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1965,98 +1966,98 @@ packages: '@rolldown/pluginutils@1.0.0-rc.3': resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} - '@rollup/rollup-android-arm-eabi@4.60.0': - resolution: {integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==} + '@rollup/rollup-android-arm-eabi@4.60.1': + resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.0': - resolution: {integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==} + '@rollup/rollup-android-arm64@4.60.1': + resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.0': - resolution: {integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==} + '@rollup/rollup-darwin-arm64@4.60.1': + resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.0': - resolution: {integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==} + '@rollup/rollup-darwin-x64@4.60.1': + resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.0': - resolution: {integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==} + '@rollup/rollup-freebsd-arm64@4.60.1': + resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.0': - resolution: {integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==} + '@rollup/rollup-freebsd-x64@4.60.1': + resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.0': - resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.0': - resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==} + '@rollup/rollup-linux-arm-musleabihf@4.60.1': + resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.0': - resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==} + '@rollup/rollup-linux-arm64-gnu@4.60.1': + resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.0': - resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==} + '@rollup/rollup-linux-arm64-musl@4.60.1': + resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.0': - resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==} + '@rollup/rollup-linux-loong64-gnu@4.60.1': + resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.0': - resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==} + '@rollup/rollup-linux-loong64-musl@4.60.1': + resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.0': - resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==} + '@rollup/rollup-linux-ppc64-gnu@4.60.1': + resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.0': - resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==} + '@rollup/rollup-linux-ppc64-musl@4.60.1': + resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.0': - resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==} + '@rollup/rollup-linux-riscv64-gnu@4.60.1': + resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.0': - resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==} + '@rollup/rollup-linux-riscv64-musl@4.60.1': + resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.0': - resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==} + '@rollup/rollup-linux-s390x-gnu@4.60.1': + resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] libc: [glibc] @@ -2067,45 +2068,45 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.0': - resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==} + '@rollup/rollup-linux-x64-gnu@4.60.1': + resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.0': - resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==} + '@rollup/rollup-linux-x64-musl@4.60.1': + resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.0': - resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==} + '@rollup/rollup-openbsd-x64@4.60.1': + resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.0': - resolution: {integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==} + '@rollup/rollup-openharmony-arm64@4.60.1': + resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.0': - resolution: {integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==} + '@rollup/rollup-win32-arm64-msvc@4.60.1': + resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.0': - resolution: {integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==} + '@rollup/rollup-win32-ia32-msvc@4.60.1': + resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.0': - resolution: {integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==} + '@rollup/rollup-win32-x64-gnu@4.60.1': + resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.0': - resolution: {integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==} + '@rollup/rollup-win32-x64-msvc@4.60.1': + resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} cpu: [x64] os: [win32] @@ -2277,11 +2278,11 @@ packages: '@tailwindcss/postcss@4.2.2': resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==} - '@tanstack/query-core@5.95.2': - resolution: {integrity: sha512-o4T8vZHZET4Bib3jZ/tCW9/7080urD4c+0/AUaYVpIqOsr7y0reBc1oX3ttNaSW5mYyvZHctiQ/UOP2PfdmFEQ==} + '@tanstack/query-core@5.97.0': + resolution: {integrity: sha512-QdpLP5VzVMgo4VtaPppRA2W04UFjIqX+bxke/ZJhE5cfd5UPkRzqIAJQt9uXkQJjqE8LBOMbKv7f8HCsZltXlg==} - '@tanstack/react-query@5.95.2': - resolution: {integrity: sha512-/wGkvLj/st5Ud1Q76KF1uFxScV7WeqN1slQx5280ycwAyYkIPGaRZAEgHxe3bjirSd5Zpwkj6zNcR4cqYni/ZA==} + '@tanstack/react-query@5.97.0': + resolution: {integrity: sha512-y4So4eGcQoK2WVMAcDNZE9ofB/p5v1OlKvtc1F3uqHwrtifobT7q+ZnXk2mRkc8E84HKYSlAE9z6HXl2V0+ySQ==} peerDependencies: react: ^18 || ^19 @@ -2313,19 +2314,21 @@ packages: react: ^17 || ^18 react-dom: ^17 || ^18 - '@trpc/client@11.10.0': - resolution: {integrity: sha512-h0s2AwDtuhS8INRb4hlo4z3RKCkarWqlOy+3ffJgrlDxzzW6aLUN+9nDrcN4huPje1Em15tbCOqhIc6oaKYTRw==} + '@trpc/client@11.16.0': + resolution: {integrity: sha512-TxIzm7OoK3baKZ0XCbuMUbI3GhgjcbKHIc4nWVKaRpCRnbSh0T31BT6fTPYwtnA/Nur8pBCGqC2B4J5hEPiPFQ==} + hasBin: true peerDependencies: - '@trpc/server': 11.10.0 + '@trpc/server': 11.16.0 typescript: '>=5.7.2' - '@trpc/next@11.10.0': - resolution: {integrity: sha512-IhyJKmXCkImGbvrfUi/TtCWYR+cLylJiWXVLqtkJs/byWK7nA0K+zmCUmbnPgr60gv3qp+X2YZpdapD7fyc5Mg==} + '@trpc/next@11.16.0': + resolution: {integrity: sha512-6KyedRnGd7wZ44crr7oZ/fHxVDPkyItvg2PS6kfhM5Y6yLlBcEsMOhqxqKQNQZHBjAtnQzZjPuCslP05gZ6BSA==} + hasBin: true peerDependencies: '@tanstack/react-query': ^5.59.15 - '@trpc/client': 11.10.0 - '@trpc/react-query': 11.10.0 - '@trpc/server': 11.10.0 + '@trpc/client': 11.16.0 + '@trpc/react-query': 11.16.0 + '@trpc/server': 11.16.0 next: '*' react: '>=16.8.0' react-dom: '>=16.8.0' @@ -2336,17 +2339,18 @@ packages: '@trpc/react-query': optional: true - '@trpc/react-query@11.10.0': - resolution: {integrity: sha512-SKLpwEMU32mpDTCc3msMxb0fx113x4Jsiw0/t+ENY6AtyvhvDMRF1bpWtoNyY6zpX5wN4JCQMhHef8k0T1rJIw==} + '@trpc/react-query@11.16.0': + resolution: {integrity: sha512-ah6ULOu4k7lCEFAEoEgqg14YPVu1lnCVVYcz8TP7avPPKwtcB0CbFKYve3ElRP+V5DoBT75Q+G66bRdR+rQ5fg==} peerDependencies: '@tanstack/react-query': ^5.80.3 - '@trpc/client': 11.10.0 - '@trpc/server': 11.10.0 + '@trpc/client': 11.16.0 + '@trpc/server': 11.16.0 react: '>=18.2.0' typescript: '>=5.7.2' - '@trpc/server@11.10.0': - resolution: {integrity: sha512-zZjTrR6He61e5TiT7e/bQqab/jRcXBZM8Fg78Yoo8uh5pz60dzzbYuONNUCOkafv5ppXVMms4NHYfNZgzw50vg==} + '@trpc/server@11.16.0': + resolution: {integrity: sha512-XgGuUMddrUTd04+za/WE5GFuZ1/YU9XQG0t3VL5WOIu2JspkOlq6k4RYEiqS6HSJt+S0RXaPdIoE2anIP/BBRQ==} + hasBin: true peerDependencies: typescript: '>=5.7.2' @@ -2473,8 +2477,11 @@ packages: '@types/node-schedule@2.1.8': resolution: {integrity: sha512-k00g6Yj/oUg/CDC+MeLHUzu0+OFxWbIqrFfDiLi6OPKxTujvpv29mHGM8GtKr7B+9Vv92FcK/8mRqi1DK5f3hA==} - '@types/node@25.5.0': - resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==} + '@types/node@25.5.2': + resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} + + '@types/node@25.6.0': + resolution: {integrity: sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2528,67 +2535,67 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.56.1': - resolution: {integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==} + '@typescript-eslint/eslint-plugin@8.58.1': + resolution: {integrity: sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.56.1 + '@typescript-eslint/parser': ^8.58.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.56.1': - resolution: {integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==} + '@typescript-eslint/parser@8.58.1': + resolution: {integrity: sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.56.1': - resolution: {integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==} + '@typescript-eslint/project-service@8.58.1': + resolution: {integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.56.1': - resolution: {integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==} + '@typescript-eslint/scope-manager@8.58.1': + resolution: {integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.56.1': - resolution: {integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==} + '@typescript-eslint/tsconfig-utils@8.58.1': + resolution: {integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.56.1': - resolution: {integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==} + '@typescript-eslint/type-utils@8.58.1': + resolution: {integrity: sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' - - '@typescript-eslint/types@8.56.1': - resolution: {integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/types@8.57.2': resolution: {integrity: sha512-/iZM6FnM4tnx9csuTxspMW4BOSegshwX5oBDznJ7S4WggL7Vczz5d2W11ecc4vRrQMQHXRSxzrCsyG5EsPPTbA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.56.1': - resolution: {integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==} + '@typescript-eslint/types@8.58.1': + resolution: {integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.58.1': + resolution: {integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.56.1': - resolution: {integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==} + '@typescript-eslint/utils@8.58.1': + resolution: {integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.56.1': - resolution: {integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==} + '@typescript-eslint/visitor-keys@8.58.1': + resolution: {integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -2697,26 +2704,26 @@ packages: cpu: [x64] os: [win32] - '@vitejs/plugin-react@5.1.4': - resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==} + '@vitejs/plugin-react@5.2.0': + resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitest/coverage-v8@4.1.2': - resolution: {integrity: sha512-sPK//PHO+kAkScb8XITeB1bf7fsk85Km7+rt4eeuRR3VS1/crD47cmV5wicisJmjNdfeokTZwjMk4Mj2d58Mgg==} + '@vitest/coverage-v8@4.1.4': + resolution: {integrity: sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w==} peerDependencies: - '@vitest/browser': 4.1.2 - vitest: 4.1.2 + '@vitest/browser': 4.1.4 + vitest: 4.1.4 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.2': - resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} + '@vitest/expect@4.1.4': + resolution: {integrity: sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==} - '@vitest/mocker@4.1.2': - resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} + '@vitest/mocker@4.1.4': + resolution: {integrity: sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2726,20 +2733,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.2': - resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} + '@vitest/pretty-format@4.1.4': + resolution: {integrity: sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==} - '@vitest/runner@4.1.2': - resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} + '@vitest/runner@4.1.4': + resolution: {integrity: sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==} - '@vitest/snapshot@4.1.2': - resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} + '@vitest/snapshot@4.1.4': + resolution: {integrity: sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==} - '@vitest/spy@4.1.2': - resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} + '@vitest/spy@4.1.4': + resolution: {integrity: sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==} - '@vitest/utils@4.1.2': - resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} + '@vitest/utils@4.1.4': + resolution: {integrity: sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -2888,10 +2895,6 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - ansis@4.2.0: - resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} - engines: {node: '>=14'} - any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -2969,8 +2972,8 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.11.0: - resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} + axe-core@4.11.2: + resolution: {integrity: sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==} engines: {node: '>=4'} axobject-query@4.1.0: @@ -2994,15 +2997,16 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.11: - resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} + baseline-browser-mapping@2.10.18: + resolution: {integrity: sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==} + engines: {node: '>=6.0.0'} hasBin: true before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - better-sqlite3@12.6.2: - resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==} + better-sqlite3@12.8.0: + resolution: {integrity: sha512-RxD2Vd96sQDjQr20kdP+F+dK/1OUNiVOl200vKBZY8u0vTwysfolF6Hq+3ZK2+h8My9YvZhHsF+RSGZW2VYrPQ==} engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x} bidi-js@1.0.3: @@ -3021,8 +3025,8 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@2.2.1: - resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} bonjour-service@1.3.0: @@ -3042,8 +3046,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3069,6 +3073,10 @@ packages: resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} @@ -3081,8 +3089,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001761: - resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} + caniuse-lite@1.0.30001787: + resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} cbor-extract@2.2.2: resolution: {integrity: sha512-hlSxxI9XO2yQfe9g6msd3g4xCfDqK5T5P0fRMLuaLHhxn4ViPrm+a+MUfhrvH2W962RGxcBwEGzLQyjbDG1gng==} @@ -3176,6 +3184,10 @@ packages: resolution: {integrity: sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==} engines: {node: '>= 0.2.0'} + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + cli-width@4.1.0: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} @@ -3222,6 +3234,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + colors@1.0.3: resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} engines: {node: '>=0.1.90'} @@ -3237,8 +3252,8 @@ packages: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} - commander@14.0.2: - resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} commander@2.20.3: @@ -3253,8 +3268,8 @@ packages: config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - content-disposition@1.0.1: - resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} content-type@1.0.5: @@ -3265,8 +3280,8 @@ packages: resolution: {integrity: sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==} engines: {node: '>=18'} - conventional-changelog-conventionalcommits@9.1.0: - resolution: {integrity: sha512-MnbEysR8wWa8dAEvbj5xcBgJKQlX/m0lhS8DsyAAWDHdfs2faDJxTgzRYlRYpXSe7UiKrIIlB4TrBKU9q9DgkA==} + conventional-changelog-conventionalcommits@9.3.1: + resolution: {integrity: sha512-dTYtpIacRpcZgrvBYvBfArMmK2xvIpv2TaxM0/ZI5CBtNUzvF2x0t15HsbRABWprS6UPmvj+PzHVjSx4qAVKyw==} engines: {node: '>=18'} conventional-changelog-writer@8.2.0: @@ -3293,8 +3308,8 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - cookie-es@1.2.2: - resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + cookie-es@1.2.3: + resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} @@ -3318,8 +3333,8 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} engines: {node: '>= 0.10'} cosmiconfig@7.1.0: @@ -3344,6 +3359,15 @@ packages: typescript: optional: true + cosmiconfig@9.0.1: + resolution: {integrity: sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} @@ -3359,8 +3383,8 @@ packages: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} - css-tree@3.1.0: - resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} cssesc@3.0.0: @@ -3368,10 +3392,6 @@ packages: engines: {node: '>=4'} hasBin: true - cssstyle@6.1.0: - resolution: {integrity: sha512-Ml4fP2UT2K3CUBQnVlbdV/8aFDdlY69E+YnwJM+3VUWl08S3J8c8aRuJqCkD9Py8DHZ7zNNvsfKl8psocHZEFg==} - engines: {node: '>=20'} - csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -3497,8 +3517,8 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.7.1: - resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -3520,8 +3540,8 @@ packages: resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} engines: {node: '>=18'} - default-browser@5.4.0: - resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} defaults@1.0.4: @@ -3539,8 +3559,8 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - defu@6.1.4: - resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + defu@6.1.6: + resolution: {integrity: sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==} depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} @@ -3560,8 +3580,8 @@ packages: devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - diff@8.0.2: - resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} dir-glob@3.0.1: @@ -3589,8 +3609,8 @@ packages: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} - dotenv@17.3.1: - resolution: {integrity: sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==} + dotenv@17.4.1: + resolution: {integrity: sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==} engines: {node: '>=12'} drizzle-kit@0.31.10: @@ -3696,15 +3716,15 @@ packages: duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} - eciesjs@0.4.16: - resolution: {integrity: sha512-dS5cbA9rA2VR4Ybuvhg6jvdmp46ubLn3E+px8cG/35aEDNclrqoCjg6mt0HYZ/M+OoESS3jSkCrqk1kWAEhWAw==} + eciesjs@0.4.18: + resolution: {integrity: sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + electron-to-chromium@1.5.335: + resolution: {integrity: sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -3752,6 +3772,10 @@ packages: resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -3801,8 +3825,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.4: - resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} hasBin: true @@ -3825,8 +3849,8 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} - eslint-config-next@16.1.6: - resolution: {integrity: sha512-vKq40io2B0XtkkNDYyleATwblNt8xuh3FWp8SpSz3pt7P01OkBFlKsJZ2mWt5WsCySlDQLckb1zMY9yE9Qy0LA==} + eslint-config-next@16.2.3: + resolution: {integrity: sha512-Dnkrylzjof/Az7iNoIQJqD18zTxQZcngir19KJaiRsMnnjpQSVoa6aEg/1Q4hQC+cW90uTlgQYadwL1CYNwFWA==} peerDependencies: eslint: '>=9.0.0' typescript: '>=3.3.1' @@ -3834,8 +3858,8 @@ packages: typescript: optional: true - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} eslint-import-resolver-typescript@3.10.1: resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} @@ -4007,8 +4031,8 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - express-rate-limit@8.2.1: - resolution: {integrity: sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==} + express-rate-limit@8.3.2: + resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -4043,8 +4067,8 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -4137,8 +4161,8 @@ packages: fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - fs-extra@11.3.3: - resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + fs-extra@11.3.4: + resolution: {integrity: sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==} engines: {node: '>=14.14'} fsevents@2.3.3: @@ -4166,9 +4190,6 @@ packages: fuzzysort@3.1.0: resolution: {integrity: sha512-sR9BNCjBg6LNgwvxlBd0sBABvQitkLzoVY9MYYROQVX/FvfJ4Mai9LsGhDgd8qYdds0bY77VzYd5iuB+v5rwQQ==} - fzf@0.5.2: - resolution: {integrity: sha512-Tt4kuxLXFKHy8KT40zwsUPUkg1CrsgY25FxA2U/j/0WgEDCk3ddc/zLTCCcbSHX9FcKtLuVaDGtGE/STWC+j3Q==} - generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} @@ -4181,8 +4202,8 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-east-asian-width@1.4.0: - resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + get-east-asian-width@1.5.0: + resolution: {integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==} engines: {node: '>=18'} get-intrinsic@1.3.0: @@ -4223,6 +4244,9 @@ packages: get-tsconfig@4.13.6: resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} + get-tsconfig@4.13.7: + resolution: {integrity: sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==} + git-log-parser@1.2.1: resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==} @@ -4275,8 +4299,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - graphql@16.12.0: - resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} + graphql@16.13.2: + resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} h3@1.15.1: @@ -4339,8 +4363,8 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - hono@4.12.2: - resolution: {integrity: sha512-gJnaDHXKDayjt8ue0n8Gs0A007yKXj4Xzb8+cNjZeYsSzzwKc0Lr+OZgYwVfB0pHfUs17EPoLvrOsEaJ9mj+Tg==} + hono@4.12.12: + resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} engines: {node: '>=16.9.0'} hook-std@4.0.0: @@ -4393,8 +4417,8 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} ieee754@1.2.1: @@ -4469,8 +4493,8 @@ packages: resolution: {integrity: sha512-2dYz766i9HprMBasCMvHMuazJ7u4WzhJwo5kb3iPSiW/iRYV6uPari3zHoqZlnuaR7V1bEiNMxikhp37rdBXbw==} engines: {node: '>=12'} - ip-address@10.0.1: - resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} ipaddr.js@1.9.1: @@ -4548,6 +4572,10 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} @@ -4685,8 +4713,8 @@ packages: resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} engines: {node: '>=18'} - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} isarray@1.0.0: @@ -4698,9 +4726,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isexe@3.1.1: - resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} - engines: {node: '>=16'} + isexe@3.1.5: + resolution: {integrity: sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==} + engines: {node: '>=18'} issue-parser@7.0.1: resolution: {integrity: sha512-3YZcUUR2Wt1WsapF+S/WiA2WmlW0cWAoPccMqne7AxEBhCdFeTPjfv/Axb8V2gyCgY3nRw+ksZ3xSUX+R47iAg==} @@ -4746,8 +4774,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + jose@6.2.2: + resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} js-sha256@0.10.1: resolution: {integrity: sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw==} @@ -4762,9 +4790,9 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true - jsdom@28.1.0: - resolution: {integrity: sha512-0+MoQNYyr2rBHqO1xilltfDjV9G7ymYGlAUazgcDLQaUf8JDHbuGwsxN6U9qWaElZ4w1B2r7yEGIL3GdeW3Rug==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + jsdom@29.0.2: + resolution: {integrity: sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -4919,6 +4947,15 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lint-staged@16.4.0: + resolution: {integrity: sha512-lBWt8hujh/Cjysw5GYVmZpFHXDCgZzhrOm8vbcUdobADZNOK/bRshr2kM3DfgrrtR1DQhfupW9gnIXOfiFi+bw==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.5: + resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} + engines: {node: '>=20.0.0'} + load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} @@ -4967,6 +5004,10 @@ packages: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + long-timeout@0.1.1: resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} @@ -4980,23 +5021,15 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.4: - resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} - engines: {node: 20 || >=22} - - lru-cache@11.2.6: - resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} - engines: {node: 20 || >=22} - - lru-cache@11.2.7: - resolution: {integrity: sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==} + lru-cache@11.3.3: + resolution: {integrity: sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ==} engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@0.577.0: - resolution: {integrity: sha512-4LjoFv2eEPwYDPg/CUdBJQSDfPyzXCRrVW1X7jrx/trgxnxkHFjnVZINbzvzxjN70dxychOfg+FTYwBiS3pQ5A==} + lucide-react@1.7.0: + resolution: {integrity: sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -5066,12 +5099,12 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - mdn-data@2.12.2: - resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} - mdn-data@2.23.0: resolution: {integrity: sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==} + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + media-typer@0.3.0: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} @@ -5199,8 +5232,8 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - minimatch@10.2.4: - resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} minimatch@3.1.2: @@ -5228,8 +5261,8 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msw@2.12.4: - resolution: {integrity: sha512-rHNiVfTyKhzc0EjoXUBVGteNKBevdjOlVC6GlIRXpy+/3LHEIGRovnB5WPjcvmNODVQ1TNFnoa7wsGbd0V3epg==} + msw@2.13.2: + resolution: {integrity: sha512-go2H1TIERKkC48pXiwec5l6sbNqYuvqOk3/vHGo1Zd+pq/H63oFawDQerH+WQdUw/flJFHDG7F+QdWMwhntA/A==} engines: {node: '>=18'} hasBin: true peerDependencies: @@ -5275,8 +5308,8 @@ packages: nerf-dart@1.0.0: resolution: {integrity: sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==} - next@16.1.6: - resolution: {integrity: sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==} + next@16.2.3: + resolution: {integrity: sha512-9V3zV4oZFza3PVev5/poB9g0dEafVcgNyQ8eTRop8GvxZjV2G15FC5ARuG1eFD42QgeYkzJBJzHghNP8Ad9xtA==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -5296,8 +5329,8 @@ packages: sass: optional: true - node-abi@3.87.0: - resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==} + node-abi@3.89.0: + resolution: {integrity: sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA==} engines: {node: '>=10'} node-domexception@1.0.0: @@ -5309,6 +5342,10 @@ packages: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + node-fetch@3.3.2: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -5320,8 +5357,8 @@ packages: node-mock-http@1.0.4: resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-releases@2.0.37: + resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} node-schedule@2.1.1: resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==} @@ -5590,9 +5627,6 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@1.6.0: - resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -5668,8 +5702,8 @@ packages: path-to-regexp@6.3.0: resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -5719,14 +5753,14 @@ packages: resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.9: + resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} + engines: {node: ^10 || ^12 || >=14} + powershell-utils@0.1.0: resolution: {integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==} engines: {node: '>=20'} @@ -5790,8 +5824,8 @@ packages: engines: {node: '>=16.0.0'} hasBin: true - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} punycode@1.4.1: resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} @@ -5804,6 +5838,14 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + engines: {node: '>=0.6'} + + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + engines: {node: '>=0.6'} + queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -5826,10 +5868,10 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@19.2.4: - resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + react-dom@19.2.5: + resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} peerDependencies: - react: ^19.2.4 + react: ^19.2.5 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -5871,8 +5913,8 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' - react@19.2.4: - resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + react@19.2.5: + resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} engines: {node: '>=0.10.0'} read-package-up@11.0.0: @@ -5962,8 +6004,8 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -5971,6 +6013,11 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -5979,15 +6026,18 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - rettime@0.7.0: - resolution: {integrity: sha512-LPRKoHnLKd/r3dVxcwO7vhCW+orkOGj9ViueosEBK6ie89CijnfRlhaDhHq/3Hxu4CkWQtxwlBG0mzTQY6uQjw==} + rettime@0.10.1: + resolution: {integrity: sha512-uyDrIlUEH37cinabq0AX4QbgV4HbFZ/gqoiunWQ1UqBtRvTTytwhNYjE++pO/MjPTZL5KQCf2bEoJ/BJNVQ5Kw==} reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.60.0: - resolution: {integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rollup@4.60.1: + resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -6080,8 +6130,8 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - shadcn@3.8.5: - resolution: {integrity: sha512-jPRx44e+eyeV7xwY3BLJXcfrks00+M0h5BGB9l6DdcBW4BpAj4x3lVmVy0TXPEs2iHEisxejr62sZAAw6B1EVA==} + shadcn@4.2.0: + resolution: {integrity: sha512-ZDuV340itidaUd4Gi1BxQX+Y7Ush6BHp6URZBM2RyxUUBZ6yFtOWIr4nVY+Ro+YRSpo82v7JrsmtcU5xoBCMJQ==} hasBin: true sharp@0.34.5: @@ -6139,6 +6189,14 @@ packages: resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} engines: {node: '>=8'} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + sorted-array-functions@1.3.0: resolution: {integrity: sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==} @@ -6212,6 +6270,10 @@ packages: strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + string-byte-length@1.6.0: resolution: {integrity: sha512-h9KzyolUa+9q6yHPCGzvPOta0VpWqG0/x0o1on22PZL0t+8txWXl0JCkRG/Gvi58HnyDvT1YCzDH2bAOpEc++g==} engines: {node: '>=14.18.0'} @@ -6224,6 +6286,10 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.2.0: + resolution: {integrity: sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==} + engines: {node: '>=20'} + string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} @@ -6264,10 +6330,6 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} @@ -6442,19 +6504,23 @@ packages: resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} engines: {node: '>=18'} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyexec@1.1.1: + resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} + engines: {node: '>=18'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} - tldts-core@7.0.19: - resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} + tldts-core@7.0.28: + resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} - tldts@7.0.19: - resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} + tldts@7.0.28: + resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} hasBin: true to-regex-range@5.0.1: @@ -6468,8 +6534,8 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - tough-cookie@6.0.0: - resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} engines: {node: '>=16'} tr46@6.0.0: @@ -6486,15 +6552,15 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - trpc-to-openapi@3.1.0: - resolution: {integrity: sha512-5hKNl8XxE7QZE5UiaxpyKrRQCk6JRNiei4Ddg9I2Tmk2eEzR3/QhTvTElS32COZY+pWwVtqksVTwVWY8dHxLNw==} + trpc-to-openapi@3.2.0: + resolution: {integrity: sha512-nRexppGtL7og73dI4900xnfiGoOAx9PppAi7CVNeh8JfRHXUJ3lDe4JCjHgDAvC3FPHmwJAGDJ0b0BbRll98YA==} peerDependencies: '@trpc/server': ^11.1.0 zod: ^4.0.0 zod-openapi: ^5.0.1 - ts-api-utils@2.4.0: - resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -6557,6 +6623,10 @@ packages: resolution: {integrity: sha512-VCn+LMHbd4t6sF3wfU/+HKT63C9OoyrSIf4b+vtWHpt2U7/4InZG467YDNMFMR70DdHjAdpPWmw2lzRdg0Xqqg==} engines: {node: '>=20'} + type-fest@5.5.0: + resolution: {integrity: sha512-PlBfpQwiUvGViBNX84Yxwjsdhd1TUlXr6zjX7eoirtCPIr08NAmxwa+fcYBTeRQxHo9YC9wwF3m9i700sHma8g==} + engines: {node: '>=20'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -6581,12 +6651,12 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript-eslint@8.56.1: - resolution: {integrity: sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==} + typescript-eslint@8.58.1: + resolution: {integrity: sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <6.1.0' typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} @@ -6611,6 +6681,9 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici-types@7.19.2: + resolution: {integrity: sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==} + undici-types@7.24.6: resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} @@ -6618,12 +6691,8 @@ packages: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} - undici@7.16.0: - resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} - engines: {node: '>=20.18.1'} - - undici@7.22.0: - resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} + undici@7.24.7: + resolution: {integrity: sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==} engines: {node: '>=20.18.1'} unicode-emoji-modifier-base@1.0.0: @@ -6770,18 +6839,20 @@ packages: yaml: optional: true - vitest@4.1.2: - resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} + vitest@4.1.4: + resolution: {integrity: sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.2 - '@vitest/browser-preview': 4.1.2 - '@vitest/browser-webdriverio': 4.1.2 - '@vitest/ui': 4.1.2 + '@vitest/browser-playwright': 4.1.4 + '@vitest/browser-preview': 4.1.4 + '@vitest/browser-webdriverio': 4.1.4 + '@vitest/coverage-istanbul': 4.1.4 + '@vitest/coverage-v8': 4.1.4 + '@vitest/ui': 4.1.4 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -6798,6 +6869,10 @@ packages: optional: true '@vitest/browser-webdriverio': optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true '@vitest/ui': optional: true happy-dom: @@ -6865,6 +6940,10 @@ packages: resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -6902,8 +6981,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -6914,8 +6993,8 @@ packages: utf-8-validate: optional: true - wsl-utils@0.3.0: - resolution: {integrity: sha512-3sFIGLiaDP7rTO4xh3g+b3AzhYDIUGGywE/WsmqzJWDxus5aJXVnPTNC/6L+r2WzrwXqVOdD262OaO+cEyPMSQ==} + wsl-utils@0.3.1: + resolution: {integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==} engines: {node: '>=20'} xml-name-validator@5.0.0: @@ -6940,8 +7019,8 @@ packages: resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} engines: {node: '>= 6'} - yaml@2.8.2: - resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} engines: {node: '>= 14.6'} hasBin: true @@ -6973,6 +7052,10 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + yocto-spinner@1.1.0: + resolution: {integrity: sha512-/BY0AUXnS7IKO354uLLA2eRcWiqDifEbd6unXCsOxkFDAkhgUL3PH9X2bFoaU0YchnDXsF+iKleeTLJGckbXfA==} + engines: {node: '>=18.19'} + yoctocolors-cjs@2.1.3: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} @@ -6987,16 +7070,16 @@ packages: peerDependencies: zod: ^3.25.74 || ^4.0.0 - zod-to-json-schema@3.25.0: - resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} - peerDependencies: - zod: ^3.25 || ^4 - zod-to-json-schema@3.25.1: resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} peerDependencies: zod: ^3.25 || ^4 + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} @@ -7029,8 +7112,6 @@ packages: snapshots: - '@acemir/cssom@0.9.31': {} - '@actions/core@2.0.1': dependencies: '@actions/exec': 2.0.0 @@ -7047,19 +7128,19 @@ snapshots: '@actions/io@2.0.0': {} - '@ajayche/trpc-panel@2.0.4(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@trpc/server@11.10.0(typescript@5.9.3))(@types/react@19.2.14)(immer@11.1.4)(monaco-editor@0.55.1)(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod@4.3.6)': + '@ajayche/trpc-panel@2.0.4(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@trpc/server@11.16.0(typescript@5.9.3))(@types/react@19.2.14)(immer@11.1.4)(monaco-editor@0.55.1)(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)': dependencies: - '@monaco-editor/react': 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@monaco-editor/react': 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@stoplight/json-schema-sampler': 0.3.0 - '@textea/json-viewer': 3.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@trpc/server': 11.10.0(typescript@5.9.3) + '@textea/json-viewer': 3.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@trpc/server': 11.16.0(typescript@5.9.3) clsx: 2.1.1 fuzzysort: 2.0.4 - nuqs: 2.8.9(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4) + nuqs: 2.8.9(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) path: 0.12.7 pretty-bytes: 6.1.1 pretty-ms: 8.0.0 - react-markdown: 9.1.0(@types/react@19.2.14)(react@19.2.4) + react-markdown: 9.1.0(@types/react@19.2.14)(react@19.2.5) string-byte-length: 1.6.0 tailwind-merge: 2.6.1 url: 0.11.4 @@ -7083,28 +7164,19 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@antfu/ni@25.0.0': - dependencies: - ansis: 4.2.0 - fzf: 0.5.2 - package-manager-detector: 1.6.0 - tinyexec: 1.0.4 - - '@asamuzakjp/css-color@5.0.1': + '@asamuzakjp/css-color@5.1.9': dependencies: '@csstools/css-calc': 3.1.1(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-color-parser': 4.0.2(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) '@csstools/css-tokenizer': 4.0.0 - lru-cache: 11.2.6 - '@asamuzakjp/dom-selector@6.8.1': + '@asamuzakjp/dom-selector@7.0.9': dependencies: '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 - css-tree: 3.1.0 + css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 - lru-cache: 11.2.6 '@asamuzakjp/nwsapi@2.3.9': {} @@ -7120,30 +7192,8 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.5': {} - '@babel/compat-data@7.29.0': {} - '@babel/core@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.29.1 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.29.0 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.29.0': dependencies: '@babel/code-frame': 7.29.0 @@ -7176,42 +7226,34 @@ snapshots: dependencies: '@babel/types': 7.29.0 - '@babel/helper-compilation-targets@7.27.2': - dependencies: - '@babel/compat-data': 7.28.5 - '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 - lru-cache: 5.1.1 - semver: 6.3.1 - '@babel/helper-compilation-targets@7.28.6': dependencies: '@babel/compat-data': 7.29.0 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.29.0) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 '@babel/traverse': 7.29.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.29.0)': + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.29.0) + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 '@babel/traverse': 7.29.0 semver: 6.3.1 @@ -7241,15 +7283,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.29.0 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -7274,16 +7307,18 @@ snapshots: '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.27.1(@babel/core@7.29.0)': + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-member-expression-to-functions': 7.28.5 @@ -7305,51 +7340,29 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.4': - dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.29.0 - '@babel/helpers@7.29.2': dependencies: '@babel/template': 7.28.6 '@babel/types': 7.29.0 - '@babel/parser@7.28.5': - dependencies: - '@babel/types': 7.29.0 - '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - transitivePeerDependencies: - - supports-color + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.29.0)': dependencies: @@ -7362,43 +7375,32 @@ snapshots: '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - transitivePeerDependencies: - - supports-color + '@babel/helper-plugin-utils': 7.28.6 '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.29.0) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color @@ -7417,30 +7419,12 @@ snapshots: '@babel/runtime@7.29.2': {} - '@babel/template@7.27.2': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.29.2 - '@babel/types': 7.29.0 - '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 '@babel/parser': 7.29.2 '@babel/types': 7.29.0 - '@babel/traverse@7.28.5': - dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.29.1 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.2 - '@babel/template': 7.27.2 - '@babel/types': 7.29.0 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - '@babel/traverse@7.29.0': dependencies: '@babel/code-frame': 7.29.0 @@ -7458,27 +7442,27 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@base-ui/react@1.3.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@base-ui/react@1.3.0(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 - '@base-ui/utils': 0.2.6(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@base-ui/utils': 0.2.6(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5) '@floating-ui/utils': 0.2.11 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) tabbable: 6.4.0 - use-sync-external-store: 1.6.0(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.5) optionalDependencies: '@types/react': 19.2.14 - '@base-ui/utils@0.2.6(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@base-ui/utils@0.2.6(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@floating-ui/utils': 0.2.11 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) reselect: 5.1.1 - use-sync-external-store: 1.6.0(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.5) optionalDependencies: '@types/react': 19.2.14 @@ -7486,7 +7470,7 @@ snapshots: '@bramus/specificity@2.4.2': dependencies: - css-tree: 3.1.0 + css-tree: 3.2.1 '@cbor-extract/cbor-extract-darwin-arm64@2.2.2': optional: true @@ -7527,40 +7511,43 @@ snapshots: dependencies: '@csstools/css-tokenizer': 4.0.0 - '@csstools/css-syntax-patches-for-csstree@1.0.28': {} + '@csstools/css-syntax-patches-for-csstree@1.1.2(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 '@csstools/css-tokenizer@4.0.0': {} - '@dotenvx/dotenvx@1.51.2': + '@dotenvx/dotenvx@1.61.0': dependencies: commander: 11.1.0 - dotenv: 17.3.1 - eciesjs: 0.4.16 + dotenv: 17.4.1 + eciesjs: 0.4.18 execa: 5.1.1 fdir: 6.5.0(picomatch@4.0.4) ignore: 5.3.2 object-treeify: 1.1.33 picomatch: 4.0.4 which: 4.0.0 + yocto-spinner: 1.1.0 '@drizzle-team/brocli@0.10.2': {} - '@ecies/ciphers@0.2.5(@noble/ciphers@1.3.0)': + '@ecies/ciphers@0.2.6(@noble/ciphers@1.3.0)': dependencies: '@noble/ciphers': 1.3.0 - '@emnapi/core@1.7.1': + '@emnapi/core@1.9.2': dependencies: - '@emnapi/wasi-threads': 1.1.0 + '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.7.1': + '@emnapi/runtime@1.9.2': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.1.0': + '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true @@ -7597,17 +7584,17 @@ snapshots: '@emotion/memoize@0.9.0': {} - '@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4)': + '@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.4) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.5) '@emotion/utils': 1.4.2 '@emotion/weak-memoize': 0.4.0 hoist-non-react-statics: 3.3.2 - react: 19.2.4 + react: 19.2.5 optionalDependencies: '@types/react': 19.2.14 transitivePeerDependencies: @@ -7623,16 +7610,16 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)': + '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4) + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5) '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.4) + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.5) '@emotion/utils': 1.4.2 - react: 19.2.4 + react: 19.2.5 optionalDependencies: '@types/react': 19.2.14 transitivePeerDependencies: @@ -7640,9 +7627,9 @@ snapshots: '@emotion/unitless@0.10.0': {} - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.4)': + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.5)': dependencies: - react: 19.2.4 + react: 19.2.5 '@emotion/utils@1.4.2': {} @@ -7664,7 +7651,7 @@ snapshots: '@esbuild/aix-ppc64@0.27.2': optional: true - '@esbuild/aix-ppc64@0.27.4': + '@esbuild/aix-ppc64@0.27.7': optional: true '@esbuild/android-arm64@0.18.20': @@ -7676,7 +7663,7 @@ snapshots: '@esbuild/android-arm64@0.27.2': optional: true - '@esbuild/android-arm64@0.27.4': + '@esbuild/android-arm64@0.27.7': optional: true '@esbuild/android-arm@0.18.20': @@ -7688,7 +7675,7 @@ snapshots: '@esbuild/android-arm@0.27.2': optional: true - '@esbuild/android-arm@0.27.4': + '@esbuild/android-arm@0.27.7': optional: true '@esbuild/android-x64@0.18.20': @@ -7700,7 +7687,7 @@ snapshots: '@esbuild/android-x64@0.27.2': optional: true - '@esbuild/android-x64@0.27.4': + '@esbuild/android-x64@0.27.7': optional: true '@esbuild/darwin-arm64@0.18.20': @@ -7712,7 +7699,7 @@ snapshots: '@esbuild/darwin-arm64@0.27.2': optional: true - '@esbuild/darwin-arm64@0.27.4': + '@esbuild/darwin-arm64@0.27.7': optional: true '@esbuild/darwin-x64@0.18.20': @@ -7724,7 +7711,7 @@ snapshots: '@esbuild/darwin-x64@0.27.2': optional: true - '@esbuild/darwin-x64@0.27.4': + '@esbuild/darwin-x64@0.27.7': optional: true '@esbuild/freebsd-arm64@0.18.20': @@ -7736,7 +7723,7 @@ snapshots: '@esbuild/freebsd-arm64@0.27.2': optional: true - '@esbuild/freebsd-arm64@0.27.4': + '@esbuild/freebsd-arm64@0.27.7': optional: true '@esbuild/freebsd-x64@0.18.20': @@ -7748,7 +7735,7 @@ snapshots: '@esbuild/freebsd-x64@0.27.2': optional: true - '@esbuild/freebsd-x64@0.27.4': + '@esbuild/freebsd-x64@0.27.7': optional: true '@esbuild/linux-arm64@0.18.20': @@ -7760,7 +7747,7 @@ snapshots: '@esbuild/linux-arm64@0.27.2': optional: true - '@esbuild/linux-arm64@0.27.4': + '@esbuild/linux-arm64@0.27.7': optional: true '@esbuild/linux-arm@0.18.20': @@ -7772,7 +7759,7 @@ snapshots: '@esbuild/linux-arm@0.27.2': optional: true - '@esbuild/linux-arm@0.27.4': + '@esbuild/linux-arm@0.27.7': optional: true '@esbuild/linux-ia32@0.18.20': @@ -7784,7 +7771,7 @@ snapshots: '@esbuild/linux-ia32@0.27.2': optional: true - '@esbuild/linux-ia32@0.27.4': + '@esbuild/linux-ia32@0.27.7': optional: true '@esbuild/linux-loong64@0.18.20': @@ -7796,7 +7783,7 @@ snapshots: '@esbuild/linux-loong64@0.27.2': optional: true - '@esbuild/linux-loong64@0.27.4': + '@esbuild/linux-loong64@0.27.7': optional: true '@esbuild/linux-mips64el@0.18.20': @@ -7808,7 +7795,7 @@ snapshots: '@esbuild/linux-mips64el@0.27.2': optional: true - '@esbuild/linux-mips64el@0.27.4': + '@esbuild/linux-mips64el@0.27.7': optional: true '@esbuild/linux-ppc64@0.18.20': @@ -7820,7 +7807,7 @@ snapshots: '@esbuild/linux-ppc64@0.27.2': optional: true - '@esbuild/linux-ppc64@0.27.4': + '@esbuild/linux-ppc64@0.27.7': optional: true '@esbuild/linux-riscv64@0.18.20': @@ -7832,7 +7819,7 @@ snapshots: '@esbuild/linux-riscv64@0.27.2': optional: true - '@esbuild/linux-riscv64@0.27.4': + '@esbuild/linux-riscv64@0.27.7': optional: true '@esbuild/linux-s390x@0.18.20': @@ -7844,7 +7831,7 @@ snapshots: '@esbuild/linux-s390x@0.27.2': optional: true - '@esbuild/linux-s390x@0.27.4': + '@esbuild/linux-s390x@0.27.7': optional: true '@esbuild/linux-x64@0.18.20': @@ -7856,7 +7843,7 @@ snapshots: '@esbuild/linux-x64@0.27.2': optional: true - '@esbuild/linux-x64@0.27.4': + '@esbuild/linux-x64@0.27.7': optional: true '@esbuild/netbsd-arm64@0.25.12': @@ -7865,7 +7852,7 @@ snapshots: '@esbuild/netbsd-arm64@0.27.2': optional: true - '@esbuild/netbsd-arm64@0.27.4': + '@esbuild/netbsd-arm64@0.27.7': optional: true '@esbuild/netbsd-x64@0.18.20': @@ -7877,7 +7864,7 @@ snapshots: '@esbuild/netbsd-x64@0.27.2': optional: true - '@esbuild/netbsd-x64@0.27.4': + '@esbuild/netbsd-x64@0.27.7': optional: true '@esbuild/openbsd-arm64@0.25.12': @@ -7886,7 +7873,7 @@ snapshots: '@esbuild/openbsd-arm64@0.27.2': optional: true - '@esbuild/openbsd-arm64@0.27.4': + '@esbuild/openbsd-arm64@0.27.7': optional: true '@esbuild/openbsd-x64@0.18.20': @@ -7898,7 +7885,7 @@ snapshots: '@esbuild/openbsd-x64@0.27.2': optional: true - '@esbuild/openbsd-x64@0.27.4': + '@esbuild/openbsd-x64@0.27.7': optional: true '@esbuild/openharmony-arm64@0.25.12': @@ -7907,7 +7894,7 @@ snapshots: '@esbuild/openharmony-arm64@0.27.2': optional: true - '@esbuild/openharmony-arm64@0.27.4': + '@esbuild/openharmony-arm64@0.27.7': optional: true '@esbuild/sunos-x64@0.18.20': @@ -7919,7 +7906,7 @@ snapshots: '@esbuild/sunos-x64@0.27.2': optional: true - '@esbuild/sunos-x64@0.27.4': + '@esbuild/sunos-x64@0.27.7': optional: true '@esbuild/win32-arm64@0.18.20': @@ -7931,7 +7918,7 @@ snapshots: '@esbuild/win32-arm64@0.27.2': optional: true - '@esbuild/win32-arm64@0.27.4': + '@esbuild/win32-arm64@0.27.7': optional: true '@esbuild/win32-ia32@0.18.20': @@ -7943,7 +7930,7 @@ snapshots: '@esbuild/win32-ia32@0.27.2': optional: true - '@esbuild/win32-ia32@0.27.4': + '@esbuild/win32-ia32@0.27.7': optional: true '@esbuild/win32-x64@0.18.20': @@ -7955,7 +7942,7 @@ snapshots: '@esbuild/win32-x64@0.27.2': optional: true - '@esbuild/win32-x64@0.27.4': + '@esbuild/win32-x64@0.27.7': optional: true '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': @@ -7990,7 +7977,7 @@ snapshots: mdn-data: 2.23.0 source-map-js: 1.2.1 - '@eslint/css@1.0.0': + '@eslint/css@1.1.0': dependencies: '@eslint/core': 1.1.1 '@eslint/css-tree': 3.6.9 @@ -8031,7 +8018,7 @@ snapshots: '@eslint/core': 1.1.1 levn: 0.4.1 - '@exodus/bytes@1.14.1(@noble/hashes@1.8.0)': + '@exodus/bytes@1.15.0(@noble/hashes@1.8.0)': optionalDependencies: '@noble/hashes': 1.8.0 @@ -8046,11 +8033,11 @@ snapshots: '@floating-ui/core': 1.7.5 '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@floating-ui/react-dom@2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@floating-ui/dom': 1.7.6 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) '@floating-ui/utils@0.2.11': {} @@ -8062,9 +8049,9 @@ snapshots: '@hapi/bourne@3.0.0': {} - '@hono/node-server@1.19.9(hono@4.12.2)': + '@hono/node-server@1.19.13(hono@4.12.12)': dependencies: - hono: 4.12.2 + hono: 4.12.12 '@humanfs/core@0.19.1': {} @@ -8079,7 +8066,7 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@img/colour@1.0.0': + '@img/colour@1.1.0': optional: true '@img/sharp-darwin-arm64@0.34.5': @@ -8164,7 +8151,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.7.1 + '@emnapi/runtime': 1.9.2 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -8178,31 +8165,31 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/confirm@5.1.21(@types/node@25.5.0)': + '@inquirer/confirm@5.1.21(@types/node@25.5.2)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.5.0) - '@inquirer/type': 3.0.10(@types/node@25.5.0) + '@inquirer/core': 10.3.2(@types/node@25.5.2) + '@inquirer/type': 3.0.10(@types/node@25.5.2) optionalDependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 - '@inquirer/core@10.3.2(@types/node@25.5.0)': + '@inquirer/core@10.3.2(@types/node@25.5.2)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.5.0) + '@inquirer/type': 3.0.10(@types/node@25.5.2) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@inquirer/figures@1.0.15': {} - '@inquirer/type@3.0.10(@types/node@25.5.0)': + '@inquirer/type@3.0.10(@types/node@25.5.2)': optionalDependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@isaacs/cliui@9.0.0': {} @@ -8215,7 +8202,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -8245,35 +8232,35 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@lingui/babel-plugin-extract-messages@5.9.4': {} + '@lingui/babel-plugin-extract-messages@5.9.5': {} - '@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3)': + '@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3)': dependencies: '@babel/core': 7.29.0 '@babel/runtime': 7.29.2 '@babel/types': 7.29.0 - '@lingui/conf': 5.9.4(typescript@5.9.3) - '@lingui/core': 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) - '@lingui/message-utils': 5.9.4 + '@lingui/conf': 5.9.5(typescript@5.9.3) + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) + '@lingui/message-utils': 5.9.5 optionalDependencies: babel-plugin-macros: 3.1.0 transitivePeerDependencies: - supports-color - typescript - '@lingui/cli@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3)': + '@lingui/cli@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3)': dependencies: '@babel/core': 7.29.0 '@babel/generator': 7.29.1 '@babel/parser': 7.29.2 '@babel/runtime': 7.29.2 '@babel/types': 7.29.0 - '@lingui/babel-plugin-extract-messages': 5.9.4 - '@lingui/babel-plugin-lingui-macro': 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) - '@lingui/conf': 5.9.4(typescript@5.9.3) - '@lingui/core': 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) - '@lingui/format-po': 5.9.4(typescript@5.9.3) - '@lingui/message-utils': 5.9.4 + '@lingui/babel-plugin-extract-messages': 5.9.5 + '@lingui/babel-plugin-lingui-macro': 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) + '@lingui/conf': 5.9.5(typescript@5.9.3) + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) + '@lingui/format-po': 5.9.5(typescript@5.9.3) + '@lingui/message-utils': 5.9.5 chokidar: 3.5.1 cli-table: 0.3.11 commander: 10.0.1 @@ -8295,7 +8282,7 @@ snapshots: - supports-color - typescript - '@lingui/conf@5.9.4(typescript@5.9.3)': + '@lingui/conf@5.9.5(typescript@5.9.3)': dependencies: '@babel/runtime': 7.29.2 cosmiconfig: 8.3.6(typescript@5.9.3) @@ -8305,69 +8292,69 @@ snapshots: transitivePeerDependencies: - typescript - '@lingui/core@5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)': + '@lingui/core@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)': dependencies: '@babel/runtime': 7.29.2 - '@lingui/message-utils': 5.9.4 + '@lingui/message-utils': 5.9.5 optionalDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) + '@lingui/babel-plugin-lingui-macro': 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) babel-plugin-macros: 3.1.0 - '@lingui/format-po@5.9.4(typescript@5.9.3)': + '@lingui/format-po@5.9.5(typescript@5.9.3)': dependencies: - '@lingui/conf': 5.9.4(typescript@5.9.3) - '@lingui/message-utils': 5.9.4 + '@lingui/conf': 5.9.5(typescript@5.9.3) + '@lingui/message-utils': 5.9.5 date-fns: 3.6.0 pofile: 1.1.4 transitivePeerDependencies: - typescript - '@lingui/loader@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3)(webpack@5.104.1)': + '@lingui/loader@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3)(webpack@5.104.1)': dependencies: '@babel/runtime': 7.29.2 - '@lingui/cli': 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) - '@lingui/conf': 5.9.4(typescript@5.9.3) + '@lingui/cli': 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) + '@lingui/conf': 5.9.5(typescript@5.9.3) webpack: 5.104.1 transitivePeerDependencies: - babel-plugin-macros - supports-color - typescript - '@lingui/macro@5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.4)': + '@lingui/macro@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.5)': dependencies: - '@lingui/core': 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) - '@lingui/react': 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.4) + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) + '@lingui/react': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.5) optionalDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) + '@lingui/babel-plugin-lingui-macro': 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) babel-plugin-macros: 3.1.0 transitivePeerDependencies: - react - '@lingui/message-utils@5.9.4': + '@lingui/message-utils@5.9.5': dependencies: '@messageformat/parser': 5.1.1 js-sha256: 0.10.1 - '@lingui/react@5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.4)': + '@lingui/react@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0)(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 - '@lingui/core': 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) - react: 19.2.4 + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) + react: 19.2.5 optionalDependencies: - '@lingui/babel-plugin-lingui-macro': 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) + '@lingui/babel-plugin-lingui-macro': 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) babel-plugin-macros: 3.1.0 - '@lingui/swc-plugin@5.11.0(@lingui/core@5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0))(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))': + '@lingui/swc-plugin@5.11.0(@lingui/core@5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0))(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))': dependencies: - '@lingui/core': 5.9.4(@lingui/babel-plugin-lingui-macro@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) + '@lingui/core': 5.9.5(@lingui/babel-plugin-lingui-macro@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3))(babel-plugin-macros@3.1.0) optionalDependencies: - next: 16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@lingui/vite-plugin@5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': + '@lingui/vite-plugin@5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@lingui/cli': 5.9.4(babel-plugin-macros@3.1.0)(typescript@5.9.3) - '@lingui/conf': 5.9.4(typescript@5.9.3) - vite: 7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + '@lingui/cli': 5.9.5(babel-plugin-macros@3.1.0)(typescript@5.9.3) + '@lingui/conf': 5.9.5(typescript@5.9.3) + vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -8377,25 +8364,25 @@ snapshots: dependencies: moo: 0.5.3 - '@modelcontextprotocol/sdk@1.27.0(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': dependencies: - '@hono/node-server': 1.19.9(hono@4.12.2) + '@hono/node-server': 1.19.13(hono@4.12.12) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 - cors: 2.8.5 + cors: 2.8.6 cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 8.2.1(express@5.2.1) - hono: 4.12.2 - jose: 6.1.3 + express-rate-limit: 8.3.2(express@5.2.1) + hono: 4.12.12 + jose: 6.2.2 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 zod: 3.25.76 - zod-to-json-schema: 3.25.1(zod@3.25.76) + zod-to-json-schema: 3.25.2(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -8403,14 +8390,14 @@ snapshots: dependencies: state-local: 1.0.7 - '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@monaco-editor/react@4.7.0(monaco-editor@0.55.1)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@monaco-editor/loader': 1.7.0 monaco-editor: 0.55.1 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) - '@mswjs/interceptors@0.40.0': + '@mswjs/interceptors@0.41.3': dependencies: '@open-draft/deferred-promise': 2.2.0 '@open-draft/logger': 0.3.0 @@ -8421,115 +8408,115 @@ snapshots: '@mui/core-downloads-tracker@5.18.0': {} - '@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@mui/core-downloads-tracker': 5.18.0 - '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4) + '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5) '@mui/types': 7.2.24(@types/react@19.2.14) - '@mui/utils': 5.17.1(@types/react@19.2.14)(react@19.2.4) + '@mui/utils': 5.17.1(@types/react@19.2.14)(react@19.2.5) '@popperjs/core': 2.11.8 '@types/react-transition-group': 4.4.12(@types/react@19.2.14) clsx: 2.1.1 csstype: 3.2.3 prop-types: 15.8.1 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) react-is: 19.2.4 - react-transition-group: 4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react-transition-group: 4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4) + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5) '@types/react': 19.2.14 - '@mui/private-theming@5.17.1(@types/react@19.2.14)(react@19.2.4)': + '@mui/private-theming@5.17.1(@types/react@19.2.14)(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 - '@mui/utils': 5.17.1(@types/react@19.2.14)(react@19.2.4) + '@mui/utils': 5.17.1(@types/react@19.2.14)(react@19.2.5) prop-types: 15.8.1 - react: 19.2.4 + react: 19.2.5 optionalDependencies: '@types/react': 19.2.14 - '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4)': + '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 csstype: 3.2.3 prop-types: 15.8.1 - react: 19.2.4 + react: 19.2.5 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4) + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5) - '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)': + '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 - '@mui/private-theming': 5.17.1(@types/react@19.2.14)(react@19.2.4) - '@mui/styled-engine': 5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) + '@mui/private-theming': 5.17.1(@types/react@19.2.14)(react@19.2.5) + '@mui/styled-engine': 5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(react@19.2.5) '@mui/types': 7.2.24(@types/react@19.2.14) - '@mui/utils': 5.17.1(@types/react@19.2.14)(react@19.2.4) + '@mui/utils': 5.17.1(@types/react@19.2.14)(react@19.2.5) clsx: 2.1.1 csstype: 3.2.3 prop-types: 15.8.1 - react: 19.2.4 + react: 19.2.5 optionalDependencies: - '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4) + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5) '@types/react': 19.2.14 '@mui/types@7.2.24(@types/react@19.2.14)': optionalDependencies: '@types/react': 19.2.14 - '@mui/utils@5.17.1(@types/react@19.2.14)(react@19.2.4)': + '@mui/utils@5.17.1(@types/react@19.2.14)(react@19.2.5)': dependencies: '@babel/runtime': 7.29.2 '@mui/types': 7.2.24(@types/react@19.2.14) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 - react: 19.2.4 + react: 19.2.5 react-is: 19.2.4 optionalDependencies: '@types/react': 19.2.14 '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.7.1 - '@emnapi/runtime': 1.7.1 + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 '@tybys/wasm-util': 0.10.1 optional: true - '@next/env@16.1.6': {} + '@next/env@16.2.3': {} - '@next/eslint-plugin-next@16.1.6': + '@next/eslint-plugin-next@16.2.3': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@16.1.6': + '@next/swc-darwin-arm64@16.2.3': optional: true - '@next/swc-darwin-x64@16.1.6': + '@next/swc-darwin-x64@16.2.3': optional: true - '@next/swc-linux-arm64-gnu@16.1.6': + '@next/swc-linux-arm64-gnu@16.2.3': optional: true - '@next/swc-linux-arm64-musl@16.1.6': + '@next/swc-linux-arm64-musl@16.2.3': optional: true - '@next/swc-linux-x64-gnu@16.1.6': + '@next/swc-linux-x64-gnu@16.2.3': optional: true - '@next/swc-linux-x64-musl@16.1.6': + '@next/swc-linux-x64-musl@16.2.3': optional: true - '@next/swc-win32-arm64-msvc@16.1.6': + '@next/swc-win32-arm64-msvc@16.2.3': optional: true - '@next/swc-win32-x64-msvc@16.1.6': + '@next/swc-win32-x64-msvc@16.2.3': optional: true '@noble/ciphers@1.3.0': {} @@ -8550,7 +8537,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@nolyfill/is-core-module@1.0.39': {} @@ -8636,7 +8623,7 @@ snapshots: '@popperjs/core@2.11.8': {} - '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4)': + '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1))(react@19.2.5)': dependencies: '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 @@ -8645,87 +8632,87 @@ snapshots: redux-thunk: 3.1.0(redux@5.0.1) reselect: 5.1.1 optionalDependencies: - react: 19.2.4 - react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) + react: 19.2.5 + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1) '@rolldown/pluginutils@1.0.0-rc.3': {} - '@rollup/rollup-android-arm-eabi@4.60.0': + '@rollup/rollup-android-arm-eabi@4.60.1': optional: true - '@rollup/rollup-android-arm64@4.60.0': + '@rollup/rollup-android-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-arm64@4.60.0': + '@rollup/rollup-darwin-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-x64@4.60.0': + '@rollup/rollup-darwin-x64@4.60.1': optional: true - '@rollup/rollup-freebsd-arm64@4.60.0': + '@rollup/rollup-freebsd-arm64@4.60.1': optional: true - '@rollup/rollup-freebsd-x64@4.60.0': + '@rollup/rollup-freebsd-x64@4.60.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.0': + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.0': + '@rollup/rollup-linux-arm-musleabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.0': + '@rollup/rollup-linux-arm64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.0': + '@rollup/rollup-linux-arm64-musl@4.60.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.0': + '@rollup/rollup-linux-loong64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.0': + '@rollup/rollup-linux-loong64-musl@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.0': + '@rollup/rollup-linux-ppc64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.0': + '@rollup/rollup-linux-ppc64-musl@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.0': + '@rollup/rollup-linux-riscv64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.0': + '@rollup/rollup-linux-riscv64-musl@4.60.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.0': + '@rollup/rollup-linux-s390x-gnu@4.60.1': optional: true '@rollup/rollup-linux-x64-gnu@4.6.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.0': + '@rollup/rollup-linux-x64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-musl@4.60.0': + '@rollup/rollup-linux-x64-musl@4.60.1': optional: true - '@rollup/rollup-openbsd-x64@4.60.0': + '@rollup/rollup-openbsd-x64@4.60.1': optional: true - '@rollup/rollup-openharmony-arm64@4.60.0': + '@rollup/rollup-openharmony-arm64@4.60.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.0': + '@rollup/rollup-win32-arm64-msvc@4.60.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.0': + '@rollup/rollup-win32-ia32-msvc@4.60.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.0': + '@rollup/rollup-win32-x64-gnu@4.60.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.0': + '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true '@rtsao/scc@1.1.0': {} @@ -8781,8 +8768,8 @@ snapshots: mime: 4.1.0 p-filter: 4.1.0 semantic-release: 25.0.3(typescript@5.9.3) - tinyglobby: 0.2.15 - undici: 7.16.0 + tinyglobby: 0.2.16 + undici: 7.24.7 url-join: 5.0.0 transitivePeerDependencies: - supports-color @@ -8794,7 +8781,7 @@ snapshots: aggregate-error: 5.0.0 env-ci: 11.2.0 execa: 9.6.1 - fs-extra: 11.3.3 + fs-extra: 11.3.4 lodash-es: 4.17.22 nerf-dart: 1.0.0 normalize-url: 8.1.0 @@ -8922,12 +8909,12 @@ snapshots: postcss: 8.5.8 tailwindcss: 4.2.2 - '@tanstack/query-core@5.95.2': {} + '@tanstack/query-core@5.97.0': {} - '@tanstack/react-query@5.95.2(react@19.2.4)': + '@tanstack/react-query@5.97.0(react@19.2.5)': dependencies: - '@tanstack/query-core': 5.95.2 - react: 19.2.4 + '@tanstack/query-core': 5.97.0 + react: 19.2.5 '@testing-library/dom@10.4.1': dependencies: @@ -8940,63 +8927,63 @@ snapshots: picocolors: 1.1.1 pretty-format: 27.5.1 - '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@babel/runtime': 7.28.4 '@testing-library/dom': 10.4.1 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) optionalDependencies: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@textea/json-viewer@3.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@textea/json-viewer@3.5.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: - '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4) - '@mui/material': 5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5) + '@mui/material': 5.18.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) clsx: 2.1.1 copy-to-clipboard: 3.3.3 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - zustand: 4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + zustand: 4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.5) transitivePeerDependencies: - '@types/react' - immer - '@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3)': + '@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3)': dependencies: - '@trpc/server': 11.10.0(typescript@5.9.3) + '@trpc/server': 11.16.0(typescript@5.9.3) typescript: 5.9.3 - '@trpc/next@11.10.0(@tanstack/react-query@5.95.2(react@19.2.4))(@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.10.0(@tanstack/react-query@5.95.2(react@19.2.4))(@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.10.0(typescript@5.9.3))(react@19.2.4)(typescript@5.9.3))(@trpc/server@11.10.0(typescript@5.9.3))(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)': + '@trpc/next@11.16.0(@tanstack/react-query@5.97.0(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/react-query@11.16.0(@tanstack/react-query@5.97.0(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(typescript@5.9.3)': dependencies: - '@trpc/client': 11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3) - '@trpc/server': 11.10.0(typescript@5.9.3) - next: 16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + '@trpc/client': 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3) + '@trpc/server': 11.16.0(typescript@5.9.3) + next: 16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) typescript: 5.9.3 optionalDependencies: - '@tanstack/react-query': 5.95.2(react@19.2.4) - '@trpc/react-query': 11.10.0(@tanstack/react-query@5.95.2(react@19.2.4))(@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.10.0(typescript@5.9.3))(react@19.2.4)(typescript@5.9.3) + '@tanstack/react-query': 5.97.0(react@19.2.5) + '@trpc/react-query': 11.16.0(@tanstack/react-query@5.97.0(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3) - '@trpc/react-query@11.10.0(@tanstack/react-query@5.95.2(react@19.2.4))(@trpc/client@11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.10.0(typescript@5.9.3))(react@19.2.4)(typescript@5.9.3)': + '@trpc/react-query@11.16.0(@tanstack/react-query@5.97.0(react@19.2.5))(@trpc/client@11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3))(@trpc/server@11.16.0(typescript@5.9.3))(react@19.2.5)(typescript@5.9.3)': dependencies: - '@tanstack/react-query': 5.95.2(react@19.2.4) - '@trpc/client': 11.10.0(@trpc/server@11.10.0(typescript@5.9.3))(typescript@5.9.3) - '@trpc/server': 11.10.0(typescript@5.9.3) - react: 19.2.4 + '@tanstack/react-query': 5.97.0(react@19.2.5) + '@trpc/client': 11.16.0(@trpc/server@11.16.0(typescript@5.9.3))(typescript@5.9.3) + '@trpc/server': 11.16.0(typescript@5.9.3) + react: 19.2.5 typescript: 5.9.3 - '@trpc/server@11.10.0(typescript@5.9.3)': + '@trpc/server@11.16.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 '@ts-morph/common@0.27.0': dependencies: fast-glob: 3.3.3 - minimatch: 10.2.4 + minimatch: 10.2.5 path-browserify: 1.0.1 '@tsconfig/next@2.0.6': {} @@ -9031,11 +9018,11 @@ snapshots: '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@types/binary-split@1.0.3': dependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@types/chai@5.2.3': dependencies: @@ -9119,7 +9106,7 @@ snapshots: '@types/jsdom@28.0.1': dependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 undici-types: 7.24.6 @@ -9138,12 +9125,16 @@ snapshots: '@types/node-schedule@2.1.8': dependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 - '@types/node@25.5.0': + '@types/node@25.5.2': dependencies: undici-types: 7.18.2 + '@types/node@25.6.0': + dependencies: + undici-types: 7.19.2 + '@types/normalize-package-data@2.4.4': {} '@types/parse-json@4.0.2': {} @@ -9179,7 +9170,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 '@types/yargs-parser@21.0.3': {} @@ -9187,97 +9178,97 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.56.1 - '@typescript-eslint/type-utils': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.56.1 + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/type-utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.58.1 eslint: 9.39.4(jiti@2.6.1) ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.56.1 - '@typescript-eslint/types': 8.56.1 - '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.56.1 + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.56.1(typescript@5.9.3)': + '@typescript-eslint/project-service@8.58.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) - '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) + '@typescript-eslint/types': 8.58.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.56.1': + '@typescript-eslint/scope-manager@8.58.1': dependencies: - '@typescript-eslint/types': 8.56.1 - '@typescript-eslint/visitor-keys': 8.56.1 + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/visitor-keys': 8.58.1 - '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.58.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.56.1 - '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.56.1': {} - '@typescript-eslint/types@8.57.2': {} - '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.3)': + '@typescript-eslint/types@8.58.1': {} + + '@typescript-eslint/typescript-estree@8.58.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.56.1(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) - '@typescript-eslint/types': 8.56.1 - '@typescript-eslint/visitor-keys': 8.56.1 + '@typescript-eslint/project-service': 8.58.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@5.9.3) + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/visitor-keys': 8.58.1 debug: 4.4.3 - minimatch: 10.2.4 + minimatch: 10.2.5 semver: 7.7.4 - tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.9.3) + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.56.1 - '@typescript-eslint/types': 8.56.1 - '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.56.1': + '@typescript-eslint/visitor-keys@8.58.1': dependencies: - '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/types': 8.58.1 eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.0': {} @@ -9341,7 +9332,7 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react@5.1.4(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitejs/plugin-react@5.2.0(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -9349,14 +9340,14 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@4.1.2(vitest@4.1.2(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)))': + '@vitest/coverage-v8@4.1.4(vitest@4.1.4)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.2 + '@vitest/utils': 4.1.4 ast-v8-to-istanbul: 1.0.0 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 @@ -9365,47 +9356,47 @@ snapshots: obug: 2.1.1 std-env: 4.0.0 tinyrainbow: 3.1.0 - vitest: 4.1.2(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) + vitest: 4.1.4(@types/node@25.5.2)(@vitest/coverage-v8@4.1.4)(jsdom@29.0.2(@noble/hashes@1.8.0))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) - '@vitest/expect@4.1.2': + '@vitest/expect@4.1.4': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.2(msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.4(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))': dependencies: - '@vitest/spy': 4.1.2 + '@vitest/spy': 4.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - msw: 2.12.4(@types/node@25.5.0)(typescript@5.9.3) - vite: 7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + msw: 2.13.2(@types/node@25.5.2)(typescript@5.9.3) + vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) - '@vitest/pretty-format@4.1.2': + '@vitest/pretty-format@4.1.4': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.2': + '@vitest/runner@4.1.4': dependencies: - '@vitest/utils': 4.1.2 + '@vitest/utils': 4.1.4 pathe: 2.0.3 - '@vitest/snapshot@4.1.2': + '@vitest/snapshot@4.1.4': dependencies: - '@vitest/pretty-format': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/pretty-format': 4.1.4 + '@vitest/utils': 4.1.4 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.2': {} + '@vitest/spy@4.1.4': {} - '@vitest/utils@4.1.2': + '@vitest/utils@4.1.4': dependencies: - '@vitest/pretty-format': 4.1.2 + '@vitest/pretty-format': 4.1.4 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -9489,13 +9480,13 @@ snapshots: '@xtuc/long@4.2.2': {} - '@xyflow/react@12.10.2(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + '@xyflow/react@12.10.2(@types/react@19.2.14)(immer@11.1.4)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': dependencies: '@xyflow/system': 0.0.76 classcat: 5.0.5 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - zustand: 4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + zustand: 4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.5) transitivePeerDependencies: - '@types/react' - immer @@ -9586,8 +9577,6 @@ snapshots: ansi-styles@6.2.3: {} - ansis@4.2.0: {} - any-promise@1.3.0: {} anymatch@3.1.3: @@ -9634,19 +9623,19 @@ snapshots: array.prototype.findlastindex@1.2.6: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 array.prototype.flat@1.3.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: @@ -9694,7 +9683,7 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.11.0: {} + axe-core@4.11.2: {} axobject-query@4.1.0: {} @@ -9702,7 +9691,7 @@ snapshots: dependencies: '@babel/runtime': 7.29.2 cosmiconfig: 7.1.0 - resolve: 1.22.11 + resolve: 1.22.12 bail@2.0.2: {} @@ -9712,11 +9701,11 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.9.11: {} + baseline-browser-mapping@2.10.18: {} before-after-hook@4.0.0: {} - better-sqlite3@12.6.2: + better-sqlite3@12.8.0: dependencies: bindings: 1.5.0 prebuild-install: 7.1.3 @@ -9741,15 +9730,15 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - body-parser@2.2.1: + body-parser@2.2.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3 http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.14.0 + qs: 6.15.1 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -9775,13 +9764,13 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.1: + browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001761 - electron-to-chromium: 1.5.267 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + baseline-browser-mapping: 2.10.18 + caniuse-lite: 1.0.30001787 + electron-to-chromium: 1.5.335 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) buffer-from@1.1.2: {} @@ -9808,6 +9797,13 @@ snapshots: get-intrinsic: 1.3.0 set-function-length: 1.2.2 + 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-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 @@ -9817,7 +9813,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001761: {} + caniuse-lite@1.0.30001787: {} cbor-extract@2.2.2: dependencies: @@ -9919,6 +9915,11 @@ snapshots: dependencies: colors: 1.0.3 + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.0 + cli-width@4.1.0: {} client-only@0.0.1: {} @@ -9949,7 +9950,7 @@ snapshots: dependencies: '@hapi/bourne': 3.0.0 inflation: 2.1.0 - qs: 6.14.0 + qs: 6.15.0 raw-body: 2.5.3 type-is: 1.6.18 @@ -9967,6 +9968,8 @@ snapshots: color-name@1.1.4: {} + colorette@2.0.20: {} + colors@1.0.3: {} comma-separated-tokens@2.0.3: {} @@ -9975,7 +9978,7 @@ snapshots: commander@11.1.0: {} - commander@14.0.2: {} + commander@14.0.3: {} commander@2.20.3: {} @@ -9991,7 +9994,7 @@ snapshots: ini: 1.3.8 proto-list: 1.2.4 - content-disposition@1.0.1: {} + content-disposition@1.1.0: {} content-type@1.0.5: {} @@ -9999,7 +10002,7 @@ snapshots: dependencies: compare-func: 2.0.0 - conventional-changelog-conventionalcommits@9.1.0: + conventional-changelog-conventionalcommits@9.3.1: dependencies: compare-func: 2.0.0 @@ -10022,7 +10025,7 @@ snapshots: convert-source-map@2.0.0: {} - cookie-es@1.2.2: {} + cookie-es@1.2.3: {} cookie-signature@1.2.2: {} @@ -10040,7 +10043,7 @@ snapshots: core-util-is@1.0.3: {} - cors@2.8.5: + cors@2.8.6: dependencies: object-assign: 4.1.1 vary: 1.1.2 @@ -10071,6 +10074,15 @@ snapshots: optionalDependencies: typescript: 5.9.3 + cosmiconfig@9.0.1(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + cron-parser@4.9.0: dependencies: luxon: 3.7.2 @@ -10089,20 +10101,13 @@ snapshots: dependencies: type-fest: 1.4.0 - css-tree@3.1.0: + css-tree@3.2.1: dependencies: - mdn-data: 2.12.2 + mdn-data: 2.27.1 source-map-js: 1.2.1 cssesc@3.0.0: {} - cssstyle@6.1.0: - dependencies: - '@asamuzakjp/css-color': 5.0.1 - '@csstools/css-syntax-patches-for-csstree': 1.0.28 - css-tree: 3.1.0 - lru-cache: 11.2.6 - csstype@3.2.3: {} d3-array@3.2.4: @@ -10220,7 +10225,7 @@ snapshots: dependencies: mimic-response: 3.1.0 - dedent@1.7.1(babel-plugin-macros@3.1.0): + dedent@1.7.2(babel-plugin-macros@3.1.0): optionalDependencies: babel-plugin-macros: 3.1.0 @@ -10232,7 +10237,7 @@ snapshots: default-browser-id@5.0.1: {} - default-browser@5.4.0: + default-browser@5.5.0: dependencies: bundle-name: 4.1.0 default-browser-id: 5.0.1 @@ -10255,7 +10260,7 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - defu@6.1.4: {} + defu@6.1.6: {} depd@2.0.0: {} @@ -10269,7 +10274,7 @@ snapshots: dependencies: dequal: 2.0.3 - diff@8.0.2: {} + diff@8.0.4: {} dir-glob@3.0.1: dependencies: @@ -10298,7 +10303,7 @@ snapshots: dependencies: is-obj: 2.0.0 - dotenv@17.3.1: {} + dotenv@17.4.1: {} drizzle-kit@0.31.10: dependencies: @@ -10307,10 +10312,10 @@ snapshots: esbuild: 0.25.12 tsx: 4.21.0 - drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.6.2): + drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.8.0): optionalDependencies: '@types/better-sqlite3': 7.6.13 - better-sqlite3: 12.6.2 + better-sqlite3: 12.8.0 dunder-proto@1.0.1: dependencies: @@ -10322,16 +10327,16 @@ snapshots: dependencies: readable-stream: 2.3.8 - eciesjs@0.4.16: + eciesjs@0.4.18: dependencies: - '@ecies/ciphers': 0.2.5(@noble/ciphers@1.3.0) + '@ecies/ciphers': 0.2.6(@noble/ciphers@1.3.0) '@noble/ciphers': 1.3.0 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 ee-first@1.1.1: {} - electron-to-chromium@1.5.267: {} + electron-to-chromium@1.5.335: {} emoji-regex@10.6.0: {} @@ -10424,6 +10429,63 @@ snapshots: unbox-primitive: 1.1.0 which-typed-array: 1.1.19 + 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.9 + 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.20 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -10555,34 +10617,34 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 - esbuild@0.27.4: + esbuild@0.27.7: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.4 - '@esbuild/android-arm': 0.27.4 - '@esbuild/android-arm64': 0.27.4 - '@esbuild/android-x64': 0.27.4 - '@esbuild/darwin-arm64': 0.27.4 - '@esbuild/darwin-x64': 0.27.4 - '@esbuild/freebsd-arm64': 0.27.4 - '@esbuild/freebsd-x64': 0.27.4 - '@esbuild/linux-arm': 0.27.4 - '@esbuild/linux-arm64': 0.27.4 - '@esbuild/linux-ia32': 0.27.4 - '@esbuild/linux-loong64': 0.27.4 - '@esbuild/linux-mips64el': 0.27.4 - '@esbuild/linux-ppc64': 0.27.4 - '@esbuild/linux-riscv64': 0.27.4 - '@esbuild/linux-s390x': 0.27.4 - '@esbuild/linux-x64': 0.27.4 - '@esbuild/netbsd-arm64': 0.27.4 - '@esbuild/netbsd-x64': 0.27.4 - '@esbuild/openbsd-arm64': 0.27.4 - '@esbuild/openbsd-x64': 0.27.4 - '@esbuild/openharmony-arm64': 0.27.4 - '@esbuild/sunos-x64': 0.27.4 - '@esbuild/win32-arm64': 0.27.4 - '@esbuild/win32-ia32': 0.27.4 - '@esbuild/win32-x64': 0.27.4 + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 escalade@3.2.0: {} @@ -10594,18 +10656,18 @@ snapshots: escape-string-regexp@5.0.0: {} - eslint-config-next@16.1.6(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): + eslint-config-next@16.2.3(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@next/eslint-plugin-next': 16.1.6 + '@next/eslint-plugin-next': 16.2.3 eslint: 9.39.4(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.6.1)) eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1)) globals: 16.4.0 - typescript-eslint: 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + typescript-eslint: 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -10614,11 +10676,11 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-import-resolver-node@0.3.9: + eslint-import-resolver-node@0.3.10: dependencies: debug: 3.2.7 is-core-module: 2.16.1 - resolve: 1.22.11 + resolve: 2.0.0-next.6 transitivePeerDependencies: - supports-color @@ -10627,28 +10689,28 @@ snapshots: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 eslint: 9.39.4(jiti@2.6.1) - get-tsconfig: 4.13.0 + get-tsconfig: 4.13.7 is-bun-module: 2.0.0 stable-hash: 0.0.5 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.6.1)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -10658,8 +10720,8 @@ snapshots: debug: 3.2.7 doctrine: 2.1.0 eslint: 9.39.4(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) + eslint-import-resolver-node: 0.3.10 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -10671,7 +10733,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -10683,7 +10745,7 @@ snapshots: array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.11.0 + axe-core: 4.11.2 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 @@ -10872,16 +10934,16 @@ snapshots: expect-type@1.3.0: {} - express-rate-limit@8.2.1(express@5.2.1): + express-rate-limit@8.3.2(express@5.2.1): dependencies: express: 5.2.1 - ip-address: 10.0.1 + ip-address: 10.1.0 express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.1 - content-disposition: 1.0.1 + body-parser: 2.2.2 + content-disposition: 1.1.0 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 @@ -10899,7 +10961,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.14.0 + qs: 6.15.1 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -10938,7 +11000,7 @@ snapshots: fast-uri@3.1.0: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -11031,7 +11093,7 @@ snapshots: fs-constants@1.0.0: {} - fs-extra@11.3.3: + fs-extra@11.3.4: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 @@ -11059,15 +11121,13 @@ snapshots: fuzzysort@3.1.0: {} - fzf@0.5.2: {} - generator-function@2.0.1: {} gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} - get-east-asian-width@1.4.0: {} + get-east-asian-width@1.5.0: {} get-intrinsic@1.3.0: dependencies: @@ -11114,6 +11174,10 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + get-tsconfig@4.13.7: + dependencies: + resolve-pkg-maps: 1.0.0 + git-log-parser@1.2.1: dependencies: argv-formatter: 1.0.0 @@ -11139,7 +11203,7 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 4.2.3 - minimatch: 10.2.4 + minimatch: 10.2.5 minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 2.0.2 @@ -11163,13 +11227,13 @@ snapshots: graceful-fs@4.2.11: {} - graphql@16.12.0: {} + graphql@16.13.2: {} h3@1.15.1: dependencies: - cookie-es: 1.2.2 + cookie-es: 1.2.3 crossws: 0.3.5 - defu: 6.1.4 + defu: 6.1.6 destr: 2.0.5 iron-webcrypto: 1.2.1 node-mock-http: 1.0.4 @@ -11248,7 +11312,7 @@ snapshots: dependencies: react-is: 16.13.1 - hono@4.12.2: {} + hono@4.12.12: {} hook-std@4.0.0: {} @@ -11258,11 +11322,11 @@ snapshots: hosted-git-info@9.0.2: dependencies: - lru-cache: 11.2.4 + lru-cache: 11.3.3 html-encoding-sniffer@6.0.0(@noble/hashes@1.8.0): dependencies: - '@exodus/bytes': 1.14.1(@noble/hashes@1.8.0) + '@exodus/bytes': 1.15.0(@noble/hashes@1.8.0) transitivePeerDependencies: - '@noble/hashes' @@ -11302,7 +11366,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.1: + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -11361,7 +11425,7 @@ snapshots: from2: 2.3.0 p-is-promise: 3.0.0 - ip-address@10.0.1: {} + ip-address@10.1.0: {} ipaddr.js@1.9.1: {} @@ -11436,6 +11500,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.5.0 + is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 @@ -11540,7 +11608,7 @@ snapshots: is-what@5.5.0: {} - is-wsl@3.1.0: + is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 @@ -11550,7 +11618,7 @@ snapshots: isexe@2.0.0: {} - isexe@3.1.1: {} + isexe@3.1.5: {} issue-parser@7.0.1: dependencies: @@ -11601,13 +11669,13 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.5.0 + '@types/node': 25.6.0 merge-stream: 2.0.0 supports-color: 8.1.1 jiti@2.6.1: {} - jose@6.1.3: {} + jose@6.2.2: {} js-sha256@0.10.1: {} @@ -11619,24 +11687,24 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@28.1.0(@noble/hashes@1.8.0): + jsdom@29.0.2(@noble/hashes@1.8.0): dependencies: - '@acemir/cssom': 0.9.31 - '@asamuzakjp/dom-selector': 6.8.1 + '@asamuzakjp/css-color': 5.1.9 + '@asamuzakjp/dom-selector': 7.0.9 '@bramus/specificity': 2.4.2 - '@exodus/bytes': 1.14.1(@noble/hashes@1.8.0) - cssstyle: 6.1.0 + '@csstools/css-syntax-patches-for-csstree': 1.1.2(css-tree@3.2.1) + '@exodus/bytes': 1.15.0(@noble/hashes@1.8.0) + css-tree: 3.2.1 data-urls: 7.0.0(@noble/hashes@1.8.0) decimal.js: 10.6.0 html-encoding-sniffer: 6.0.0(@noble/hashes@1.8.0) - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 + lru-cache: 11.3.3 parse5: 8.0.0 saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 6.0.0 - undici: 7.22.0 + tough-cookie: 6.0.1 + undici: 7.24.7 w3c-xmlserializer: 5.0.0 webidl-conversions: 8.0.1 whatwg-mimetype: 5.0.0 @@ -11644,7 +11712,6 @@ snapshots: xml-name-validator: 5.0.0 transitivePeerDependencies: - '@noble/hashes' - - supports-color jsesc@3.1.0: {} @@ -11757,6 +11824,24 @@ snapshots: lines-and-columns@1.2.4: {} + lint-staged@16.4.0: + dependencies: + commander: 14.0.3 + listr2: 9.0.5 + picomatch: 4.0.4 + string-argv: 0.3.2 + tinyexec: 1.0.4 + yaml: 2.8.3 + + listr2@9.0.5: + dependencies: + cli-truncate: 5.2.0 + colorette: 2.0.20 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + load-json-file@4.0.0: dependencies: graceful-fs: 4.2.11 @@ -11801,6 +11886,14 @@ snapshots: chalk: 5.6.2 is-unicode-supported: 1.3.0 + log-update@6.1.0: + dependencies: + ansi-escapes: 7.2.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + long-timeout@0.1.1: {} longest-streak@3.1.0: {} @@ -11811,19 +11904,15 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.4: {} - - lru-cache@11.2.6: {} - - lru-cache@11.2.7: {} + lru-cache@11.3.3: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - lucide-react@0.577.0(react@19.2.4): + lucide-react@1.7.0(react@19.2.5): dependencies: - react: 19.2.4 + react: 19.2.5 luxon@3.7.2: {} @@ -11955,10 +12044,10 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - mdn-data@2.12.2: {} - mdn-data@2.23.0: {} + mdn-data@2.27.1: {} + media-typer@0.3.0: {} media-typer@1.1.0: {} @@ -12131,7 +12220,7 @@ snapshots: mimic-response@3.1.0: {} - minimatch@10.2.4: + minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 @@ -12158,24 +12247,24 @@ snapshots: ms@2.1.3: {} - msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3): + msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3): dependencies: - '@inquirer/confirm': 5.1.21(@types/node@25.5.0) - '@mswjs/interceptors': 0.40.0 + '@inquirer/confirm': 5.1.21(@types/node@25.5.2) + '@mswjs/interceptors': 0.41.3 '@open-draft/deferred-promise': 2.2.0 '@types/statuses': 2.0.6 cookie: 1.1.1 - graphql: 16.12.0 + graphql: 16.13.2 headers-polyfill: 4.0.3 is-node-process: 1.2.0 outvariant: 1.4.3 path-to-regexp: 6.3.0 picocolors: 1.1.1 - rettime: 0.7.0 + rettime: 0.10.1 statuses: 2.0.2 strict-event-emitter: 0.5.1 - tough-cookie: 6.0.0 - type-fest: 5.3.1 + tough-cookie: 6.0.1 + type-fest: 5.5.0 until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: @@ -12210,31 +12299,31 @@ snapshots: nerf-dart@1.0.0: {} - next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): dependencies: - '@next/env': 16.1.6 + '@next/env': 16.2.3 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001761 + baseline-browser-mapping: 2.10.18 + caniuse-lite: 1.0.30001787 postcss: 8.4.31 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - styled-jsx: 5.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + styled-jsx: 5.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react@19.2.5) optionalDependencies: - '@next/swc-darwin-arm64': 16.1.6 - '@next/swc-darwin-x64': 16.1.6 - '@next/swc-linux-arm64-gnu': 16.1.6 - '@next/swc-linux-arm64-musl': 16.1.6 - '@next/swc-linux-x64-gnu': 16.1.6 - '@next/swc-linux-x64-musl': 16.1.6 - '@next/swc-win32-arm64-msvc': 16.1.6 - '@next/swc-win32-x64-msvc': 16.1.6 + '@next/swc-darwin-arm64': 16.2.3 + '@next/swc-darwin-x64': 16.2.3 + '@next/swc-linux-arm64-gnu': 16.2.3 + '@next/swc-linux-arm64-musl': 16.2.3 + '@next/swc-linux-x64-gnu': 16.2.3 + '@next/swc-linux-x64-musl': 16.2.3 + '@next/swc-win32-arm64-msvc': 16.2.3 + '@next/swc-win32-x64-msvc': 16.2.3 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - node-abi@3.87.0: + node-abi@3.89.0: dependencies: semver: 7.7.4 @@ -12247,6 +12336,13 @@ snapshots: emojilib: 2.4.0 skin-tone: 2.0.0 + 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-fetch@3.3.2: dependencies: data-uri-to-buffer: 4.0.1 @@ -12260,7 +12356,7 @@ snapshots: node-mock-http@1.0.4: {} - node-releases@2.0.27: {} + node-releases@2.0.37: {} node-schedule@2.1.1: dependencies: @@ -12299,12 +12395,12 @@ snapshots: npm@11.7.0: {} - nuqs@2.8.9(next@16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(react@19.2.4): + nuqs@2.8.9(next@16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5): dependencies: '@standard-schema/spec': 1.0.0 - react: 19.2.4 + react: 19.2.5 optionalDependencies: - next: 16.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + next: 16.2.3(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) object-assign@4.1.1: {} @@ -12339,9 +12435,9 @@ snapshots: object.groupby@1.0.3: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 object.values@1.2.1: dependencies: @@ -12376,16 +12472,16 @@ snapshots: open@11.0.0: dependencies: - default-browser: 5.4.0 + default-browser: 5.5.0 define-lazy-prop: 3.0.0 is-in-ssh: 1.0.0 is-inside-container: 1.0.0 powershell-utils: 0.1.0 - wsl-utils: 0.3.0 + wsl-utils: 0.3.1 openapi3-ts@4.4.0: dependencies: - yaml: 2.8.2 + yaml: 2.8.3 optionator@0.9.4: dependencies: @@ -12418,7 +12514,7 @@ snapshots: log-symbols: 6.0.0 stdin-discarder: 0.2.2 string-width: 7.2.0 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 outvariant@1.4.3: {} @@ -12468,8 +12564,6 @@ snapshots: package-json-from-dist@1.0.1: {} - package-manager-detector@1.6.0: {} - parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -12538,12 +12632,12 @@ snapshots: path-scurry@2.0.2: dependencies: - lru-cache: 11.2.7 + lru-cache: 11.3.3 minipass: 7.1.3 path-to-regexp@6.3.0: {} - path-to-regexp@8.3.0: {} + path-to-regexp@8.4.2: {} path-type@4.0.0: {} @@ -12584,13 +12678,13 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.6: + postcss@8.5.8: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.8: + postcss@8.5.9: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -12606,8 +12700,8 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.87.0 - pump: 3.0.3 + node-abi: 3.89.0 + pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 tar-fs: 2.1.4 @@ -12665,7 +12759,7 @@ snapshots: dependencies: commander: 10.0.1 - pump@3.0.3: + pump@3.0.4: dependencies: end-of-stream: 1.4.5 once: 1.4.0 @@ -12678,6 +12772,14 @@ snapshots: dependencies: side-channel: 1.1.0 + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + + qs@6.15.1: + dependencies: + side-channel: 1.1.0 + queue-microtask@1.2.3: {} radix3@1.1.2: {} @@ -12695,7 +12797,7 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 unpipe: 1.0.0 rc@1.2.8: @@ -12705,9 +12807,9 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - react-dom@19.2.4(react@19.2.4): + react-dom@19.2.5(react@19.2.5): dependencies: - react: 19.2.4 + react: 19.2.5 scheduler: 0.27.0 react-is@16.13.1: {} @@ -12718,7 +12820,7 @@ snapshots: react-is@19.2.4: {} - react-markdown@9.1.0(@types/react@19.2.14)(react@19.2.4): + react-markdown@9.1.0(@types/react@19.2.14)(react@19.2.5): dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 @@ -12727,7 +12829,7 @@ snapshots: hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 mdast-util-to-hast: 13.2.1 - react: 19.2.4 + react: 19.2.5 remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 @@ -12736,27 +12838,27 @@ snapshots: transitivePeerDependencies: - supports-color - react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1): + react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 - react: 19.2.4 - use-sync-external-store: 1.6.0(react@19.2.4) + react: 19.2.5 + use-sync-external-store: 1.6.0(react@19.2.5) optionalDependencies: '@types/react': 19.2.14 redux: 5.0.1 react-refresh@0.18.0: {} - react-transition-group@4.4.5(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + react-transition-group@4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5): dependencies: '@babel/runtime': 7.29.2 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) - react@19.2.4: {} + react@19.2.5: {} read-package-up@11.0.0: dependencies: @@ -12775,7 +12877,7 @@ snapshots: '@types/normalize-package-data': 2.4.4 normalize-package-data: 8.0.0 parse-json: 8.3.0 - type-fest: 5.3.1 + type-fest: 5.5.0 unicorn-magic: 0.3.0 read-pkg@9.0.1: @@ -12814,21 +12916,21 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - recharts@3.8.1(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react-is@19.2.4)(react@19.2.4)(redux@5.0.1): + recharts@3.8.1(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react-is@19.2.4)(react@19.2.5)(redux@5.0.1): dependencies: - '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1))(react@19.2.4) + '@reduxjs/toolkit': 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1))(react@19.2.5) clsx: 2.1.1 decimal.js-light: 2.5.1 es-toolkit: 1.45.1 eventemitter3: 5.0.4 immer: 10.2.0 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) react-is: 19.2.4 - react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.4)(redux@5.0.1) + react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1) reselect: 5.1.1 tiny-invariant: 1.3.3 - use-sync-external-store: 1.6.0(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.5) victory-vendor: 37.3.6 transitivePeerDependencies: - '@types/react' @@ -12893,8 +12995,9 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve@1.22.11: + resolve@1.22.12: dependencies: + es-errors: 1.3.0 is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -12905,6 +13008,15 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 @@ -12915,39 +13027,41 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - rettime@0.7.0: {} + rettime@0.10.1: {} reusify@1.1.0: {} - rollup@4.60.0: + rfdc@1.4.1: {} + + rollup@4.60.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.0 - '@rollup/rollup-android-arm64': 4.60.0 - '@rollup/rollup-darwin-arm64': 4.60.0 - '@rollup/rollup-darwin-x64': 4.60.0 - '@rollup/rollup-freebsd-arm64': 4.60.0 - '@rollup/rollup-freebsd-x64': 4.60.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.0 - '@rollup/rollup-linux-arm-musleabihf': 4.60.0 - '@rollup/rollup-linux-arm64-gnu': 4.60.0 - '@rollup/rollup-linux-arm64-musl': 4.60.0 - '@rollup/rollup-linux-loong64-gnu': 4.60.0 - '@rollup/rollup-linux-loong64-musl': 4.60.0 - '@rollup/rollup-linux-ppc64-gnu': 4.60.0 - '@rollup/rollup-linux-ppc64-musl': 4.60.0 - '@rollup/rollup-linux-riscv64-gnu': 4.60.0 - '@rollup/rollup-linux-riscv64-musl': 4.60.0 - '@rollup/rollup-linux-s390x-gnu': 4.60.0 - '@rollup/rollup-linux-x64-gnu': 4.60.0 - '@rollup/rollup-linux-x64-musl': 4.60.0 - '@rollup/rollup-openbsd-x64': 4.60.0 - '@rollup/rollup-openharmony-arm64': 4.60.0 - '@rollup/rollup-win32-arm64-msvc': 4.60.0 - '@rollup/rollup-win32-ia32-msvc': 4.60.0 - '@rollup/rollup-win32-x64-gnu': 4.60.0 - '@rollup/rollup-win32-x64-msvc': 4.60.0 + '@rollup/rollup-android-arm-eabi': 4.60.1 + '@rollup/rollup-android-arm64': 4.60.1 + '@rollup/rollup-darwin-arm64': 4.60.1 + '@rollup/rollup-darwin-x64': 4.60.1 + '@rollup/rollup-freebsd-arm64': 4.60.1 + '@rollup/rollup-freebsd-x64': 4.60.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.1 + '@rollup/rollup-linux-arm-musleabihf': 4.60.1 + '@rollup/rollup-linux-arm64-gnu': 4.60.1 + '@rollup/rollup-linux-arm64-musl': 4.60.1 + '@rollup/rollup-linux-loong64-gnu': 4.60.1 + '@rollup/rollup-linux-loong64-musl': 4.60.1 + '@rollup/rollup-linux-ppc64-gnu': 4.60.1 + '@rollup/rollup-linux-ppc64-musl': 4.60.1 + '@rollup/rollup-linux-riscv64-gnu': 4.60.1 + '@rollup/rollup-linux-riscv64-musl': 4.60.1 + '@rollup/rollup-linux-s390x-gnu': 4.60.1 + '@rollup/rollup-linux-x64-gnu': 4.60.1 + '@rollup/rollup-linux-x64-musl': 4.60.1 + '@rollup/rollup-openbsd-x64': 4.60.1 + '@rollup/rollup-openharmony-arm64': 4.60.1 + '@rollup/rollup-win32-arm64-msvc': 4.60.1 + '@rollup/rollup-win32-ia32-msvc': 4.60.1 + '@rollup/rollup-win32-x64-gnu': 4.60.1 + '@rollup/rollup-win32-x64-msvc': 4.60.1 fsevents: 2.3.3 router@2.2.0: @@ -12956,7 +13070,7 @@ snapshots: depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.3.0 + path-to-regexp: 8.4.2 transitivePeerDependencies: - supports-color @@ -13095,33 +13209,32 @@ snapshots: setprototypeof@1.2.0: {} - shadcn@3.8.5(@types/node@25.5.0)(babel-plugin-macros@3.1.0)(typescript@5.9.3): + shadcn@4.2.0(@types/node@25.5.2)(babel-plugin-macros@3.1.0)(typescript@5.9.3): dependencies: - '@antfu/ni': 25.0.0 - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) - '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) - '@dotenvx/dotenvx': 1.51.2 - '@modelcontextprotocol/sdk': 1.27.0(zod@3.25.76) + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) + '@dotenvx/dotenvx': 1.61.0 + '@modelcontextprotocol/sdk': 1.29.0(zod@3.25.76) '@types/validate-npm-package-name': 4.0.2 - browserslist: 4.28.1 - commander: 14.0.2 - cosmiconfig: 9.0.0(typescript@5.9.3) - dedent: 1.7.1(babel-plugin-macros@3.1.0) + browserslist: 4.28.2 + commander: 14.0.3 + cosmiconfig: 9.0.1(typescript@5.9.3) + dedent: 1.7.2(babel-plugin-macros@3.1.0) deepmerge: 4.3.1 - diff: 8.0.2 + diff: 8.0.4 execa: 9.6.1 fast-glob: 3.3.3 - fs-extra: 11.3.3 + fs-extra: 11.3.4 fuzzysort: 3.1.0 https-proxy-agent: 7.0.6 kleur: 4.1.5 - msw: 2.12.4(@types/node@25.5.0)(typescript@5.9.3) + msw: 2.13.2(@types/node@25.5.2)(typescript@5.9.3) node-fetch: 3.3.2 open: 11.0.0 ora: 8.2.0 - postcss: 8.5.6 + postcss: 8.5.9 postcss-selector-parser: 7.1.1 prompts: 2.4.2 recast: 0.23.11 @@ -13131,7 +13244,7 @@ snapshots: tsconfig-paths: 4.2.0 validate-npm-package-name: 7.0.2 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.2(zod@3.25.76) transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' @@ -13141,7 +13254,7 @@ snapshots: sharp@0.34.5: dependencies: - '@img/colour': 1.0.0 + '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.7.4 optionalDependencies: @@ -13231,6 +13344,16 @@ snapshots: dependencies: unicode-emoji-modifier-base: 1.0.0 + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + sorted-array-functions@1.3.0: {} source-map-js@1.2.1: {} @@ -13292,6 +13415,8 @@ snapshots: strict-event-emitter@0.5.1: {} + string-argv@0.3.2: {} + string-byte-length@1.6.0: {} string-width@4.2.3: @@ -13303,14 +13428,19 @@ snapshots: string-width@7.2.0: dependencies: emoji-regex: 10.6.0 - get-east-asian-width: 1.4.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + string-width@8.2.0: + dependencies: + get-east-asian-width: 1.5.0 strip-ansi: 7.2.0 string.prototype.includes@2.0.1: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 define-properties: 1.2.1 - es-abstract: 1.24.1 + es-abstract: 1.24.2 string.prototype.matchall@4.0.12: dependencies: @@ -13345,7 +13475,7 @@ snapshots: string.prototype.trimend@1.0.9: dependencies: - call-bind: 1.0.8 + call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.1 @@ -13379,10 +13509,6 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -13407,10 +13533,10 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - styled-jsx@5.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react@19.2.4): + styled-jsx@5.1.6(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react@19.2.5): dependencies: client-only: 0.0.1 - react: 19.2.4 + react: 19.2.5 optionalDependencies: '@babel/core': 7.29.0 babel-plugin-macros: 3.1.0 @@ -13466,7 +13592,7 @@ snapshots: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.3 + pump: 3.0.4 tar-stream: 2.2.0 tar-stream@2.2.0: @@ -13542,18 +13668,20 @@ snapshots: tinyexec@1.0.4: {} - tinyglobby@0.2.15: + tinyexec@1.1.1: {} + + tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 tinyrainbow@3.1.0: {} - tldts-core@7.0.19: {} + tldts-core@7.0.28: {} - tldts@7.0.19: + tldts@7.0.28: dependencies: - tldts-core: 7.0.19 + tldts-core: 7.0.28 to-regex-range@5.0.1: dependencies: @@ -13563,9 +13691,9 @@ snapshots: toidentifier@1.0.1: {} - tough-cookie@6.0.0: + tough-cookie@6.0.1: dependencies: - tldts: 7.0.19 + tldts: 7.0.28 tr46@6.0.0: dependencies: @@ -13577,9 +13705,9 @@ snapshots: trough@2.2.0: {} - trpc-to-openapi@3.1.0(@trpc/server@11.10.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6): + trpc-to-openapi@3.2.0(@trpc/server@11.16.0(typescript@5.9.3))(zod-openapi@5.4.6(zod@4.3.6))(zod@4.3.6): dependencies: - '@trpc/server': 11.10.0(typescript@5.9.3) + '@trpc/server': 11.16.0(typescript@5.9.3) co-body: 6.2.0 h3: 1.15.1 openapi3-ts: 4.4.0 @@ -13588,7 +13716,7 @@ snapshots: optionalDependencies: '@rollup/rollup-linux-x64-gnu': 4.6.1 - ts-api-utils@2.4.0(typescript@5.9.3): + ts-api-utils@2.5.0(typescript@5.9.3): dependencies: typescript: 5.9.3 @@ -13645,6 +13773,10 @@ snapshots: dependencies: tagged-tag: 1.0.0 + type-fest@5.5.0: + dependencies: + tagged-tag: 1.0.0 + type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -13689,12 +13821,12 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript-eslint@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): + typescript-eslint@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/parser': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) - '@typescript-eslint/utils': 8.56.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.4(jiti@2.6.1) typescript: 5.9.3 transitivePeerDependencies: @@ -13718,15 +13850,15 @@ snapshots: undici-types@7.18.2: {} + undici-types@7.19.2: {} + undici-types@7.24.6: {} undici@5.29.0: dependencies: '@fastify/busboy': 2.1.1 - undici@7.16.0: {} - - undici@7.22.0: {} + undici@7.24.7: {} unicode-emoji-modifier-base@1.0.0: {} @@ -13803,9 +13935,9 @@ snapshots: until-async@3.0.2: {} - update-browserslist-db@1.2.3(browserslist@4.28.1): + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - browserslist: 4.28.1 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 @@ -13820,9 +13952,9 @@ snapshots: punycode: 1.4.1 qs: 6.14.0 - use-sync-external-store@1.6.0(react@19.2.4): + use-sync-external-store@1.6.0(react@19.2.5): dependencies: - react: 19.2.4 + react: 19.2.5 util-deprecate@1.0.2: {} @@ -13866,42 +13998,42 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): + vite-tsconfig-paths@6.1.1(typescript@5.9.3)(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.9.3) - vite: 7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) transitivePeerDependencies: - supports-color - typescript - vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2): + vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: - esbuild: 0.27.4 + esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.8 - rollup: 4.60.0 - tinyglobby: 0.2.15 + postcss: 8.5.9 + rollup: 4.60.1 + tinyglobby: 0.2.16 optionalDependencies: - '@types/node': 25.5.0 + '@types/node': 25.5.2 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.32.0 terser: 5.46.1 tsx: 4.21.0 - yaml: 2.8.2 + yaml: 2.8.3 - vitest@4.1.2(@types/node@25.5.0)(jsdom@28.1.0(@noble/hashes@1.8.0))(msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)): + vitest@4.1.4(@types/node@25.5.2)(@vitest/coverage-v8@4.1.4)(jsdom@29.0.2(@noble/hashes@1.8.0))(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)): dependencies: - '@vitest/expect': 4.1.2 - '@vitest/mocker': 4.1.2(msw@2.12.4(@types/node@25.5.0)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2)) - '@vitest/pretty-format': 4.1.2 - '@vitest/runner': 4.1.2 - '@vitest/snapshot': 4.1.2 - '@vitest/spy': 4.1.2 - '@vitest/utils': 4.1.2 + '@vitest/expect': 4.1.4 + '@vitest/mocker': 4.1.4(msw@2.13.2(@types/node@25.5.2)(typescript@5.9.3))(vite@7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)) + '@vitest/pretty-format': 4.1.4 + '@vitest/runner': 4.1.4 + '@vitest/snapshot': 4.1.4 + '@vitest/spy': 4.1.4 + '@vitest/utils': 4.1.4 es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -13910,14 +14042,15 @@ snapshots: picomatch: 4.0.4 std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.4 - tinyglobby: 0.2.15 + tinyexec: 1.1.1 + tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.0(@types/node@25.5.0)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.2) + vite: 7.3.0(@types/node@25.5.2)(jiti@2.6.1)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.5.0 - jsdom: 28.1.0(@noble/hashes@1.8.0) + '@types/node': 25.5.2 + '@vitest/coverage-v8': 4.1.4(vitest@4.1.4) + jsdom: 29.0.2(@noble/hashes@1.8.0) transitivePeerDependencies: - msw @@ -13952,7 +14085,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.16.0 acorn-import-phases: 1.0.4(acorn@8.16.0) - browserslist: 4.28.1 + browserslist: 4.28.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.20.1 es-module-lexer: 2.0.0 @@ -13978,7 +14111,7 @@ snapshots: whatwg-url@16.0.1(@noble/hashes@1.8.0): dependencies: - '@exodus/bytes': 1.14.1(@noble/hashes@1.8.0) + '@exodus/bytes': 1.15.0(@noble/hashes@1.8.0) tr46: 6.0.0 webidl-conversions: 8.0.1 transitivePeerDependencies: @@ -14025,13 +14158,23 @@ snapshots: gopd: 1.2.0 has-tostringtag: 1.0.2 + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + which@2.0.2: dependencies: isexe: 2.0.0 which@4.0.0: dependencies: - isexe: 3.1.1 + isexe: 3.1.5 why-is-node-running@2.3.0: dependencies: @@ -14062,11 +14205,11 @@ snapshots: wrappy@1.0.2: {} - ws@8.19.0: {} + ws@8.20.0: {} - wsl-utils@0.3.0: + wsl-utils@0.3.1: dependencies: - is-wsl: 3.1.0 + is-wsl: 3.1.1 powershell-utils: 0.1.0 xml-name-validator@5.0.0: {} @@ -14081,7 +14224,7 @@ snapshots: yaml@1.10.3: {} - yaml@2.8.2: {} + yaml@2.8.3: {} yargs-parser@20.2.9: {} @@ -14120,6 +14263,10 @@ snapshots: yocto-queue@0.1.0: {} + yocto-spinner@1.1.0: + dependencies: + yoctocolors: 2.1.2 + yoctocolors-cjs@2.1.3: {} yoctocolors@2.1.2: {} @@ -14128,18 +14275,14 @@ snapshots: dependencies: zod: 4.3.6 - zod-to-json-schema@3.25.0(zod@3.25.76): + zod-to-json-schema@3.25.1(zod@4.3.6): dependencies: - zod: 3.25.76 + zod: 4.3.6 - zod-to-json-schema@3.25.1(zod@3.25.76): + zod-to-json-schema@3.25.2(zod@3.25.76): dependencies: zod: 3.25.76 - zod-to-json-schema@3.25.1(zod@4.3.6): - dependencies: - zod: 4.3.6 - zod-validation-error@4.0.2(zod@4.3.6): dependencies: zod: 4.3.6 @@ -14148,12 +14291,12 @@ snapshots: zod@4.3.6: {} - zustand@4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.4): + zustand@4.5.7(@types/react@19.2.14)(immer@11.1.4)(react@19.2.5): dependencies: - use-sync-external-store: 1.6.0(react@19.2.4) + use-sync-external-store: 1.6.0(react@19.2.5) optionalDependencies: '@types/react': 19.2.14 immer: 11.1.4 - react: 19.2.4 + react: 19.2.5 zwitch@2.0.4: {} diff --git a/renovate.json b/renovate.json index 7377f0a6..c80ab2d0 100644 --- a/renovate.json +++ b/renovate.json @@ -5,6 +5,7 @@ ], "automerge": true, "automergeType": "branch", + "minimumReleaseAge": "3 days", "lockFileMaintenance": { "enabled": true, "automerge": true @@ -33,7 +34,6 @@ }, { "matchUpdateTypes": [ - "minor", "major" ], "automerge": false, @@ -132,6 +132,16 @@ "commitMessagePrefix": "chore(deps):", "commitMessageAction": "Update {{packageName}}" }, + { + "description": "Pin requires-python to pod range β€” Pod 3 is 3.9, Pod 4/5 is 3.10", + "matchPackageNames": [ + "python" + ], + "matchManagers": [ + "pep621" + ], + "allowedVersions": "<3.11" + }, { "description": "Pin scipy <1.16 β€” pod runs Python 3.10, scipy 1.16+ requires 3.11", "matchPackageNames": [ diff --git a/scripts/README.md b/scripts/README.md index ce811e11..3b6af7f1 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -21,25 +21,72 @@ curl -fsSL https://raw.githubusercontent.com/sleepypod/core/main/scripts/install This will: 1. **Pre-flight checks** - Verify disk space, network, dependencies -2. **Detect dac.sock** - Auto-detect hardware socket location -3. **Install Node.js 20** - Via nodesource repository -4. **Clone repository** - From GitHub main branch -5. **Install dependencies** - With `--frozen-lockfile` and `--ignore-scripts` for security -6. **Build application** - Next.js production build -7. **Database migrations** - Safe schema updates (not destructive push) +2. **Download code** - From GitHub tarball (or use `--local`) +3. **Detect pod generation** - Auto-detect dac.sock path and pod hardware (`scripts/pod/detect`) +4. **Install Node.js 22** - Binary download (no apt required) +5. **Install dependencies** - With `--frozen-lockfile` +6. **Build application** - Next.js production build (skipped if pre-built) +7. **Database migrations** - Run automatically on startup 8. **Create systemd service** - With auto-restart and hardening -9. **CLI shortcuts** - sp-status, sp-restart, sp-logs, sp-update -10. **Start scheduler** - Automated temperature/power/alarm jobs -11. **Optional SSH setup** - Interactive prompt for SSH on port 8822 (keys only) +9. **Install CLI tools** - From `scripts/bin/` to `/usr/local/bin/` +10. **Install uv** - Rust-based Python package manager (bypasses broken Yocto stdlib) +11. **Install biometrics modules** - `uv sync` for each module + systemd services +12. **Optional SSH setup** - Interactive prompt for SSH on port 8822 (keys only) + +### Install Flow + +```mermaid +flowchart TD + Start([curl install | bash]) --> Preflight[Pre-flight checks\ndisk, network, deps] + Preflight --> Download{Code source?} + + Download -->|--local| Local[Use code on disk] + Download -->|default| Release{CI release\navailable?} + Release -->|yes| Tarball[Download pre-built tarball] + Release -->|no| Source[Download source tarball\nfallback build on pod] + + Local --> Detect + Tarball --> Detect + Source --> Detect + + Detect[Detect pod generation\nscripts/pod/detect] --> Node[Install Node.js 22 + pnpm] + Node --> Deps[pnpm install --frozen-lockfile --prod] + Deps --> Build{.next exists?} + Build -->|yes| Skip[Skip build] + Build -->|no| BuildApp[pnpm build\n⚠️ needs ~1GB RAM] + Skip --> Env + BuildApp --> Env + + Env[Write .env\nDAC_SOCK_PATH, DATABASE_URL] --> DB[Backup existing DB\nMigrations run on startup] + DB --> Service[Create systemd service\nstart sleepypod] + Service --> CLI[Install CLI tools\nscripts/bin/ β†’ /usr/local/bin/] + + CLI --> UV{uv\navailable?} + UV -->|no| InstallUV[Install uv\ncurl astral.sh] + UV -->|yes| Modules + InstallUV --> Modules + + Modules[Install biometrics modules] --> UVSync[uv sync per module\ncreates .venv + installs deps] + UVSync --> ModService[Create module systemd services] + + ModService --> SSH{Interactive\nterminal?} + SkipBio --> SSH + SSH -->|yes| SSHSetup[Optional SSH setup\nport 8822, keys only] + SSH -->|no| Done + SSHSetup --> Done([Installation complete]) +``` ## CLI Commands -After installation: +After installation (installed from `scripts/bin/`): - `sp-status` - View service status -- `sp-restart` - Restart sleepypod service +- `sp-restart` - Restart sleepypod + reconnect frankenfirmware - `sp-logs` - View live logs -- `sp-update` - Update to latest version +- `sp-update` - Update to latest version from GitHub +- `sp-freesleep` - Switch to free-sleep (persists across reboots) +- `sp-sleepypod` - Switch to sleepypod (persists across reboots) +- `sp-uninstall` - Remove sleepypod and all related services ## Internet Control @@ -106,6 +153,34 @@ After installation, sleepypod provides: - **Health Monitoring** - Scheduler status and hardware connectivity checks - **Timezone Support** - Full timezone awareness for all schedules +## Script Structure + +``` +scripts/ +β”œβ”€β”€ install # Core orchestrator +β”œβ”€β”€ lib/ +β”‚ └── iptables-helpers # Shared WAN/iptables functions (sourced by sp-update) +β”œβ”€β”€ pod/ +β”‚ └── detect # Pod detection: DAC_SOCK_PATH, POD_GEN +β”œβ”€β”€ bin/ # CLI tools β€” copied to /usr/local/bin/ during install +β”‚ β”œβ”€β”€ sp-status +β”‚ β”œβ”€β”€ sp-restart +β”‚ β”œβ”€β”€ sp-logs +β”‚ β”œβ”€β”€ sp-update +β”‚ β”œβ”€β”€ sp-freesleep +β”‚ β”œβ”€β”€ sp-sleepypod +β”‚ └── sp-uninstall +β”œβ”€β”€ deploy # Dev deploy (build local, push to pod) +β”œβ”€β”€ push # Fast push (pre-built .next only) +└── internet-control # WAN block/unblock utility +``` + +## Python Environment (uv) + +Biometrics modules use [uv](https://docs.astral.sh/uv/) for Python environment management. uv is a Rust-based tool that creates virtualenvs and installs packages without relying on Python's stdlib (`ensurepip`, `pyexpat`, etc.) β€” which are broken on Pod 3/4 Yocto images. + +Each module has a `pyproject.toml` and `uv.lock`. The install script runs `uv sync` per module, which creates a `.venv` and installs locked dependencies. + ## File Locations - **Installation**: `/home/dac/sleepypod-core/` diff --git a/scripts/bin/sp-freesleep b/scripts/bin/sp-freesleep new file mode 100755 index 00000000..59bfff4a --- /dev/null +++ b/scripts/bin/sp-freesleep @@ -0,0 +1,18 @@ +#!/bin/bash +set -euo pipefail +echo "Switching to free-sleep..." +systemctl stop sleepypod.service 2>/dev/null || true +for svc in sleepypod-piezo-processor sleepypod-sleep-detector sleepypod-environment-monitor sleepypod-calibrator; do + systemctl stop "$svc.service" 2>/dev/null || true + systemctl disable "$svc.service" 2>/dev/null || true +done +# Persist across reboots +systemctl disable sleepypod.service 2>/dev/null || true +systemctl enable free-sleep.service free-sleep-stream.service 2>/dev/null || true +systemctl start free-sleep.service free-sleep-stream.service 2>/dev/null || true +sleep 2 +if systemctl is-active --quiet free-sleep.service; then + echo "βœ“ free-sleep is running on port 3000 (persists across reboots)" +else + echo "βœ— free-sleep failed to start β€” check: journalctl -u free-sleep.service" +fi diff --git a/scripts/bin/sp-logs b/scripts/bin/sp-logs new file mode 100755 index 00000000..8ba707fe --- /dev/null +++ b/scripts/bin/sp-logs @@ -0,0 +1,2 @@ +#!/bin/bash +journalctl -u sleepypod.service -f diff --git a/scripts/bin/sp-maintenance b/scripts/bin/sp-maintenance new file mode 100755 index 00000000..04cab0bf --- /dev/null +++ b/scripts/bin/sp-maintenance @@ -0,0 +1,52 @@ +#!/bin/bash +set -euo pipefail + +# sp-maintenance β€” housekeeping tasks run on every boot +# +# Called by sleepypod.service ExecStartPre so it runs before the app starts. +# Keeps disk usage in check on the pod's limited storage (~6GB). + +DATA_DIR="/persistent/sleepypod-data" +INSTALL_DIR="/home/dac/sleepypod-core" + +echo "=== SleepyPod Maintenance ===" + +# 1. Vacuum systemd journal (cap at 50MB) +if command -v journalctl &>/dev/null; then + JOURNAL_SIZE=$(du -sm /persistent/journal 2>/dev/null | awk '{print $1}' || echo 0) + if [ "$JOURNAL_SIZE" -gt 50 ]; then + echo "Vacuuming journal (${JOURNAL_SIZE}MB β†’ 50MB)..." + journalctl --vacuum-size=50M 2>/dev/null || true + fi +fi + +# 2. Clean stale temp files (leftover from failed updates, builds, etc.) +TEMP_CLEANED=0 +for d in /tmp/tmp.* /tmp/sleepypod-* /tmp/sp-* /tmp/core-*; do + if [ -e "$d" ]; then + rm -rf "$d" 2>/dev/null && TEMP_CLEANED=$((TEMP_CLEANED + 1)) + fi +done +[ "$TEMP_CLEANED" -gt 0 ] && echo "Cleaned $TEMP_CLEANED stale temp entries." + +# 3. Prune old database backups (keep 3 most recent) +if [ -d "$DATA_DIR" ]; then + BAK_COUNT=$(ls "$DATA_DIR"/sleepypod.db.bak.* 2>/dev/null | wc -l) + if [ "$BAK_COUNT" -gt 3 ]; then + PRUNE=$((BAK_COUNT - 3)) + ls -t "$DATA_DIR"/sleepypod.db.bak.* | tail -n "$PRUNE" | xargs rm -f + echo "Pruned $PRUNE old DB backups (kept 3)." + fi +fi + +# 4. Prune pnpm content-addressable store +if command -v pnpm &>/dev/null; then + STORE_SIZE=$(du -sm /home/root/.local/share/pnpm/store 2>/dev/null | awk '{print $1}' || echo 0) + if [ "$STORE_SIZE" -gt 100 ]; then + echo "Pruning pnpm store (${STORE_SIZE}MB)..." + pnpm store prune 2>/dev/null || true + fi +fi + +DISK_AVAIL=$(df -m "$INSTALL_DIR" | awk 'NR==2{print $4}') +echo "Maintenance complete. ${DISK_AVAIL}MB free." diff --git a/scripts/bin/sp-restart b/scripts/bin/sp-restart new file mode 100755 index 00000000..7225ac8d --- /dev/null +++ b/scripts/bin/sp-restart @@ -0,0 +1,26 @@ +#!/bin/bash +set -euo pipefail + +INSTALL_DIR="/home/dac/sleepypod-core" + +systemctl restart sleepypod.service + +# Wait for sleepypod to create dac.sock before killing frankenfirmware +DAC_SOCK=$(grep '^DAC_SOCK_PATH=' "$INSTALL_DIR/.env" 2>/dev/null | cut -d= -f2- || true) +if [ -z "$DAC_SOCK" ]; then + # Fall back to pod detection if .env doesn't have it + source "$INSTALL_DIR/scripts/pod/detect" 2>/dev/null || true + DAC_SOCK="${DAC_SOCK_PATH:-/persistent/deviceinfo/dac.sock}" +fi + +for i in $(seq 1 10); do + [ -S "$DAC_SOCK" ] && break + sleep 1 +done + +if [ -S "$DAC_SOCK" ]; then + # Kill frankenfirmware so its supervisor restarts it against the new dac.sock + pkill frankenfirmware 2>/dev/null || true +else + echo "Warning: dac.sock not found after 10s β€” skipping frankenfirmware restart" >&2 +fi diff --git a/scripts/bin/sp-sleepypod b/scripts/bin/sp-sleepypod new file mode 100755 index 00000000..31a8ce9d --- /dev/null +++ b/scripts/bin/sp-sleepypod @@ -0,0 +1,23 @@ +#!/bin/bash +set -euo pipefail +echo "Switching to sleepypod..." +systemctl stop free-sleep.service free-sleep-stream.service 2>/dev/null || true +# Persist across reboots +systemctl disable free-sleep.service free-sleep-stream.service 2>/dev/null || true +systemctl enable sleepypod.service 2>/dev/null || true +# Kill anything on port 3000 +if command -v fuser &>/dev/null; then + fuser -k 3000/tcp 2>/dev/null || true + sleep 1 +fi +systemctl restart sleepypod.service +for svc in sleepypod-piezo-processor sleepypod-sleep-detector sleepypod-environment-monitor sleepypod-calibrator; do + systemctl enable "$svc.service" 2>/dev/null || true + systemctl restart "$svc.service" 2>/dev/null || true +done +sleep 3 +if systemctl is-active --quiet sleepypod.service; then + echo "βœ“ sleepypod is running on port 3000 (persists across reboots)" +else + echo "βœ— sleepypod failed to start β€” check: sp-logs" +fi diff --git a/scripts/bin/sp-status b/scripts/bin/sp-status new file mode 100755 index 00000000..2e3d3882 --- /dev/null +++ b/scripts/bin/sp-status @@ -0,0 +1,2 @@ +#!/bin/bash +systemctl status sleepypod.service diff --git a/scripts/bin/sp-uninstall b/scripts/bin/sp-uninstall new file mode 100755 index 00000000..17c44eaf --- /dev/null +++ b/scripts/bin/sp-uninstall @@ -0,0 +1,137 @@ +#!/bin/bash +set -euo pipefail + +# sp-uninstall β€” remove sleepypod-core and all installed artifacts +# +# Usage: +# sp-uninstall # interactive (prompts for confirmation) +# sp-uninstall --force # skip confirmation +# sp-uninstall --keep-data # remove everything except databases + +if [ "$EUID" -ne 0 ]; then + echo "Error: Must run as root (use sudo)" >&2 + exit 1 +fi + +KEEP_DATA=false +FORCE=false +for arg in "$@"; do + case "$arg" in + --keep-data) KEEP_DATA=true ;; + --force) FORCE=true ;; + esac +done + +INSTALL_DIR="/home/dac/sleepypod-core" +DATA_DIR="/persistent/sleepypod-data" +MODULES_DIR="/opt/sleepypod/modules" + +if [ "$FORCE" != true ]; then + echo "This will remove sleepypod-core and all related services." + if [ "$KEEP_DATA" = true ]; then + echo "Databases at $DATA_DIR will be preserved." + else + echo "WARNING: Databases at $DATA_DIR will be DELETED." + fi + echo "" + read -rp "Continue? [y/N] " confirm + if [[ ! "$confirm" =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 + fi +fi + +echo "=== Uninstalling SleepyPod ===" + +# Stop and disable services +echo "Stopping services..." +SERVICES=( + sleepypod.service + sleepypod-piezo-processor.service + sleepypod-sleep-detector.service + sleepypod-environment-monitor.service + sleepypod-calibrator.service +) +for svc in "${SERVICES[@]}"; do + systemctl stop "$svc" 2>/dev/null || true + systemctl disable "$svc" 2>/dev/null || true + rm -f "/etc/systemd/system/$svc" +done +systemctl daemon-reload + +# Remove CLI tools +echo "Removing CLI tools..." +rm -f /usr/local/bin/sp-status +rm -f /usr/local/bin/sp-restart +rm -f /usr/local/bin/sp-logs +rm -f /usr/local/bin/sp-update +rm -f /usr/local/bin/sp-freesleep +rm -f /usr/local/bin/sp-sleepypod +rm -f /usr/local/bin/sp-uninstall + +# Remove Python modules +if [ -d "$MODULES_DIR" ]; then + echo "Removing Python modules..." + rm -rf "$MODULES_DIR" +fi +rm -rf /etc/sleepypod + +# Remove application code +if [ -d "$INSTALL_DIR" ]; then + echo "Removing application code..." + rm -rf "$INSTALL_DIR" +fi + +# Remove data (unless --keep-data) +if [ "$KEEP_DATA" = true ]; then + echo "Keeping databases at $DATA_DIR" +else + if [ -d "$DATA_DIR" ]; then + echo "Removing databases..." + rm -rf "$DATA_DIR" + fi +fi + +# Remove /etc/hosts telemetry block +if grep -q "# BEGIN sleepypod-telemetry-block" /etc/hosts 2>/dev/null && \ + grep -q "# END sleepypod-telemetry-block" /etc/hosts 2>/dev/null; then + echo "Removing /etc/hosts telemetry block..." + sed -i '/# BEGIN sleepypod-telemetry-block/,/# END sleepypod-telemetry-block/d' /etc/hosts +elif grep -q "# BEGIN sleepypod-telemetry-block" /etc/hosts 2>/dev/null; then + echo "Warning: Incomplete telemetry block in /etc/hosts (missing END marker), skipping removal" +fi + +# Restore iptables to allow all traffic (IPv4 and IPv6) +echo "Restoring iptables (allowing all traffic)..." +iptables -F 2>/dev/null || true +iptables -X 2>/dev/null || true +iptables -t nat -F 2>/dev/null || true +iptables -t nat -X 2>/dev/null || true +iptables -P INPUT ACCEPT 2>/dev/null || true +iptables -P OUTPUT ACCEPT 2>/dev/null || true +iptables -P FORWARD ACCEPT 2>/dev/null || true +ip6tables -F 2>/dev/null || true +ip6tables -X 2>/dev/null || true +ip6tables -t nat -F 2>/dev/null || true +ip6tables -t nat -X 2>/dev/null || true +ip6tables -P INPUT ACCEPT 2>/dev/null || true +ip6tables -P OUTPUT ACCEPT 2>/dev/null || true +ip6tables -P FORWARD ACCEPT 2>/dev/null || true +rm -f /etc/iptables/iptables.rules /etc/iptables/rules.v4 /etc/iptables/rules.v6 + +# Re-enable free-sleep if available +for fs_svc in free-sleep.service free-sleep-stream.service; do + if systemctl list-unit-files "$fs_svc" 2>/dev/null | grep -q "$fs_svc"; then + echo "Re-enabling $fs_svc..." + systemctl enable "$fs_svc" 2>/dev/null || true + systemctl start "$fs_svc" 2>/dev/null || true + fi +done + +echo "" +echo "=== SleepyPod uninstalled ===" +if [ "$KEEP_DATA" = true ]; then + echo "Databases preserved at: $DATA_DIR" +fi +echo "Note: Node.js and pnpm were left in place (/usr/local/lib/nodejs)" +echo "Note: SSH config changes (if any) were not reverted β€” edit /etc/ssh/sshd_config manually" diff --git a/scripts/bin/sp-update b/scripts/bin/sp-update new file mode 100755 index 00000000..ca4b44b8 --- /dev/null +++ b/scripts/bin/sp-update @@ -0,0 +1,274 @@ +#!/bin/bash +set -euo pipefail + +# Ensure /usr/local/bin is in PATH (Yocto's root SSH PATH is minimal) +case ":$PATH:" in + *:/usr/local/bin:*) ;; + *) export PATH="/usr/local/bin:$PATH" ;; +esac + +# sp-update β€” self-update sleepypod-core from GitHub via curl+tarball +# +# Usage: +# sp-update # update to latest main +# sp-update feat/alarms # update to a specific branch +# +# Handles iptables toggle automatically (opens WAN, downloads, closes WAN). +# No git required β€” uses GitHub's tarball API. + +INSTALL_DIR="/home/dac/sleepypod-core" +DATA_DIR="/persistent/sleepypod-data" +GITHUB_REPO="${SLEEPYPOD_GITHUB_REPO:-sleepypod/core}" +BRANCH="${1:-main}" + +echo "=== SleepyPod Update ===" +echo "Branch: $BRANCH" +echo "Repo: $GITHUB_REPO" +echo "" + +# Source shared iptables helpers (with inline fallback for transitional installs) +if [ -f "$INSTALL_DIR/scripts/lib/iptables-helpers" ]; then + source "$INSTALL_DIR/scripts/lib/iptables-helpers" +else + # Inline fallback β€” remove once all pods have scripts/lib/ + SAVED_IPTABLES="" + WAN_WAS_BLOCKED=false + wan_is_blocked() { + iptables -L OUTPUT -n 2>/dev/null | grep -q "DROP" 2>/dev/null + } + unblock_wan() { + echo "Opening WAN access..." + SAVED_IPTABLES="$(iptables-save 2>/dev/null || true)" + iptables -F && iptables -X && iptables -t nat -F && iptables -t nat -X + echo "WAN open." + } + restore_wan() { + if [ -n "$SAVED_IPTABLES" ]; then + echo "Restoring iptables rules..." + echo "$SAVED_IPTABLES" | iptables-restore 2>/dev/null || iptables -P OUTPUT DROP + else + iptables -P OUTPUT DROP + fi + echo "WAN blocked." + } +fi + +# Cleanup β€” re-block WAN, rollback code, and restart service on failure +ROLLBACK_READY=false +cleanup() { + local exit_code=$? + if [ "$WAN_WAS_BLOCKED" = true ]; then + restore_wan + WAN_WAS_BLOCKED=false + fi + if [ "$exit_code" -ne 0 ] && [ "$ROLLBACK_READY" = true ]; then + echo "Update failed, rolling back..." >&2 + if [ -d "$BACKUP_DIR" ]; then + find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 \ + ! -name 'node_modules' \ + ! -name '.env' \ + -exec rm -rf {} + + cp -r "$BACKUP_DIR/." "$INSTALL_DIR/" + # Restore CLI tools from rolled-back code + if [ -d "$INSTALL_DIR/scripts/bin" ]; then + for tool in "$INSTALL_DIR/scripts/bin"/sp-*; do + cp "$tool" /usr/local/bin/ + chmod +x "/usr/local/bin/$(basename "$tool")" + done + fi + echo "Previous code restored." + fi + if [ -f "$DATA_DIR/sleepypod.db.bak" ]; then + cp "$DATA_DIR/sleepypod.db.bak" "$DATA_DIR/sleepypod.db" + [ -f "$DATA_DIR/sleepypod.db-wal.bak" ] && cp "$DATA_DIR/sleepypod.db-wal.bak" "$DATA_DIR/sleepypod.db-wal" + [ -f "$DATA_DIR/sleepypod.db-shm.bak" ] && cp "$DATA_DIR/sleepypod.db-shm.bak" "$DATA_DIR/sleepypod.db-shm" + echo "Database restored." + fi + systemctl start sleepypod.service 2>/dev/null || true + elif [ "$exit_code" -ne 0 ]; then + echo "Update failed before rollback point, starting service with existing code..." >&2 + systemctl start sleepypod.service 2>/dev/null || true + fi +} +trap cleanup EXIT + +# Pre-flight: disk space +DISK_AVAIL=$(df -m "$INSTALL_DIR" | awk 'NR==2{print $4}') +if [ "$DISK_AVAIL" -lt 300 ]; then + echo "Error: Need 300MB free, only ${DISK_AVAIL}MB available" >&2 + exit 1 +fi + +# Open WAN if blocked +if wan_is_blocked; then + # Ensure /etc/hosts telemetry block is in place before opening WAN + if ! grep -q "# BEGIN sleepypod-telemetry-block" /etc/hosts 2>/dev/null; then + echo "Installing /etc/hosts telemetry block before opening WAN..." + "$INSTALL_DIR/scripts/internet-control" hosts-block 2>/dev/null || true + fi + WAN_WAS_BLOCKED=true + unblock_wan +fi + +# Check connectivity +if ! curl -sf --max-time 10 https://github.com > /dev/null 2>&1; then + echo "Error: Cannot reach GitHub. Check network." >&2 + exit 1 +fi + +# Backup current code for rollback (everything except node_modules) +BACKUP_DIR="/tmp/sleepypod-rollback" +rm -rf "$BACKUP_DIR" +mkdir -p "$BACKUP_DIR" +tar cf - -C "$INSTALL_DIR" --exclude='node_modules' . 2>/dev/null | tar xf - -C "$BACKUP_DIR" || true + +# Stop service before DB backup (ensures WAL is flushed) +systemctl stop sleepypod.service + +# Backup database (after stop so WAL/SHM are consistent) +if [ -f "$DATA_DIR/sleepypod.db" ]; then + cp "$DATA_DIR/sleepypod.db" "$DATA_DIR/sleepypod.db.bak" + [ -f "$DATA_DIR/sleepypod.db-wal" ] && cp "$DATA_DIR/sleepypod.db-wal" "$DATA_DIR/sleepypod.db-wal.bak" + [ -f "$DATA_DIR/sleepypod.db-shm" ] && cp "$DATA_DIR/sleepypod.db-shm" "$DATA_DIR/sleepypod.db-shm.bak" + echo "Database backed up." +fi + +# Download code β€” try CI release first (includes pre-built .next), fall back to source tarball +WORK_DIR=$(mktemp -d) +HAS_BUILD=false + +# Try CI release by tag β€” works for any branch with a GitHub Release (main, dev, etc.) +RELEASE_TAG="$BRANCH" +[ "$RELEASE_TAG" = "latest" ] && RELEASE_TAG="main" +echo "Checking for CI release (tag: $RELEASE_TAG)..." +RELEASE_URL=$(curl -sf "https://api.github.com/repos/${GITHUB_REPO}/releases/tags/${RELEASE_TAG}" \ + | grep -o '"browser_download_url": *"[^"]*sleepypod-core\.tar\.gz"' \ + | grep -o 'https://[^"]*' || true) + +if [ -n "$RELEASE_URL" ]; then + echo "Downloading CI release..." + if curl -fSL "$RELEASE_URL" | tar xz -C "$WORK_DIR"; then + HAS_BUILD=true + echo "CI release downloaded (pre-built)." + fi +fi + +if [ "$HAS_BUILD" = false ]; then + # Fall back to source tarball (no .next β€” would need build on pod) + # "latest" is not a real branch β€” use main for the source tarball + SOURCE_BRANCH="$BRANCH" + [ "$SOURCE_BRANCH" = "latest" ] && SOURCE_BRANCH="main" + echo "Downloading $SOURCE_BRANCH from GitHub..." + TARBALL_URL="https://github.com/${GITHUB_REPO}/archive/refs/heads/${SOURCE_BRANCH}.tar.gz" + if ! curl -fSL "$TARBALL_URL" | tar xz -C "$WORK_DIR"; then + echo "Error: Failed to download branch '$BRANCH'" >&2 + exit 1 + fi + # Source tarball extracts to a subdirectory (e.g., core-main/) + SUBDIR=$(ls "$WORK_DIR" | head -1) + if [ -z "$SUBDIR" ] || [ ! -d "$WORK_DIR/$SUBDIR" ]; then + echo "Error: Unexpected tarball structure in $WORK_DIR" >&2 + exit 1 + fi + # Move contents up so WORK_DIR is the root + mv "$WORK_DIR/$SUBDIR"/* "$WORK_DIR/$SUBDIR"/.[!.]* "$WORK_DIR/" 2>/dev/null || true + rmdir "$WORK_DIR/$SUBDIR" 2>/dev/null || true +fi + +# Clean old files (preserve node_modules, .env) +find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 \ + ! -name 'node_modules' \ + ! -name '.env' \ + -exec rm -rf {} + +ROLLBACK_READY=true + +# Move new code into place +cp -r "$WORK_DIR/." "$INSTALL_DIR/" +rm -rf "$WORK_DIR" +echo "Source updated." + +cd "$INSTALL_DIR" + +# Install production dependencies +pnpm install --frozen-lockfile --prod + +# Turbopack mangles native module requires with pnpm virtual store hashes +# that differ between CI (x86_64) and pod (arm64). Create symlinks so the +# hashed names in .next resolve to the real modules. +for chunk in "$INSTALL_DIR"/.next/server/chunks/*.js; do + [ -f "$chunk" ] || continue + for hashed in $(grep -o 'better-sqlite3-[a-f0-9]\{16\}' "$chunk" 2>/dev/null | sort -u); do + if [ ! -e "$INSTALL_DIR/node_modules/$hashed" ]; then + ln -sf better-sqlite3 "$INSTALL_DIR/node_modules/$hashed" + echo "Linked $hashed β†’ better-sqlite3" + fi + done + break # only need to scan one chunk +done + +# Build only if no pre-built .next exists +if [ ! -d "$INSTALL_DIR/.next" ]; then + echo "No pre-built .next found, building..." + pnpm install --frozen-lockfile + SP_BRANCH="$BRANCH" pnpm build +else + # Regenerate .git-info with the correct branch for pre-built releases + SP_BRANCH="$BRANCH" node scripts/generate-git-info.mjs 2>/dev/null || true +fi + +# Sync biometrics modules +if [ -d "$INSTALL_DIR/modules" ]; then + MODULES_DEST="/opt/sleepypod/modules" + mkdir -p "$MODULES_DEST" + if [ -d "$INSTALL_DIR/modules/common" ]; then + mkdir -p "$MODULES_DEST/common" + cp -r "$INSTALL_DIR/modules/common/." "$MODULES_DEST/common/" + fi + if ! command -v uv &>/dev/null; then + echo "Warning: uv not found β€” biometrics modules not synced. Re-run the full installer." + fi + for mod in piezo-processor sleep-detector environment-monitor calibrator; do + if [ -d "$INSTALL_DIR/modules/$mod" ]; then + mkdir -p "$MODULES_DEST/$mod" + cp -r "$INSTALL_DIR/modules/$mod/." "$MODULES_DEST/$mod/" + rm -rf "$MODULES_DEST/$mod/venv" + if command -v uv &>/dev/null; then + (cd "$MODULES_DEST/$mod" && uv sync --frozen --python python3) || true + fi + svc="sleepypod-$mod.service" + if [ -f "$MODULES_DEST/$mod/$svc" ]; then + cp "$MODULES_DEST/$mod/$svc" "/etc/systemd/system/$svc" + systemctl daemon-reload + systemctl enable "$svc" 2>/dev/null || true + systemctl restart "$svc" 2>/dev/null || true + fi + fi + done +fi + +# Install CLI tools from new source +if [ -d "$INSTALL_DIR/scripts/bin" ]; then + for tool in "$INSTALL_DIR/scripts/bin"/sp-*; do + cp "$tool" /usr/local/bin/ + chmod +x "/usr/local/bin/$(basename "$tool")" + done +fi + +# Re-block WAN before starting service +if [ "$WAN_WAS_BLOCKED" = true ]; then + restore_wan + WAN_WAS_BLOCKED=false +fi + +echo "Starting service..." +systemctl start sleepypod.service + +sleep 5 +if systemctl is-active --quiet sleepypod.service; then + echo "Update complete! Service is running." + exit 0 +fi + +# If we get here, service didn't start. +# Cleanup trap handles rollback β€” just exit with failure. +exit 1 diff --git a/scripts/deploy b/scripts/deploy index a6109d7e..d020fd32 100755 --- a/scripts/deploy +++ b/scripts/deploy @@ -14,8 +14,8 @@ set -euo pipefail # # What it does: # 1. Optionally checks out and pulls the specified branch -# 2. Builds the Next.js app locally (fast, full RAM) -# 3. Syncs source + .next build to the pod via tar+ssh +# 2. Builds the Next.js app locally (fast, full RAM) with output: 'standalone' +# 3. Syncs standalone build to the pod via tar+ssh # 4. Runs the install script on the pod (prod deps only, no build) # # Requirements: @@ -67,7 +67,7 @@ echo "" echo "Syncing to pod..." SSH_CMD="ssh -p $SSH_PORT -o ServerAliveInterval=30 $SSH_USER@$POD_IP" -# Clean old files on pod (preserve node_modules, .env) +# Clean old files on pod (preserve node_modules, .env, persistent data) $SSH_CMD "mkdir -p '$REMOTE_DIR' && \ find '$REMOTE_DIR' -mindepth 1 -maxdepth 1 \ ! -name 'node_modules' \ diff --git a/scripts/generate-git-info.mjs b/scripts/generate-git-info.mjs index 931edaff..e7be168e 100644 --- a/scripts/generate-git-info.mjs +++ b/scripts/generate-git-info.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node import { execSync } from 'node:child_process' -import { writeFileSync } from 'node:fs' +import { readFileSync, writeFileSync } from 'node:fs' const run = (cmd) => { try { @@ -11,13 +11,36 @@ const run = (cmd) => { } } +const branch = process.env.SP_BRANCH || run('git rev-parse --abbrev-ref HEAD') + try { - writeFileSync('.git-info', JSON.stringify({ - branch: run('git rev-parse --abbrev-ref HEAD'), - commitHash: run('git rev-parse --short HEAD'), - commitTitle: run('git log -1 --format=%s'), - buildDate: new Date().toISOString(), - })) + // If SP_BRANCH is set and a .git-info already exists (e.g. from CI build), + // patch the branch in-place to preserve the correct commitHash/commitTitle. + if (process.env.SP_BRANCH) { + let existing = {} + try { + existing = JSON.parse(readFileSync('.git-info', 'utf-8')) + } + catch (err) { + if (err?.code !== 'ENOENT') { + console.warn('Existing .git-info unreadable; regenerating metadata:', err.message) + } + } + writeFileSync('.git-info', JSON.stringify({ + branch, + commitHash: existing.commitHash || run('git rev-parse --short HEAD'), + commitTitle: existing.commitTitle || run('git log -1 --format=%s'), + buildDate: new Date().toISOString(), + })) + } + else { + writeFileSync('.git-info', JSON.stringify({ + branch, + commitHash: run('git rev-parse --short HEAD'), + commitTitle: run('git log -1 --format=%s'), + buildDate: new Date().toISOString(), + })) + } console.log('Generated .git-info') } catch (err) { diff --git a/scripts/install b/scripts/install index 19ebc9ca..e94687a3 100755 --- a/scripts/install +++ b/scripts/install @@ -62,6 +62,8 @@ trap 'cleanup $LINENO' EXIT # -------------------------------------------------------------------------------- # iptables helpers +# Canonical copy: scripts/lib/iptables-helpers (sourced by sp-update at runtime). +# Kept inline here because install needs them before the source tree is on disk. wan_is_blocked() { # Check if the OUTPUT chain has a DROP rule (WAN is blocked) @@ -150,6 +152,7 @@ esac INSTALL_DIR="/home/dac/sleepypod-core" GITHUB_REPO="${SLEEPYPOD_GITHUB_REPO:-sleepypod/core}" +SCRIPT_DEFAULT_BRANCH="__BRANCH__" # substituted at release time by .github/workflows/{release,dev-release}.yml # Lock file to prevent concurrent installs LOCKFILE="/var/run/sleepypod-install.lock" @@ -203,60 +206,8 @@ if [ "$DISK_AVAIL" -lt 500 ]; then exit 1 fi -# Detect dac.sock path β€” read from frank.sh (the firmware launcher) to match -# whatever path frankenfirmware actually connects to. Different pod hardware -# versions use different paths: -# Pod 3/4: /deviceinfo/dac.sock (tmpfs) -# Pod 5: /persistent/deviceinfo/dac.sock (ext4) -DAC_SOCK_PATH="" - -# Only accept known firmware-supported socket locations -is_supported_dac_path() { - case "$1" in - /deviceinfo/dac.sock|/persistent/deviceinfo/dac.sock) return 0 ;; - *) return 1 ;; - esac -} - -# Priority 1: Read from existing .env (validate against known paths) -if [ -f "$INSTALL_DIR/.env" ]; then - EXISTING_PATH=$(grep '^DAC_SOCK_PATH=' "$INSTALL_DIR/.env" 2>/dev/null | cut -d= -f2-) - if [ -n "$EXISTING_PATH" ] && is_supported_dac_path "$EXISTING_PATH"; then - DAC_SOCK_PATH="$EXISTING_PATH" - elif [ -n "$EXISTING_PATH" ]; then - echo "Warning: Ignoring unsupported DAC_SOCK_PATH from .env: $EXISTING_PATH" - fi -fi - -# Priority 2: Extract from frank.sh (what frankenfirmware actually uses) -if [ -z "$DAC_SOCK_PATH" ] && [ -f /opt/eight/bin/frank.sh ]; then - FRANK_PATH=$(grep 'DAC_SOCKET=' /opt/eight/bin/frank.sh 2>/dev/null \ - | grep -o '[^ ]*dac\.sock' | head -1 || true) - if [ -n "$FRANK_PATH" ]; then - DAC_SOCK_PATH="$FRANK_PATH" - echo "Detected dac.sock path from frank.sh: $DAC_SOCK_PATH" - fi -fi - -# Priority 3: Check known socket locations -if [ -z "$DAC_SOCK_PATH" ]; then - for sock_path in \ - /persistent/deviceinfo/dac.sock \ - /deviceinfo/dac.sock \ - ; do - if [ -S "$sock_path" ]; then - DAC_SOCK_PATH="$sock_path" - echo "Found active dac.sock at: $DAC_SOCK_PATH" - break - fi - done -fi - -# Priority 4: Default to /persistent/deviceinfo/dac.sock -if [ -z "$DAC_SOCK_PATH" ]; then - DAC_SOCK_PATH="/persistent/deviceinfo/dac.sock" - echo "Warning: dac.sock not found, using default: $DAC_SOCK_PATH" -fi +# DAC_SOCK_PATH detection is in scripts/pod/detect β€” sourced after code is on disk. +# Pre-create both possible parent directories so mkdir works before detection runs. # Create data directory with shared group for multi-user SQLite access. # The Node.js app (root) creates biometrics.db, but calibrator and @@ -270,7 +221,6 @@ echo "Creating data directory at $DATA_DIR..." # /run/dac is handled by RuntimeDirectory=dac in the service unit. mkdir -p /persistent/deviceinfo mkdir -p /deviceinfo -mkdir -p "$(dirname "$DAC_SOCK_PATH")" groupadd --force sleepypod if id dac &>/dev/null; then usermod -aG sleepypod dac @@ -369,7 +319,7 @@ if [ "$INSTALL_LOCAL" = true ]; then fi else # Download code via tarball (no git required on device) - DOWNLOAD_BRANCH="${INSTALL_BRANCH:-main}" + DOWNLOAD_BRANCH="${INSTALL_BRANCH:-$SCRIPT_DEFAULT_BRANCH}" echo "Downloading $DOWNLOAD_BRANCH from GitHub..." DOWNLOAD_DIR=$(mktemp -d) @@ -437,9 +387,20 @@ else echo "Source downloaded." fi +# Detect pod generation and DAC socket path (code is now on disk) +if [ -f "$INSTALL_DIR/scripts/pod/detect" ]; then + source "$INSTALL_DIR/scripts/pod/detect" +else + echo "Error: $INSTALL_DIR/scripts/pod/detect not found." >&2 + echo " The downloaded code does not match this install script." >&2 + echo " Try: curl -fsSL | sudo bash -s -- --branch $SCRIPT_DEFAULT_BRANCH" >&2 + exit 1 +fi +echo "Pod generation: $POD_GEN (DAC: $DAC_SOCK_PATH)" + # Install production dependencies (prebuild-install downloads prebuilt better-sqlite3) echo "Installing dependencies..." -pnpm install --frozen-lockfile --prod +CI=true pnpm install --frozen-lockfile --prod # Verify better-sqlite3 native module is ready if [ ! -f "$INSTALL_DIR/node_modules/better-sqlite3/build/Release/better_sqlite3.node" ] && \ @@ -455,13 +416,17 @@ if [ ! -f "$INSTALL_DIR/node_modules/better-sqlite3/build/Release/better_sqlite3 fi # Build application (skip if .next already exists β€” pre-built by deploy script or CI) +# Effective branch: --branch flag, download branch, or script default +EFFECTIVE_BRANCH="${INSTALL_BRANCH:-${DOWNLOAD_BRANCH:-$SCRIPT_DEFAULT_BRANCH}}" if [ -d "$INSTALL_DIR/.next" ]; then echo "Pre-built .next found, skipping build." + # Regenerate .git-info with the correct branch (CI builds may have a different branch baked in) + SP_BRANCH="$EFFECTIVE_BRANCH" node scripts/generate-git-info.mjs 2>/dev/null || true else echo "No pre-built .next found, building from source..." echo "Installing all dependencies (including devDependencies for build)..." - pnpm install --frozen-lockfile - pnpm build + CI=true pnpm install --frozen-lockfile + SP_BRANCH="$EFFECTIVE_BRANCH" pnpm build fi # Turbopack hashes the better-sqlite3 module name β€” create a symlink so the @@ -526,7 +491,9 @@ Environment="NODE_ENV=production" Environment="DATABASE_URL=file:$DATA_DIR/sleepypod.db" Environment="DAC_SOCK_PATH=$DAC_SOCK_PATH" ExecStartPre=/bin/sh -c '[ "$DAC_SOCK_PATH" != "/deviceinfo/dac.sock" ] && ln -sf $DAC_SOCK_PATH /deviceinfo/dac.sock 2>/dev/null; true' -ExecStart=/usr/local/bin/pnpm start +ExecStartPre=$INSTALL_DIR/scripts/bin/sp-maintenance +Environment="PATH=/usr/local/bin:/usr/bin:/bin" +ExecStart=/bin/sh -c 'if [ -f .next/standalone/server.js ]; then exec /usr/local/bin/node .next/standalone/server.js; else exec /usr/local/bin/pnpm start; fi' Restart=always RestartSec=10 @@ -542,6 +509,16 @@ ProtectKernelModules=true WantedBy=multi-user.target EOF +# Cap journal size to prevent unbounded log growth on limited disk +mkdir -p /etc/systemd/journald.conf.d +cat > /etc/systemd/journald.conf.d/sleepypod.conf << 'JOURNALD' +[Journal] +SystemMaxUse=50M +SystemKeepFree=200M +MaxFileSec=1week +JOURNALD +systemctl restart systemd-journald 2>/dev/null || true + # Reload systemd and enable service echo "Enabling service..." systemctl daemon-reload @@ -571,6 +548,31 @@ if command -v fuser &>/dev/null; then fi fi +# Install Avahi mDNS service file (must happen here, not at runtime, +# because ProtectSystem=strict makes /etc read-only for the service process). +# Port values are baked in at install time; sp-update re-runs this to refresh. +if [ -d /etc/avahi ]; then + mkdir -p /etc/avahi/services + TRPC_PORT=${PORT:-3000} + WS_PORT=${PIEZO_WS_PORT:-3001} + cat > /etc/avahi/services/sleepypod.service << AVAHI_EOF + + + + sleepypod + + _sleepypod._tcp + $TRPC_PORT + wsPort=$WS_PORT + version=1.0.0 + + +AVAHI_EOF + # Reload avahi to pick up the new service file + kill -HUP "$(pidof avahi-daemon 2>/dev/null)" 2>/dev/null || true + echo "Avahi mDNS service installed (_sleepypod._tcp on port $TRPC_PORT)" +fi + systemctl restart sleepypod.service # Wait for sleepypod to create dac.sock before killing frankenfirmware. @@ -606,10 +608,14 @@ MODULES_DEST="/opt/sleepypod/modules" mkdir -p "$MODULES_DEST" mkdir -p /etc/sleepypod/modules -# Check Python 3 is available -if ! command -v python3 &>/dev/null; then - echo "Warning: python3 not found β€” skipping biometrics module installation" - echo "Install Python 3 and re-run the installer to enable health monitoring" +# Install uv (Rust-based Python package manager β€” bypasses broken ensurepip/pyexpat on Yocto) +if ! command -v uv &>/dev/null; then + echo "Installing uv..." + curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin INSTALLER_NO_MODIFY_PATH=1 sh +fi + +if ! command -v uv &>/dev/null; then + echo "Warning: uv installation failed β€” skipping biometrics module installation" else install_module() { local name="$1" @@ -628,19 +634,14 @@ else mkdir -p "$dest" cp -r "$src/." "$dest/" - # Check if python3 venv is available (built-in on most distros, separate package on Debian) - if ! python3 -m venv --help >/dev/null 2>&1; then - echo "Warning: python3-venv not available, skipping module $name" - echo "On Debian/Ubuntu: apt-get install -y python3-venv" - return - fi + # Remove orphaned venv/ from pre-uv installs + rm -rf "$dest/venv" - # Create Python virtualenv and install deps - if [ ! -d "$dest/venv" ]; then - python3 -m venv "$dest/venv" - fi - "$dest/venv/bin/pip" install --quiet --upgrade pip - "$dest/venv/bin/pip" install --quiet -r "$dest/requirements.txt" + # Create Python environment with uv (no ensurepip/pyexpat needed) + (cd "$dest" && uv sync --frozen --python python3) || { + echo "Warning: uv sync failed for module $name" + return + } # Install systemd service if [ -f "$dest/$service" ]; then @@ -671,311 +672,15 @@ fi # files with 0644 between the initial fixup and the service restarts above. fix_db_permissions -# Create CLI shortcuts -echo "Creating CLI shortcuts..." -cat > /usr/local/bin/sp-status << 'EOF' -#!/bin/bash -systemctl status sleepypod.service -EOF - -cat > /usr/local/bin/sp-restart << RESTARTEOF -#!/bin/bash -set -euo pipefail -systemctl restart sleepypod.service -# Wait for sleepypod to create dac.sock before killing frankenfirmware -DAC_SOCK=\$(grep '^DAC_SOCK_PATH=' $INSTALL_DIR/.env 2>/dev/null | cut -d= -f2- || echo "") -[ -z "\$DAC_SOCK" ] && DAC_SOCK="$DAC_SOCK_PATH" -for i in \$(seq 1 10); do - [ -S "\$DAC_SOCK" ] && break - sleep 1 -done -if [ -S "\$DAC_SOCK" ]; then - # Kill frankenfirmware so its supervisor restarts it against the new dac.sock - pkill frankenfirmware 2>/dev/null || true -else - echo "Warning: dac.sock not found after 10s β€” skipping frankenfirmware restart" >&2 -fi -RESTARTEOF - -cat > /usr/local/bin/sp-logs << 'EOF' -#!/bin/bash -journalctl -u sleepypod.service -f -EOF - -cat > /usr/local/bin/sp-update << 'UPDATEEOF' -#!/bin/bash -set -euo pipefail - -# Ensure /usr/local/bin is in PATH (Yocto's root SSH PATH is minimal) -case ":$PATH:" in - *:/usr/local/bin:*) ;; - *) export PATH="/usr/local/bin:$PATH" ;; -esac - -# sp-update β€” self-update sleepypod-core from GitHub via curl+tarball -# -# Usage: -# sp-update # update to latest main -# sp-update feat/alarms # update to a specific branch -# -# Handles iptables toggle automatically (opens WAN, downloads, closes WAN). -# No git required β€” uses GitHub's tarball API. - -INSTALL_DIR="/home/dac/sleepypod-core" -DATA_DIR="/persistent/sleepypod-data" -GITHUB_REPO="${SLEEPYPOD_GITHUB_REPO:-sleepypod/core}" -BRANCH="${1:-main}" -IPTABLES="/usr/sbin/iptables" - -echo "=== SleepyPod Update ===" -echo "Branch: $BRANCH" -echo "Repo: $GITHUB_REPO" -echo "" - -# iptables helpers -wan_is_blocked() { - $IPTABLES -L OUTPUT -n 2>/dev/null | grep -q "DROP" 2>/dev/null -} - -SAVED_IPTABLES="" -WAN_WAS_BLOCKED=false - -unblock_wan() { - echo "Opening WAN access..." - SAVED_IPTABLES="$(/usr/sbin/iptables-save 2>/dev/null || true)" - $IPTABLES -F && $IPTABLES -X && $IPTABLES -t nat -F && $IPTABLES -t nat -X - echo "WAN open." -} - -restore_wan() { - if [ -n "$SAVED_IPTABLES" ]; then - echo "Restoring iptables rules..." - echo "$SAVED_IPTABLES" | /usr/sbin/iptables-restore 2>/dev/null || true - fi - echo "WAN blocked." -} - -# Cleanup β€” always re-block WAN and restart service on failure -cleanup() { - local exit_code=$? - if [ "$WAN_WAS_BLOCKED" = true ]; then - restore_wan - fi - if [ $exit_code -ne 0 ]; then - echo "Update failed, starting service with existing code..." >&2 - systemctl start sleepypod.service 2>/dev/null || true - fi -} -trap cleanup EXIT - -# Pre-flight: disk space -DISK_AVAIL=$(df -m "$INSTALL_DIR" | awk 'NR==2{print $4}') -if [ "$DISK_AVAIL" -lt 300 ]; then - echo "Error: Need 300MB free, only ${DISK_AVAIL}MB available" >&2 - exit 1 -fi - -# Open WAN if blocked -if wan_is_blocked; then - # Ensure /etc/hosts telemetry block is in place before opening WAN - if ! grep -q "# BEGIN sleepypod-telemetry-block" /etc/hosts 2>/dev/null; then - echo "Installing /etc/hosts telemetry block before opening WAN..." - "$INSTALL_DIR/scripts/internet-control" hosts-block 2>/dev/null || true - fi - WAN_WAS_BLOCKED=true - unblock_wan -fi - -# Check connectivity -if ! curl -sf --max-time 10 https://github.com > /dev/null 2>&1; then - echo "Error: Cannot reach GitHub. Check network." >&2 - exit 1 -fi - -# Backup database -if [ -f "$DATA_DIR/sleepypod.db" ]; then - cp "$DATA_DIR/sleepypod.db" "$DATA_DIR/sleepypod.db.bak" - echo "Database backed up." -fi - -# Backup current code for rollback (everything except node_modules) -BACKUP_DIR="/tmp/sleepypod-rollback" -rm -rf "$BACKUP_DIR" -mkdir -p "$BACKUP_DIR" -tar cf - -C "$INSTALL_DIR" --exclude='node_modules' . 2>/dev/null | tar xf - -C "$BACKUP_DIR" || true - -# Stop service -systemctl stop sleepypod.service - -# Download code β€” try CI release first (includes pre-built .next), fall back to source tarball -TMPDIR=$(mktemp -d) -HAS_BUILD=false - -if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "latest" ]; then - # Try latest GitHub Release (CI-built, includes .next) - echo "Checking for CI release..." - RELEASE_URL=$(curl -sf "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" \ - | grep -o '"browser_download_url": *"[^"]*sleepypod-core\.tar\.gz"' \ - | grep -o 'https://[^"]*' || true) - - if [ -n "$RELEASE_URL" ]; then - echo "Downloading CI release..." - if curl -fSL "$RELEASE_URL" | tar xz -C "$TMPDIR"; then - HAS_BUILD=true - echo "CI release downloaded (pre-built)." - fi - fi -fi - -if [ "$HAS_BUILD" = false ]; then - # Fall back to source tarball (no .next β€” would need build on pod) - echo "Downloading $BRANCH from GitHub..." - TARBALL_URL="https://github.com/${GITHUB_REPO}/archive/refs/heads/${BRANCH}.tar.gz" - if ! curl -fSL "$TARBALL_URL" | tar xz -C "$TMPDIR"; then - echo "Error: Failed to download branch '$BRANCH'" >&2 - exit 1 - fi - # Source tarball extracts to a subdirectory (e.g., core-main/) - SUBDIR=$(ls "$TMPDIR" | head -1) - if [ -z "$SUBDIR" ] || [ ! -d "$TMPDIR/$SUBDIR" ]; then - echo "Error: Unexpected tarball structure in $TMPDIR" >&2 - exit 1 - fi - # Move contents up so TMPDIR is the root - mv "$TMPDIR/$SUBDIR"/* "$TMPDIR/$SUBDIR"/.[!.]* "$TMPDIR/" 2>/dev/null || true - rmdir "$TMPDIR/$SUBDIR" 2>/dev/null || true -fi - -# Clean old files (preserve node_modules, .env) -find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 \ - ! -name 'node_modules' \ - ! -name '.env' \ - -exec rm -rf {} + - -# Move new code into place -cp -r "$TMPDIR/." "$INSTALL_DIR/" -rm -rf "$TMPDIR" -echo "Source updated." - -cd "$INSTALL_DIR" - -# Install prod deps (migrations run automatically on app startup via instrumentation.ts) -if pnpm install --frozen-lockfile --prod; then - - # Build only if no pre-built .next exists - if [ ! -d "$INSTALL_DIR/.next" ]; then - echo "No pre-built .next found, building..." - pnpm install --frozen-lockfile - pnpm build - fi - - # Sync biometrics modules - if [ -d "$INSTALL_DIR/modules" ]; then - MODULES_DEST="/opt/sleepypod/modules" - mkdir -p "$MODULES_DEST" - if [ -d "$INSTALL_DIR/modules/common" ]; then - mkdir -p "$MODULES_DEST/common" - cp -r "$INSTALL_DIR/modules/common/." "$MODULES_DEST/common/" - fi - for mod in piezo-processor sleep-detector environment-monitor calibrator; do - if [ -d "$INSTALL_DIR/modules/$mod" ]; then - mkdir -p "$MODULES_DEST/$mod" - cp -r "$INSTALL_DIR/modules/$mod/." "$MODULES_DEST/$mod/" - if [ ! -d "$MODULES_DEST/$mod/venv" ] && command -v python3 &>/dev/null; then - python3 -m venv "$MODULES_DEST/$mod/venv" 2>/dev/null || true - fi - if [ -d "$MODULES_DEST/$mod/venv" ] && [ -f "$MODULES_DEST/$mod/requirements.txt" ]; then - "$MODULES_DEST/$mod/venv/bin/pip" install --quiet -r "$MODULES_DEST/$mod/requirements.txt" 2>/dev/null || true - fi - svc="sleepypod-$mod.service" - if [ -f "$MODULES_DEST/$mod/$svc" ]; then - cp "$MODULES_DEST/$mod/$svc" "/etc/systemd/system/$svc" - systemctl daemon-reload - systemctl enable "$svc" 2>/dev/null || true - systemctl restart "$svc" 2>/dev/null || true - fi - fi - done - fi - - # Re-block WAN before starting service - if [ "$WAN_WAS_BLOCKED" = true ]; then - restore_wan - WAN_WAS_BLOCKED=false - fi - - echo "Starting service..." - systemctl start sleepypod.service - - sleep 5 - if systemctl is-active --quiet sleepypod.service; then - echo "Update complete! Service is running." - exit 0 - fi -fi - -# Rollback on failure β€” restore full previous code and database -echo "Update failed, restoring previous code..." >&2 -if [ -d "$BACKUP_DIR" ]; then - find "$INSTALL_DIR" -mindepth 1 -maxdepth 1 \ - ! -name 'node_modules' \ - ! -name '.env' \ - -exec rm -rf {} + - cp -r "$BACKUP_DIR/." "$INSTALL_DIR/" - echo "Previous code restored." -fi -if [ -f "$DATA_DIR/sleepypod.db.bak" ]; then - cp "$DATA_DIR/sleepypod.db.bak" "$DATA_DIR/sleepypod.db" - echo "Database restored." -fi -systemctl start sleepypod.service 2>/dev/null || true - -exit 1 -UPDATEEOF - -cat > /usr/local/bin/sp-freesleep << 'EOF' -#!/bin/bash -echo "Switching to free-sleep..." -systemctl stop sleepypod.service 2>/dev/null || true -for svc in sleepypod-piezo-processor sleepypod-sleep-detector sleepypod-environment-monitor sleepypod-calibrator; do - systemctl stop "$svc.service" 2>/dev/null || true -done -systemctl start free-sleep.service free-sleep-stream.service 2>/dev/null || true -sleep 2 -if systemctl is-active --quiet free-sleep.service; then - echo "βœ“ free-sleep is running on port 3000" -else - echo "βœ— free-sleep failed to start β€” check: journalctl -u free-sleep.service" -fi -EOF - -cat > /usr/local/bin/sp-sleepypod << 'EOF' -#!/bin/bash -echo "Switching to sleepypod..." -systemctl stop free-sleep.service free-sleep-stream.service 2>/dev/null || true -# Kill anything on port 3000 -if command -v fuser &>/dev/null; then - fuser -k 3000/tcp 2>/dev/null || true - sleep 1 -fi -systemctl restart sleepypod.service -for svc in sleepypod-piezo-processor sleepypod-sleep-detector sleepypod-environment-monitor sleepypod-calibrator; do - systemctl restart "$svc.service" 2>/dev/null || true -done -sleep 3 -if systemctl is-active --quiet sleepypod.service; then - echo "βœ“ sleepypod is running on port 3000" -else - echo "βœ— sleepypod failed to start β€” check: sp-logs" +# Install CLI tools from scripts/bin/ +if [ -d "$INSTALL_DIR/scripts/bin" ]; then + echo "Installing CLI tools..." + for tool in "$INSTALL_DIR/scripts/bin"/sp-*; do + [ -f "$tool" ] || continue + cp "$tool" /usr/local/bin/ + chmod +x "/usr/local/bin/$(basename "$tool")" + done fi -EOF - -chmod +x /usr/local/bin/sp-status -chmod +x /usr/local/bin/sp-restart -chmod +x /usr/local/bin/sp-logs -chmod +x /usr/local/bin/sp-update -chmod +x /usr/local/bin/sp-freesleep -chmod +x /usr/local/bin/sp-sleepypod # Optional SSH configuration if [ "$SKIP_SSH" = true ]; then @@ -1077,10 +782,13 @@ echo " - Sleep session detection and movement tracking (sleep-detector)" echo " - Ambient, bed zone, and freezer temperature monitoring (environment-monitor)" echo "" echo "CLI Commands:" -echo " sp-status - View service status" -echo " sp-restart - Restart service" -echo " sp-logs - View live logs" -echo " sp-update - Update to latest version" +echo " sp-status - View service status" +echo " sp-restart - Restart service" +echo " sp-logs - View live logs" +echo " sp-update - Update to latest version" +echo " sp-freesleep - Switch to free-sleep" +echo " sp-sleepypod - Switch to sleepypod" +echo " sp-uninstall - Remove sleepypod" echo "" echo "Files:" echo " Config DB: $DATA_DIR/sleepypod.db" diff --git a/scripts/internet-control b/scripts/internet-control index 4dbb8122..ac0a4a58 100755 --- a/scripts/internet-control +++ b/scripts/internet-control @@ -43,6 +43,21 @@ fi COMMAND="$1" +# --------------------------------------------------------------------------- +# Pod OS / iptables detection +# --------------------------------------------------------------------------- +# Pod 3/4 run Yocto (no apt, iptables at /sbin/iptables) +# Pod 5 runs Debian (has apt, iptables at /usr/sbin/iptables) +if [ -x /usr/bin/apt-get ]; then + POD_OS="debian" + IPTABLES="/usr/sbin/iptables" + IP6TABLES="/usr/sbin/ip6tables" +else + POD_OS="yocto" + IPTABLES="/sbin/iptables" + IP6TABLES="/sbin/ip6tables" +fi + # --------------------------------------------------------------------------- # /etc/hosts management for Eight Sleep telemetry blocking # --------------------------------------------------------------------------- @@ -153,52 +168,59 @@ case "$COMMAND" in echo "Local subnet: $LOCAL_SUBNET" echo "" - # Install iptables and iptables-persistent if not present - if ! command -v iptables &>/dev/null || ! dpkg -l | grep -q iptables-persistent; then - echo "Installing iptables..." - apt-get update - apt-get install -y iptables iptables-persistent + # Install iptables + persistence on Debian; on Yocto it must be pre-installed + if [ "$POD_OS" = "debian" ]; then + if ! command -v "$IPTABLES" &>/dev/null || ! dpkg -l | grep -q iptables-persistent; then + echo "Installing iptables..." + apt-get update + apt-get install -y iptables iptables-persistent + fi + else + if ! command -v "$IPTABLES" &>/dev/null; then + echo "Error: iptables not found at $IPTABLES. Cannot block WAN access." >&2 + exit 1 + fi fi # Create custom chain for SleepyPod rules (IPv4) - iptables -N SLEEPYPOD-BLOCK 2>/dev/null || iptables -F SLEEPYPOD-BLOCK - iptables -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true - iptables -I OUTPUT -j SLEEPYPOD-BLOCK + "$IPTABLES" -N SLEEPYPOD-BLOCK 2>/dev/null || "$IPTABLES" -F SLEEPYPOD-BLOCK + "$IPTABLES" -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true + "$IPTABLES" -I OUTPUT -j SLEEPYPOD-BLOCK # Allow loopback - iptables -A SLEEPYPOD-BLOCK -o lo -j ACCEPT + "$IPTABLES" -A SLEEPYPOD-BLOCK -o lo -j ACCEPT # Allow established/related connections - iptables -A SLEEPYPOD-BLOCK -m state --state ESTABLISHED,RELATED -j ACCEPT + "$IPTABLES" -A SLEEPYPOD-BLOCK -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow local network - iptables -A SLEEPYPOD-BLOCK -d "$LOCAL_SUBNET" -j ACCEPT + "$IPTABLES" -A SLEEPYPOD-BLOCK -d "$LOCAL_SUBNET" -j ACCEPT # Allow mDNS for local discovery - iptables -A SLEEPYPOD-BLOCK -d 224.0.0.251/32 -p udp --dport 5353 -j ACCEPT + "$IPTABLES" -A SLEEPYPOD-BLOCK -d 224.0.0.251/32 -p udp --dport 5353 -j ACCEPT # Block all other outbound traffic - iptables -A SLEEPYPOD-BLOCK -j DROP + "$IPTABLES" -A SLEEPYPOD-BLOCK -j DROP # Save IPv4 rules mkdir -p /etc/iptables - iptables-save > /etc/iptables/rules.v4 + "${IPTABLES}-save" > /etc/iptables/rules.v4 # Block IPv6 as well (prevents bypass) - if command -v ip6tables &>/dev/null; then + if command -v "$IP6TABLES" &>/dev/null; then echo "Configuring IPv6 rules..." - ip6tables -N SLEEPYPOD-BLOCK 2>/dev/null || ip6tables -F SLEEPYPOD-BLOCK - ip6tables -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true - ip6tables -I OUTPUT -j SLEEPYPOD-BLOCK + "$IP6TABLES" -N SLEEPYPOD-BLOCK 2>/dev/null || "$IP6TABLES" -F SLEEPYPOD-BLOCK + "$IP6TABLES" -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true + "$IP6TABLES" -I OUTPUT -j SLEEPYPOD-BLOCK - ip6tables -A SLEEPYPOD-BLOCK -o lo -j ACCEPT - ip6tables -A SLEEPYPOD-BLOCK -m state --state ESTABLISHED,RELATED -j ACCEPT - ip6tables -A SLEEPYPOD-BLOCK -d fe80::/10 -j ACCEPT - ip6tables -A SLEEPYPOD-BLOCK -j DROP + "$IP6TABLES" -A SLEEPYPOD-BLOCK -o lo -j ACCEPT + "$IP6TABLES" -A SLEEPYPOD-BLOCK -m state --state ESTABLISHED,RELATED -j ACCEPT + "$IP6TABLES" -A SLEEPYPOD-BLOCK -d fe80::/10 -j ACCEPT + "$IP6TABLES" -A SLEEPYPOD-BLOCK -j DROP - if command -v ip6tables-save &>/dev/null; then - ip6tables-save > /etc/iptables/rules.v6 + if command -v "${IP6TABLES}-save" &>/dev/null; then + "${IP6TABLES}-save" > /etc/iptables/rules.v6 fi fi @@ -211,7 +233,7 @@ case "$COMMAND" in echo " Layer 1: iptables DROP on all non-LAN outbound" echo " Layer 2: /etc/hosts null routes for *.8slp.net" echo " Local network ($LOCAL_SUBNET) is still accessible" - if command -v ip6tables &>/dev/null; then + if command -v "$IP6TABLES" &>/dev/null; then echo " IPv6 is also blocked" fi echo "" @@ -226,30 +248,30 @@ case "$COMMAND" in echo "" # Remove custom chains (IPv4) - if iptables -L SLEEPYPOD-BLOCK &>/dev/null; then + if "$IPTABLES" -L SLEEPYPOD-BLOCK &>/dev/null; then echo "Removing IPv4 firewall rules..." - iptables -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true - iptables -F SLEEPYPOD-BLOCK - iptables -X SLEEPYPOD-BLOCK + "$IPTABLES" -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true + "$IPTABLES" -F SLEEPYPOD-BLOCK + "$IPTABLES" -X SLEEPYPOD-BLOCK fi - iptables -P OUTPUT ACCEPT + "$IPTABLES" -P OUTPUT ACCEPT - if command -v iptables-save &>/dev/null; then + if command -v "${IPTABLES}-save" &>/dev/null; then mkdir -p /etc/iptables - iptables-save > /etc/iptables/rules.v4 + "${IPTABLES}-save" > /etc/iptables/rules.v4 fi # Remove IPv6 rules - if command -v ip6tables &>/dev/null && ip6tables -L SLEEPYPOD-BLOCK &>/dev/null; then + if command -v "$IP6TABLES" &>/dev/null && "$IP6TABLES" -L SLEEPYPOD-BLOCK &>/dev/null; then echo "Removing IPv6 firewall rules..." - ip6tables -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true - ip6tables -F SLEEPYPOD-BLOCK - ip6tables -X SLEEPYPOD-BLOCK - ip6tables -P OUTPUT ACCEPT + "$IP6TABLES" -D OUTPUT -j SLEEPYPOD-BLOCK 2>/dev/null || true + "$IP6TABLES" -F SLEEPYPOD-BLOCK + "$IP6TABLES" -X SLEEPYPOD-BLOCK + "$IP6TABLES" -P OUTPUT ACCEPT - if command -v ip6tables-save &>/dev/null; then - ip6tables-save > /etc/iptables/rules.v6 + if command -v "${IP6TABLES}-save" &>/dev/null; then + "${IP6TABLES}-save" > /etc/iptables/rules.v6 fi fi @@ -293,11 +315,11 @@ case "$COMMAND" in echo "" # Layer 1: iptables β€” check both OUTPUT and SLEEPYPOD-BLOCK chains - if iptables -L SLEEPYPOD-BLOCK -n 2>/dev/null | grep -q "DROP"; then - DROP_STATS=$(iptables -L SLEEPYPOD-BLOCK -n -v 2>/dev/null | grep "DROP" | awk '{print $1 " packets, " $2}') + if "$IPTABLES" -L SLEEPYPOD-BLOCK -n 2>/dev/null | grep -q "DROP"; then + DROP_STATS=$("$IPTABLES" -L SLEEPYPOD-BLOCK -n -v 2>/dev/null | grep "DROP" | awk '{print $1 " packets, " $2}') echo " Layer 1 (iptables): ACTIVE β€” SLEEPYPOD-BLOCK chain ($DROP_STATS blocked)" - elif iptables -L OUTPUT -n 2>/dev/null | grep -q "DROP"; then - DROP_STATS=$(iptables -L OUTPUT -n -v 2>/dev/null | grep "DROP" | awk '{print $1 " packets, " $2}') + elif "$IPTABLES" -L OUTPUT -n 2>/dev/null | grep -q "DROP"; then + DROP_STATS=$("$IPTABLES" -L OUTPUT -n -v 2>/dev/null | grep "DROP" | awk '{print $1 " packets, " $2}') echo " Layer 1 (iptables): ACTIVE β€” DROP rule in OUTPUT ($DROP_STATS blocked)" else echo " Layer 1 (iptables): INACTIVE β€” no DROP rule found" diff --git a/scripts/lib/iptables-helpers b/scripts/lib/iptables-helpers new file mode 100755 index 00000000..9b09e7a4 --- /dev/null +++ b/scripts/lib/iptables-helpers @@ -0,0 +1,78 @@ +#!/bin/bash +# Shared iptables helpers for WAN access control. +# Sourced by scripts/bin/sp-update at runtime. +# NOTE: scripts/install has an inline copy of these functions because it needs +# them before the source tree exists on disk (curl-pipe install case). + +SAVED_IPTABLES="" +WAN_WAS_BLOCKED=false + +wan_is_blocked() { + # Check if the OUTPUT chain has a DROP rule (WAN is blocked) + iptables -L OUTPUT -n 2>/dev/null | grep -q "DROP" 2>/dev/null +} + +unblock_wan() { + echo "Temporarily unblocking WAN access..." + SAVED_IPTABLES="$(iptables-save 2>/dev/null || true)" + iptables -F 2>/dev/null || true + iptables -X 2>/dev/null || true + iptables -t nat -F 2>/dev/null || true + iptables -t nat -X 2>/dev/null || true + echo "WAN unblocked." +} + +restore_wan() { + if [ -n "$SAVED_IPTABLES" ]; then + echo "Restoring saved iptables rules..." + echo "$SAVED_IPTABLES" | iptables-restore 2>/dev/null || block_wan + else + block_wan + fi +} + +block_wan() { + echo "Re-blocking WAN access..." + + # Flush first to avoid duplicate rules + iptables -F 2>/dev/null || true + iptables -X 2>/dev/null || true + iptables -t nat -F 2>/dev/null || true + iptables -t nat -X 2>/dev/null || true + + # Allow established connections + iptables -I INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + iptables -I OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT + + # Allow LAN traffic (RFC 1918) + for cidr in 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16; do + iptables -A INPUT -s "$cidr" -j ACCEPT + iptables -A OUTPUT -d "$cidr" -j ACCEPT + done + + # Allow NTP + iptables -I OUTPUT -p udp --dport 123 -j ACCEPT + iptables -I INPUT -p udp --sport 123 -j ACCEPT + + # Allow mDNS (Bonjour β€” iOS discovers pod via _sleepypod._tcp) + iptables -I OUTPUT -p udp --dport 5353 -j ACCEPT + iptables -I OUTPUT -p udp --sport 5353 -j ACCEPT + iptables -I INPUT -p udp --dport 5353 -j ACCEPT + iptables -I INPUT -p udp --sport 5353 -j ACCEPT + + # Allow loopback + iptables -A INPUT -i lo -j ACCEPT + iptables -A OUTPUT -o lo -j ACCEPT + + # Block everything else + iptables -A INPUT -j DROP + iptables -A OUTPUT -j DROP + + # Persist + if command -v iptables-save &>/dev/null; then + mkdir -p /etc/iptables + iptables-save > /etc/iptables/iptables.rules + fi + + echo "WAN blocked." +} diff --git a/scripts/pod/detect b/scripts/pod/detect new file mode 100755 index 00000000..f4fa9124 --- /dev/null +++ b/scripts/pod/detect @@ -0,0 +1,70 @@ +#!/bin/bash +# Pod detection β€” determines DAC socket path and pod generation. +# Sourced by scripts/install (after code is on disk) and usable by CLI tools. +# +# Sets in caller's scope: INSTALL_DIR, DATA_DIR, DAC_SOCK_PATH, POD_GEN +# +# POD_GEN is "3_4" or "5" β€” enough for install-time branching. +# Precise pod version (H00/I00/J00) is only available at runtime +# via the hardware sensor label (see src/hardware/responseParser.ts). + +INSTALL_DIR="${INSTALL_DIR:-/home/dac/sleepypod-core}" +DATA_DIR="${DATA_DIR:-/persistent/sleepypod-data}" + +# Only accept known firmware-supported socket locations +is_supported_dac_path() { + case "$1" in + /deviceinfo/dac.sock|/persistent/deviceinfo/dac.sock) return 0 ;; + *) return 1 ;; + esac +} + +detect_dac_sock() { + local path="" + + # Priority 1: Read from existing .env (validate against known paths) + if [ -f "$INSTALL_DIR/.env" ]; then + path=$(grep '^DAC_SOCK_PATH=' "$INSTALL_DIR/.env" 2>/dev/null | cut -d= -f2-) + if [ -n "$path" ] && is_supported_dac_path "$path"; then + echo "$path" + return + elif [ -n "$path" ]; then + echo "Warning: Ignoring unsupported DAC_SOCK_PATH from .env: $path" >&2 + fi + fi + + # Priority 2: Extract from frank.sh (what frankenfirmware actually uses) + if [ -f /opt/eight/bin/frank.sh ]; then + path=$(grep 'DAC_SOCKET=' /opt/eight/bin/frank.sh 2>/dev/null \ + | grep -o '[^ ]*dac\.sock' | head -1 || true) + if [ -n "$path" ] && is_supported_dac_path "$path"; then + echo "Detected dac.sock path from frank.sh: $path" >&2 + echo "$path" + return + elif [ -n "$path" ]; then + echo "Warning: Ignoring unsupported DAC_SOCK_PATH from frank.sh: $path" >&2 + fi + fi + + # Priority 3: Check known socket locations + for sock in /persistent/deviceinfo/dac.sock /deviceinfo/dac.sock; do + if [ -S "$sock" ]; then + echo "Found active dac.sock at: $sock" >&2 + echo "$sock" + return + fi + done + + # Priority 4: Default to Pod 5 path + echo "Warning: dac.sock not found, using default: /persistent/deviceinfo/dac.sock" >&2 + echo "/persistent/deviceinfo/dac.sock" +} + +DAC_SOCK_PATH="$(detect_dac_sock)" + +# Derive pod generation from socket path +case "$DAC_SOCK_PATH" in + /persistent/deviceinfo/dac.sock) POD_GEN="5" ;; + /deviceinfo/dac.sock) POD_GEN="3_4" ;; + *) POD_GEN="5" ;; +esac diff --git a/src/components/BottomNav/BottomNav.tsx b/src/components/BottomNav/BottomNav.tsx index 6c760346..45364331 100644 --- a/src/components/BottomNav/BottomNav.tsx +++ b/src/components/BottomNav/BottomNav.tsx @@ -2,10 +2,11 @@ import { msg } from '@lingui/core/macro' import { useLingui } from '@lingui/react' -import { Activity, BarChart3, Calendar, Radio, Settings, Thermometer } from 'lucide-react' +import { Activity, BarChart3, Calendar, Radio, Thermometer } from 'lucide-react' import Link from 'next/link' import { usePathname } from 'next/navigation' import clsx from 'clsx' +import { useScheduleActive } from '@/src/hooks/useScheduleActive' const tabs = [ { id: 'temp', icon: Thermometer, label: msg`Temp`, href: '/' }, @@ -22,6 +23,7 @@ const tabs = [ export const BottomNav = () => { const { i18n } = useLingui() const pathname = usePathname() + const { isActive: scheduleActive } = useScheduleActive() // Extract the path segment after /[lang]/ to determine active tab const getIsActive = (href: string) => { @@ -47,13 +49,18 @@ export const BottomNav = () => { href={`/${lang}${tab.href}`} className="group flex min-h-[44px] min-w-0 flex-1 flex-col items-center justify-center gap-0.5 sm:gap-1" > - + + {tab.id === 'schedule' && scheduleActive && ( + )} - /> + { diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 6360db23..9538e681 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,5 +1,6 @@ 'use client' +import Link from 'next/link' import { Plane, Settings } from 'lucide-react' import { useSide } from '@/src/providers/SideProvider' import { useSideNames } from '@/src/hooks/useSideNames' @@ -29,13 +30,13 @@ export const Header = () => { )} - - + ) } diff --git a/src/components/MovementChart/MovementChart.tsx b/src/components/MovementChart/MovementChart.tsx index b2b77335..3d47825d 100644 --- a/src/components/MovementChart/MovementChart.tsx +++ b/src/components/MovementChart/MovementChart.tsx @@ -220,8 +220,8 @@ export function MovementChart({ dualSide = false, hideNav = false }: MovementCha } ) - const records = (movementData ?? []) as MovementRecord[] - const otherRecords = (otherMovementData ?? []) as MovementRecord[] + const records = useMemo(() => (movementData ?? []) as MovementRecord[], [movementData]) + const otherRecords = useMemo(() => (otherMovementData ?? []) as MovementRecord[], [otherMovementData]) // Compute total sleep duration for the week const totalSleepSeconds = useMemo(() => { diff --git a/src/components/Schedule/AICurveWizard.tsx b/src/components/Schedule/AICurveWizard.tsx deleted file mode 100644 index 1da41a03..00000000 --- a/src/components/Schedule/AICurveWizard.tsx +++ /dev/null @@ -1,923 +0,0 @@ -'use client' - -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { - X, - Sparkles, - Copy, - Check, - Share2, - ClipboardPaste, - AlertTriangle, - ChevronLeft, - ChevronRight, - Loader2, - Trash2, - Plus, - Minus, - Save, -} from 'lucide-react' -import { cn } from '@/lib/utils' -import { trpc } from '@/src/utils/trpc' -import { - generatePrompt, - parseAIResponse, - EXAMPLE_SUGGESTIONS, - loadTemplates, - saveTemplate, - deleteTemplate, -} from '@/src/lib/sleepCurve/curvePrompt' -import type { GeneratedCurve, CurveTemplate, ParseResult } from '@/src/lib/sleepCurve/curvePrompt' -import { timeStringToMinutes } from '@/src/lib/sleepCurve/generate' -import type { CurvePoint } from '@/src/lib/sleepCurve/types' -import { CurveChart } from './CurveChart' -import { PhaseLegend } from './PhaseLegend' -import type { DayOfWeek } from './DaySelector' - -type Side = 'left' | 'right' - -interface AICurveWizardProps { - open: boolean - onClose: () => void - side: Side - selectedDays: Set - onApplied?: (config: { - setPoints: Array<{ time: string, tempF: number }> - bedtime: string - wakeTime: string - }) => void -} - -type Step = 0 | 1 | 2 | 3 - -const STEP_LABELS = ['Describe', 'Review', 'Import', 'Preview'] - -// ─── Main Component ────────────────────────────────────────────────── - -export function AICurveWizard({ open, onClose, side, selectedDays, onApplied }: AICurveWizardProps) { - const [step, setStep] = useState(0) - const [highestStep, setHighestStep] = useState(0) - - // Step 1: Describe - const [preferences, setPreferences] = useState('') - - // Step 2: Review - const [prompt, setPrompt] = useState('') - const [copied, setCopied] = useState(false) - - // Step 3: Import - const [jsonInput, setJsonInput] = useState('') - const [parseResult, setParseResult] = useState(null) - - // Step 4: Preview - const [curve, setCurve] = useState(null) - const [editablePoints, setEditablePoints] = useState>([]) - const [applying, setApplying] = useState(false) - const [applied, setApplied] = useState(false) - const [savedTemplates, setSavedTemplates] = useState([]) - - // tRPC - const utils = trpc.useUtils() - const createTempSchedule = trpc.schedules.createTemperatureSchedule.useMutation() - const deleteTempSchedule = trpc.schedules.deleteTemperatureSchedule.useMutation() - const createPowerSchedule = trpc.schedules.createPowerSchedule.useMutation() - const deletePowerSchedule = trpc.schedules.deletePowerSchedule.useMutation() - - // Load templates on mount - useEffect(() => { - if (open) { - setSavedTemplates(loadTemplates()) - } - }, [open]) - - // Reset on close - useEffect(() => { - if (!open) { - setStep(0) - setHighestStep(0) - setPreferences('') - setPrompt('') - setCopied(false) - setJsonInput('') - setParseResult(null) - setCurve(null) - setEditablePoints([]) - setApplying(false) - setApplied(false) - } - }, [open]) - - // Auto-parse JSON input (debounced) - useEffect(() => { - if (!jsonInput.trim()) { - setParseResult(null) - return - } - const timer = setTimeout(() => { - const result = parseAIResponse(jsonInput) - setParseResult(result) - if (result.success) { - setCurve(result.curve) - setEditablePoints( - Object.entries(result.curve.points) - .map(([time, tempF]) => ({ time, tempF })) - .sort((a, b) => a.time.localeCompare(b.time)), - ) - } - }, 500) - return () => clearTimeout(timer) - }, [jsonInput]) - - // ── Step Navigation ── - - const goNext = useCallback(() => { - if (step === 0) { - // Generate prompt - const p = generatePrompt(preferences) - setPrompt(p) - const next: Step = 1 - setStep(next) - setHighestStep(prev => Math.max(prev, next) as Step) - } - else if (step === 1) { - const next: Step = 2 - setStep(next) - setHighestStep(prev => Math.max(prev, next) as Step) - } - else if (step === 2 && parseResult?.success) { - const next: Step = 3 - setStep(next) - setHighestStep(prev => Math.max(prev, next) as Step) - } - }, [step, preferences, parseResult]) - - const goBack = useCallback(() => { - if (step > 0) setStep((step - 1) as Step) - }, [step]) - - const goToStep = useCallback((target: Step) => { - if (target <= highestStep) setStep(target) - }, [highestStep]) - - // Skip directly to Import (step 2) β€” user already has JSON - const handleSkipToImport = useCallback(() => { - setStep(2) - setHighestStep(prev => Math.max(prev, 2) as Step) - }, []) - - // ── Actions ── - - const handleCopy = useCallback(async () => { - // Try clipboard API first (works on HTTPS / localhost) - if (navigator.clipboard?.writeText) { - try { - await navigator.clipboard.writeText(prompt) - setCopied(true) - setTimeout(() => setCopied(false), 2000) - return - } - catch { /* fall through */ } - } - // Fallback: select the text in the prompt display so user can Cmd+C / long-press copy - const el = document.getElementById('ai-prompt-text') - if (el) { - const range = document.createRange() - range.selectNodeContents(el) - const sel = window.getSelection() - sel?.removeAllRanges() - sel?.addRange(range) - } - setCopied(true) - setTimeout(() => setCopied(false), 2000) - }, [prompt]) - - const handleShare = useCallback(async () => { - if (navigator.share) { - try { - await navigator.share({ text: prompt }) - } - catch { /* user cancelled */ } - } - }, [prompt]) - - const canShare = typeof navigator !== 'undefined' && !!navigator.share - - const handlePaste = useCallback(async () => { - if (navigator.clipboard?.readText) { - try { - const text = await navigator.clipboard.readText() - setJsonInput(text) - return - } - catch { /* fall through */ } - } - // Can't read clipboard over HTTP β€” focus the textarea so user can paste manually - }, []) - - const handleLoadTemplate = useCallback((template: CurveTemplate) => { - setCurve(template) - setEditablePoints( - Object.entries(template.points) - .map(([time, tempF]) => ({ time, tempF })) - .sort((a, b) => a.time.localeCompare(b.time)), - ) - setStep(3) - setHighestStep(3) - }, []) - - const handleDeleteTemplate = useCallback((name: string) => { - deleteTemplate(name) - setSavedTemplates(loadTemplates()) - }, []) - - const handleSaveTemplate = useCallback(() => { - if (!curve) return - const pointsObj: Record = {} - for (const p of editablePoints) pointsObj[p.time] = p.tempF - const updated: GeneratedCurve = { ...curve, points: pointsObj } - saveTemplate(updated) - setSavedTemplates(loadTemplates()) - }, [curve, editablePoints]) - - // Set point editing - const updatePoint = useCallback((idx: number, field: 'time' | 'tempF', value: string | number) => { - setEditablePoints((prev) => { - const next = [...prev] - if (field === 'time') next[idx] = { ...next[idx], time: value as string } - else next[idx] = { ...next[idx], tempF: Math.max(55, Math.min(110, value as number)) } - return next.sort((a, b) => a.time.localeCompare(b.time)) - }) - }, []) - - const addPoint = useCallback(() => { - setEditablePoints((prev) => { - const last = prev[prev.length - 1] - const newTime = last ? incrementTime(last.time, 15) : '22:00' - return [...prev, { time: newTime, tempF: 78 }].sort((a, b) => a.time.localeCompare(b.time)) - }) - }, []) - - const removePoint = useCallback((idx: number) => { - setEditablePoints((prev) => { - if (prev.length <= 3) return prev - return prev.filter((_, i) => i !== idx) - }) - }, []) - - // Apply curve to schedule - const handleApply = useCallback(async () => { - if (!curve || editablePoints.length < 3) return - setApplying(true) - - try { - const daysArray = Array.from(selectedDays) - - for (const day of daysArray) { - const existing = await utils.schedules.getByDay.fetch({ side, dayOfWeek: day }) - - await Promise.all([ - ...existing.temperature.map((s: { id: number }) => - deleteTempSchedule.mutateAsync({ id: s.id }), - ), - ...existing.power.map((s: { id: number }) => - deletePowerSchedule.mutateAsync({ id: s.id }), - ), - ]) - - await Promise.all([ - ...editablePoints.map(p => - createTempSchedule.mutateAsync({ - side, - dayOfWeek: day, - time: p.time, - temperature: p.tempF, - enabled: true, - }), - ), - createPowerSchedule.mutateAsync({ - side, - dayOfWeek: day, - onTime: curve.bedtime, - offTime: curve.wake, - onTemperature: editablePoints[0]?.tempF ?? 78, - enabled: true, - }) as Promise, - ]) - } - - await utils.schedules.invalidate() - setApplied(true) - onApplied?.({ - setPoints: editablePoints, - bedtime: curve.bedtime, - wakeTime: curve.wake, - }) - setTimeout(() => onClose(), 1500) - } - catch (err) { - console.error('Failed to apply AI curve:', err) - } - finally { - setApplying(false) - } - }, [curve, editablePoints, selectedDays, side, utils, createTempSchedule, deleteTempSchedule, createPowerSchedule, deletePowerSchedule, onApplied, onClose]) - - // Temp range for display - const tempRange = useMemo(() => { - if (editablePoints.length === 0) return { min: 55, max: 110 } - const temps = editablePoints.map(p => p.tempF) - return { min: Math.min(...temps), max: Math.max(...temps) } - }, [editablePoints]) - - // Convert editable set points β†’ CurvePoint[] for chart - const chartData = useMemo(() => { - if (!curve || editablePoints.length < 2) return null - const btMin = timeStringToMinutes(curve.bedtime) - - // Compute minutesFromBedtime for each point, then sort by that (not by time string) - const withRelative = editablePoints.map((p) => { - let tMin = timeStringToMinutes(p.time) - btMin - if (tMin < -120) tMin += 24 * 60 // overnight wrap - return { ...p, minutesFromBedtime: tMin } - }).sort((a, b) => a.minutesFromBedtime - b.minutesFromBedtime) - - const total = withRelative.length - const points: CurvePoint[] = withRelative.map((p, i) => { - const frac = i / (total - 1) - const phase = frac < 0.1 - ? 'warmUp' as const - : frac < 0.25 - ? 'coolDown' as const - : frac < 0.55 - ? 'deepSleep' as const - : frac < 0.75 - ? 'maintain' as const - : frac < 0.9 - ? 'preWake' as const - : 'wake' as const - - return { minutesFromBedtime: p.minutesFromBedtime, tempOffset: p.tempF - 80, phase } - }) - - return { points, bedtimeMinutes: btMin } - }, [curve, editablePoints]) - - if (!open) return null - - return ( - <> - {/* Backdrop */} -
- - {/* Modal */} -
- {/* Header */} -
-
- - Custom AI Curve -
- -
- - {/* Step indicator */} -
- {STEP_LABELS.map((label, i) => ( - - ))} -
- - {/* Content */} -
- {step === 0 && ( - - )} - {step === 1 && ( - - )} - {step === 2 && ( - - )} - {step === 3 && curve && ( - - )} -
- - {/* Footer nav */} -
- - - {step === 0 && ( -
- - -
- )} - - {step > 0 && step < 3 && ( - - )} -
-
- - ) -} - -// ─── Step 1: Describe ──────────────────────────────────────────────── - -function StepDescribe({ - preferences, - onPreferencesChange, - templates, - onLoadTemplate, - onDeleteTemplate, -}: { - preferences: string - onPreferencesChange: (v: string) => void - templates: CurveTemplate[] - onLoadTemplate: (t: CurveTemplate) => void - onDeleteTemplate: (name: string) => void -}) { - return ( -
-
-

- Describe your sleep preferences in natural language. An AI will design a personalized temperature curve. -

-