Blog
++ Publish product updates, engineering notes, and launch posts here. +
+From 90ad5e02609c4931b5fd1b9f77ad31160bec70e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20Alves=20R=C3=B6sel?=
<60182044+velocitatem@users.noreply.github.com>
Date: Thu, 2 Apr 2026 18:47:14 +0200
Subject: [PATCH] Initial commit
---
.claude/commands/api.md | 14 +
.claude/commands/build.md | 15 +
.claude/commands/page.md | 11 +
.claude/commands/plan.md | 11 +
.claude/commands/review.md | 10 +
.claude/commands/ship.md | 10 +
.dockerignore | 34 +
.env.example | 54 +
.github/workflows/ci.yml | 47 +
.gitignore | 11 +
AGENTS.md | 57 +
CLAUDE.md | 99 +
Makefile | 185 +
README.md | 155 +
alveslib/__init__.py | 4 +
alveslib/agent.py | 139 +
alveslib/logger.py | 83 +
alveslib/project.json | 32 +
alveslib/scraper.py | 70 +
apps/backend/fastapi/project.json | 40 +
apps/backend/fastapi/server.py | 24 +
apps/backend/flask/project.json | 40 +
apps/backend/flask/server.py | 17 +
apps/webapp-minimal/app.py | 1 +
apps/webapp-minimal/project.json | 15 +
apps/webapp/.gitignore | 16 +
apps/webapp/README.md | 36 +
apps/webapp/bun.lock | 862 ++++
apps/webapp/eslint.config.mjs | 25 +
apps/webapp/middleware.ts | 16 +
apps/webapp/next.config.ts | 8 +
apps/webapp/package.json | 31 +
apps/webapp/postcss.config.mjs | 5 +
apps/webapp/project.json | 44 +
apps/webapp/public/file.svg | 1 +
apps/webapp/public/globe.svg | 1 +
apps/webapp/public/llms.txt | 1 +
apps/webapp/public/next.svg | 1 +
apps/webapp/public/robots.txt | 0
apps/webapp/public/vercel.svg | 1 +
apps/webapp/public/window.svg | 1 +
apps/webapp/src/app/auth/confirm/route.ts | 28 +
apps/webapp/src/app/blog/page.tsx | 10 +
apps/webapp/src/app/dashboard/actions.ts | 12 +
apps/webapp/src/app/dashboard/layout.tsx | 14 +
apps/webapp/src/app/dashboard/page.tsx | 21 +
apps/webapp/src/app/error.tsx | 20 +
apps/webapp/src/app/error/page.tsx | 5 +
apps/webapp/src/app/favicon.ico | Bin 0 -> 25931 bytes
apps/webapp/src/app/globals.css | 26 +
apps/webapp/src/app/instruments/page.tsx | 8 +
apps/webapp/src/app/layout.tsx | 29 +
apps/webapp/src/app/login/actions.ts | 46 +
apps/webapp/src/app/login/page.tsx | 14 +
apps/webapp/src/app/not-found.tsx | 12 +
apps/webapp/src/app/page.tsx | 17 +
apps/webapp/src/app/privacy-policy/page.tsx | 10 +
apps/webapp/src/app/tos/page.tsx | 10 +
apps/webapp/src/components/FeaturesGrid.tsx | 22 +
apps/webapp/src/components/Footer.tsx | 37 +
apps/webapp/src/components/Header.tsx | 27 +
apps/webapp/src/components/Hero.tsx | 24 +
apps/webapp/src/components/Pricing.tsx | 47 +
apps/webapp/src/components/Testimonials1.tsx | 25 +
apps/webapp/src/libs/locales.ts | 32 +
apps/webapp/src/locales/en/common.json | 85 +
apps/webapp/src/utils/supabase/client.ts | 8 +
apps/webapp/src/utils/supabase/middleware.ts | 66 +
apps/webapp/src/utils/supabase/server.ts | 29 +
apps/webapp/tsconfig.json | 27 +
apps/worker/project.json | 40 +
apps/worker/worker.py | 23 +
bun.lock | 275 ++
docker-compose.yml | 162 +
docker/ml.Dockerfile | 38 +
docker/worker.Dockerfile | 34 +
ml/__init__.py | 0
ml/configs/data/default.yaml | 6 +
ml/configs/train/default.yaml | 10 +
ml/data/README.md | 5 +
ml/data/etl.py | 52 +
ml/data/processed/dataset.pt | Bin 0 -> 149285 bytes
ml/data/processed/metadata.json | 8 +
ml/inference.py | 36 +
ml/models/arch.py | 15 +
ml/models/train.py | 98 +
ml/project.json | 70 +
nx.json | 50 +
package.json | 19 +
pyproject.toml | 51 +
requirements.txt | 33 +
src/__init__.py | 0
src/main.py | 22 +
uv.lock | 3812 ++++++++++++++++++
94 files changed, 7797 insertions(+)
create mode 100644 .claude/commands/api.md
create mode 100644 .claude/commands/build.md
create mode 100644 .claude/commands/page.md
create mode 100644 .claude/commands/plan.md
create mode 100644 .claude/commands/review.md
create mode 100644 .claude/commands/ship.md
create mode 100644 .dockerignore
create mode 100644 .env.example
create mode 100644 .github/workflows/ci.yml
create mode 100644 .gitignore
create mode 100644 AGENTS.md
create mode 100644 CLAUDE.md
create mode 100644 Makefile
create mode 100644 README.md
create mode 100644 alveslib/__init__.py
create mode 100644 alveslib/agent.py
create mode 100644 alveslib/logger.py
create mode 100644 alveslib/project.json
create mode 100644 alveslib/scraper.py
create mode 100644 apps/backend/fastapi/project.json
create mode 100644 apps/backend/fastapi/server.py
create mode 100644 apps/backend/flask/project.json
create mode 100644 apps/backend/flask/server.py
create mode 100644 apps/webapp-minimal/app.py
create mode 100644 apps/webapp-minimal/project.json
create mode 100644 apps/webapp/.gitignore
create mode 100644 apps/webapp/README.md
create mode 100644 apps/webapp/bun.lock
create mode 100644 apps/webapp/eslint.config.mjs
create mode 100644 apps/webapp/middleware.ts
create mode 100644 apps/webapp/next.config.ts
create mode 100644 apps/webapp/package.json
create mode 100644 apps/webapp/postcss.config.mjs
create mode 100644 apps/webapp/project.json
create mode 100644 apps/webapp/public/file.svg
create mode 100644 apps/webapp/public/globe.svg
create mode 100644 apps/webapp/public/llms.txt
create mode 100644 apps/webapp/public/next.svg
create mode 100644 apps/webapp/public/robots.txt
create mode 100644 apps/webapp/public/vercel.svg
create mode 100644 apps/webapp/public/window.svg
create mode 100644 apps/webapp/src/app/auth/confirm/route.ts
create mode 100644 apps/webapp/src/app/blog/page.tsx
create mode 100644 apps/webapp/src/app/dashboard/actions.ts
create mode 100644 apps/webapp/src/app/dashboard/layout.tsx
create mode 100644 apps/webapp/src/app/dashboard/page.tsx
create mode 100644 apps/webapp/src/app/error.tsx
create mode 100644 apps/webapp/src/app/error/page.tsx
create mode 100644 apps/webapp/src/app/favicon.ico
create mode 100644 apps/webapp/src/app/globals.css
create mode 100644 apps/webapp/src/app/instruments/page.tsx
create mode 100644 apps/webapp/src/app/layout.tsx
create mode 100644 apps/webapp/src/app/login/actions.ts
create mode 100644 apps/webapp/src/app/login/page.tsx
create mode 100644 apps/webapp/src/app/not-found.tsx
create mode 100644 apps/webapp/src/app/page.tsx
create mode 100644 apps/webapp/src/app/privacy-policy/page.tsx
create mode 100644 apps/webapp/src/app/tos/page.tsx
create mode 100644 apps/webapp/src/components/FeaturesGrid.tsx
create mode 100644 apps/webapp/src/components/Footer.tsx
create mode 100644 apps/webapp/src/components/Header.tsx
create mode 100644 apps/webapp/src/components/Hero.tsx
create mode 100644 apps/webapp/src/components/Pricing.tsx
create mode 100644 apps/webapp/src/components/Testimonials1.tsx
create mode 100644 apps/webapp/src/libs/locales.ts
create mode 100644 apps/webapp/src/locales/en/common.json
create mode 100644 apps/webapp/src/utils/supabase/client.ts
create mode 100644 apps/webapp/src/utils/supabase/middleware.ts
create mode 100644 apps/webapp/src/utils/supabase/server.ts
create mode 100644 apps/webapp/tsconfig.json
create mode 100644 apps/worker/project.json
create mode 100644 apps/worker/worker.py
create mode 100644 bun.lock
create mode 100644 docker-compose.yml
create mode 100644 docker/ml.Dockerfile
create mode 100644 docker/worker.Dockerfile
create mode 100644 ml/__init__.py
create mode 100644 ml/configs/data/default.yaml
create mode 100644 ml/configs/train/default.yaml
create mode 100644 ml/data/README.md
create mode 100644 ml/data/etl.py
create mode 100644 ml/data/processed/dataset.pt
create mode 100644 ml/data/processed/metadata.json
create mode 100644 ml/inference.py
create mode 100644 ml/models/arch.py
create mode 100644 ml/models/train.py
create mode 100644 ml/project.json
create mode 100644 nx.json
create mode 100644 package.json
create mode 100644 pyproject.toml
create mode 100644 requirements.txt
create mode 100644 src/__init__.py
create mode 100644 src/main.py
create mode 100644 uv.lock
diff --git a/.claude/commands/api.md b/.claude/commands/api.md
new file mode 100644
index 0000000..b754241
--- /dev/null
+++ b/.claude/commands/api.md
@@ -0,0 +1,14 @@
+Scaffold a new API endpoint. Read apps/backend/fastapi/server.py (or flask if BACKEND_MODE=flask) first.
+
+From the arguments, determine:
+- HTTP method and path (e.g. POST /items)
+- Request/response shape
+- Any DB or external service calls needed
+
+Add the route to the appropriate server.py. Follow the existing structure:
+- Use Pydantic models for FastAPI request/response schemas
+- Use dotenv for any config
+- Add proper status codes and error responses
+- Keep business logic out of the route handler; extract to a helper if more than ~15 lines
+
+If a new dependency is needed, add it to requirements.txt.
diff --git a/.claude/commands/build.md b/.claude/commands/build.md
new file mode 100644
index 0000000..a53ee72
--- /dev/null
+++ b/.claude/commands/build.md
@@ -0,0 +1,15 @@
+Read CLAUDE.md and AGENTS.md. Read the relevant existing code before making any changes.
+
+Implement the feature or task described in the arguments end-to-end across the full stack as needed. Follow the code style rules in CLAUDE.md strictly:
+- No redundant comments, no boilerplate, no fluff
+- Pure functions preferred, side effects at boundaries
+- Reuse what already exists in alveslib before writing new utilities
+- Type all public interfaces
+- Keep modules under 400 lines
+
+After implementing, run a quick sanity check:
+- If Python changed: check imports resolve correctly
+- If Next.js changed: check TypeScript compiles (bun run typecheck)
+- If docker-compose changed: verify YAML is valid
+
+Do not create documentation files. Do not add emojis. Summarize what was done in one paragraph.
diff --git a/.claude/commands/page.md b/.claude/commands/page.md
new file mode 100644
index 0000000..691a839
--- /dev/null
+++ b/.claude/commands/page.md
@@ -0,0 +1,11 @@
+Scaffold a new Next.js App Router page. Read apps/webapp/src/app/layout.tsx and an existing page (e.g. apps/webapp/src/app/page.tsx) for context.
+
+From the arguments, determine:
+- Route path (maps to directory under apps/webapp/src/app/)
+- Whether it needs auth (server component checking Supabase session)
+- Whether it needs client interactivity (use client directive)
+- Data it needs to fetch
+
+Create the page file bare-bones with correct structure - no inline styles. Add any new strings to apps/webapp/src/locales/en/common.json. If a reusable component is needed, create it in apps/webapp/src/components/ without styling (styling is done last per CLAUDE.md).
+
+If the page requires server actions, create an adjacent actions.ts file.
diff --git a/.claude/commands/plan.md b/.claude/commands/plan.md
new file mode 100644
index 0000000..6d0ebed
--- /dev/null
+++ b/.claude/commands/plan.md
@@ -0,0 +1,11 @@
+Read CLAUDE.md and AGENTS.md to understand the project structure, then read the current state of the codebase.
+
+Given the idea or feature described by the user (provided as arguments to this command, or ask if not given), produce a direct implementation plan:
+
+1. Which parts of the scaffold to use (webapp, backend, worker, ml, src)
+2. What files to create or modify and in what order
+3. What dependencies to add to requirements.txt or package.json
+4. Any services to enable in docker-compose.yml
+5. Any env vars to add to .env.example
+
+Be specific and concrete. No fluff. The plan should be immediately actionable by you or a developer.
diff --git a/.claude/commands/review.md b/.claude/commands/review.md
new file mode 100644
index 0000000..a417631
--- /dev/null
+++ b/.claude/commands/review.md
@@ -0,0 +1,10 @@
+Run `git diff HEAD` (or `git diff HEAD~1` if nothing staged) to get recent changes.
+
+Review the diff for:
+1. Correctness - logic errors, off-by-ones, missing error handling
+2. Code style - violations of CLAUDE.md tenets (redundancy, unclear names, noisy loops)
+3. Security - exposed secrets, unvalidated input, CORS issues, SQL injection surface
+4. Performance - N+1 queries, blocking I/O in async context, missing indices
+5. Missing edge cases or TODOs that should be addressed before shipping
+
+Be direct. List issues with file:line references where possible. Skip praise.
diff --git a/.claude/commands/ship.md b/.claude/commands/ship.md
new file mode 100644
index 0000000..9bd7bb2
--- /dev/null
+++ b/.claude/commands/ship.md
@@ -0,0 +1,10 @@
+Stage all meaningful changes and create a commit with a concise, accurate message following conventional commits format (feat:, fix:, refactor:, chore:, etc.).
+
+Steps:
+1. Run `git status` and `git diff` to understand what changed
+2. Skip any generated files, lockfiles (unless intentional), secrets, or build artifacts
+3. Stage the relevant changes with `git add`
+4. Write a commit message: one-line summary (type: description), optional body if needed
+5. Commit
+
+Do not push unless explicitly asked. Do not amend existing commits. Report what was committed.
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..729cb2d
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,34 @@
+# Version control
+.git
+.gitignore
+
+# Node / frontend
+**/node_modules
+apps/webapp/.next
+apps/webapp/bun.lock
+
+# Python
+**/__pycache__
+**/*.pyc
+**/*.pyo
+.venv
+**/.venv
+*.egg-info
+build/
+dist/
+
+# Dev artifacts
+.env
+**/.env
+**/.scraper_cache
+**/downloaded_files
+logs/
+ml/tensorboard/
+ml/notebooks/
+FLAT.xml
+
+# Docs / meta
+*.md
+AGENTS.md
+CLAUDE.md
+.github/
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..61ef19d
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,54 @@
+NAME=myproject
+COMPOSE_PROJECT_NAME=$NAME
+
+# Backend
+BACKEND_MODE=fastapi
+BACKEND_PORT=9812
+
+# Ports
+REDIS_PORT=6378
+GRAFANA_PORT=3125
+LOKI_PORT=3142
+
+# PostgreSQL
+POSTGRES_PORT=5432
+POSTGRES_DB=app
+POSTGRES_USER=postgres
+POSTGRES_PASSWORD=postgres
+POSTGRES_HOST=localhost
+
+# MongoDB
+MONGO_PORT=27017
+MONGO_DB=app
+MONGO_USER=admin
+MONGO_PASSWORD=admin123
+MONGO_HOST=localhost
+
+DATABASE_TYPE=postgres
+
+# Redis
+REDIS_URL=redis://localhost:$REDIS_PORT
+
+# Logging
+LOGDIR="/tmp/logs-$NAME/"
+
+# Supabase (webapp auth - set NEXT_PUBLIC_REQUIRE_AUTH=true to enable gating)
+NEXT_PUBLIC_REQUIRE_AUTH=false
+NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
+NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_supabase_anon_key_here
+
+# MinIO
+MINIO_ROOT_USER=minioadmin
+MINIO_ROOT_PASSWORD=minioadmin
+MINIO_ENDPOINT=localhost:9900
+
+# ML
+ML_LATEST_WEIGHTS_PATH=/app/models/weights
+MLFLOW_TRACKING_URI=http://localhost:5000
+
+# AI / Agents
+ANTHROPIC_API_KEY=sk-ant-...
+# Optional: use Bedrock instead of direct Anthropic API
+# CLAUDE_CODE_USE_BEDROCK=1
+# Optional: use Vertex AI
+# CLAUDE_CODE_USE_VERTEX=1
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..f55f9c7
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,47 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ webapp:
+ name: Next.js build
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+ - name: Install dependencies
+ working-directory: apps/webapp
+ run: bun install --frozen-lockfile
+ - name: Type check
+ working-directory: apps/webapp
+ run: bun run typecheck
+ - name: Build
+ working-directory: apps/webapp
+ run: bun run build
+ env:
+ NEXT_PUBLIC_REQUIRE_AUTH: "false"
+ NEXT_PUBLIC_SUPABASE_URL: "https://placeholder.supabase.co"
+ NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY: "placeholder"
+
+ python:
+ name: Python lint + import check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+ - name: Install deps
+ run: pip install ruff python-dotenv
+ - name: Lint
+ run: ruff check src/ ml/ apps/worker/ apps/backend/ alveslib/ || true
+ - name: Import smoke test
+ run: |
+ python -c "from alveslib.logger import get_logger; print('logger ok')"
+ python -c "from alveslib.scraper import ScraperCache; print('scraper ok')" || true
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e5eaa34
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+**/node_modules
+*.env
+.venv/
+**/__pycache__
+**/.scraper_cache/
+**/downloaded_files/
+**.egg-info/
+logs/
+FLAT.xml
+**/package-lock.json
+.nx/
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..6c30779
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,57 @@
+# UltiPlate - Agent Instructions
+
+Scaffold for any project: webapp, API, ML pipeline, scraper, worker, CLI, or SDK. Deployable via Makefile and Docker Compose.
+
+## Project Layout
+
+```
+apps/webapp/ Next.js 15 + React 19 + Tailwind 4 (Bun, Turbopack, auth optional)
+apps/webapp-minimal/ Streamlit prototype
+apps/backend/fastapi/ FastAPI server
+apps/backend/flask/ Flask server
+apps/worker/ Celery worker (Redis broker)
+ml/ PyTorch ML pipeline (arch, train, inference, etl)
+alveslib/ Shared Python library: logger, scraper, agent
+src/ Simple scripts / CLI
+```
+
+## Rules for Agents
+
+- Use `make init` to bootstrap. Use `make dev` to run webapp. Use `make help` for all targets.
+- Python deps: use root `pyproject.toml` + `uv.lock`; `make envlink` propagates `.env` to sub-apps.
+- JS/TS: Bun is the package manager for `apps/webapp`. Use `bun add` / `bun install` / `bun dev`.
+- Do not create rogue files or test scripts outside the established structure.
+- All shared Python utilities go in `alveslib/`. Import from there, never duplicate logic.
+- No emojis in code, comments, or logs.
+
+## AI / Agent SDK
+
+`ANTHROPIC_API_KEY` is required for AI features. `alveslib.agent` provides:
+
+```python
+from alveslib import ask, stream, Agent
+
+ask("prompt") # blocking one-shot
+stream("prompt") # iterator of text chunks
+Agent(system="...").chat("prompt") # multi-turn
+```
+
+For full agentic loops with file/bash tools, use the Claude Agent SDK:
+```bash
+pip install claude-agent-sdk
+```
+```python
+from claude_agent_sdk import query, ClaudeAgentOptions
+async for msg in query(prompt="...", options=ClaudeAgentOptions(allowed_tools=["Read","Bash"])):
+ print(msg)
+```
+
+## Slash Commands (.claude/commands/)
+
+Use in Claude Code sessions (type `/`):
+- `/plan` - plan an implementation within this boilerplate
+- `/build` - implement a feature end-to-end
+- `/api` - scaffold a backend endpoint
+- `/page` - scaffold a Next.js page
+- `/review` - review recent changes
+- `/ship` - commit staged changes
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..5990c08
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,99 @@
+# Template
+My template repository for almost any project that I might be doing in the field of AI or building a software product or any sort of tool ranging form a machine learning project, data analysis, SaaS app or a python library or some sort of API or scraper or ETL tool or some sort of simulation or just a small python program to run something simple.
+This template is AI native and platform agnostic and meant to be effortlessly deployable to anywhere at any time with any software with minimal effort managed via my Makefile.
+
+#### Directory Breakdown
+- apps
+ - webapp (A next.js 15.5.2 webapp with react 19 pre-configured with basics and Tailwind CSS)
+ - This can server both as the frontend but also nextjs allows for API route definitions for simple things that we could build.
+ - webapp-minimal (Streamlit webapp with minimal web interface for quick prototypes)
+ - worker (Background worker template for long running tasks for programs)
+ - backend/{fastapi|flask} to define a web api with either or whichever is specified/user wants or is a best fit scenario.
+- ml (Machine learning pipeline for PyTorch)
+ - models (arch.py where we define architectures and train.py for the training loop)
+ - inference.py (A minimal setup webserver with fastapi to run inference online)
+ - notebooks (For any notebook needs)
+ - data (has etl.py for any ETL and should be a single place for turning raw data into pytorch ready datasets)
+- src (Just as __init__.py for any simple modules or building libraries within there, should be used for simple python scripts without any otehr needs or just running something in the CLI)
+
+
+#### Services Associated
+- I setup a basic optional minio service to run for any needs of object storage or manipulation for any machine learning tasks
+- Tensorboard is very useful for monitoring experiments and is defined as a docker service an spinupable with the make tensorboard command.
+
+###### Logging
+1. Grafana to view (must be configured by adding loki url with "http://loki:31000")
+2. For now just python directly adds the logs to loki (via the alveslib package)
+
+```python
+from alveslib import get_logger
+logger = get_logger("service") # if you are writing contents for logs or any relevant prints do not use emojis in any debug statements or logs.
+```
+FOR REFERENCE ALL OTHER REUSABLE MODULES LIKE THIS SHOULD BE DEFINED THE SAME WAY IN THE ALVESLIB package - if used in python.
+Using lazydocker to manager containers...
+
+### Checklists and Best Practices and Code Hygine
+
+#### `apps/webapp` - building a nextjs app.
+
+Reusability is key - define modules and do not repeat code or logic anywhere. Style should be done LAST, when defining new components define them bare-bones or motivated with globals.css if compelted. You can make use of https://reactbits.dev/
+
+- [ ] Make sure to update layout.tsx with proper title and meta description
+- [ ] Using a provided moodboard -> create a globals.css update to match the style [use creative structure for each project differently (do not take provided as given)]
+- [ ] Flesh out proper content for the TOC and Privacy Policy - write content for both of the components to properly define them
+- [ ] Optionally turn off eslint
+- [ ] Properly stylize the Header and Footer
+- [ ] Define the robots.txt and llms.txt
+- [ ] Connect a supabase project if it is being used and properly spin up a local supabase container setup if desired for local testing. If a webapp does not require user auth (or just yet) just remove references to the /login /dashboard routes but do not delete the code of using them.
+- [ ] Connect analytics with google with `gtag package`
+
+#### `ml/data` - using and building datasets for ML
+Parallel data loading and using third party datasets. All code written must ensure that running the model training is possible on any machine.
+Downloading data from third party sources must be done in a reproducible way (the export part of the ETL). If datasets are too large for just pandas, using spark is the best way forward.
+More on ETL: Dataset ETL: deterministic, resumable, cached stages. Hash raw inputs and params to derive cache keys. If data missing at runtime, trigger acquisition with rate-limited, cached requests.
+Data processing should be cachable so if any stage of the transformation or data processing fails it does not start from scrach. If a dataloader class is being used and data is not present on the system, it should trigger logic to get the data and handle any transformations necssary. The whole pipeline should be self informed about what is hapenning and aware of the phase its in. If third party requests are made ferquently, they should also be cached to prevent overloading any servers.
+Dataloaders defined in pytorch if handling for example 1e6 images with a storage bucket like minio (just a dummy example it should be generaliziable) must stream the data as it is being used for training just in time. (refer streaming (IterableDatasets), WebDataset/tar shards, or torchdata for 1e6+ items. Avoid loading whole corpora in memory.)
+
+#### `ml/models` - creating model architectures and defining training loops
+Define the architectuer in arch.py and training loop in train.py - training should be logged with tensorboard always and evaluations metrics should be versioned and defined in separate logic units like eval.py to make experiments comparable, if at any poitns eval metrics change or scale they should be tracked under a sparate track of experiments in tensorboard - do not arbitrarily modify the eval of anything or loss unless explicitly necessary.
+Experiments should be tracked with meaningful names and logging any parameters into experiments. Machine learning is a life cycle and always evolving. DO NOT create any rogue execution scripts just use the train.py for training.
+## Code style notes
+- Prefer pure functions where possible. Keep side effects at boundaries. Make sure functions or classes you write do not repeat code or logic and are highly modular with minimal code expressions for solutions. Use list/dictionary comprehension to generate cleaner code that is easily readable. Feel free to use shorter and more sucient variable names.
+- when it comes to writing comments if you have a lot to say do not split up comments on multipline by multiple consequent # or // symbols but rather write complete sentences and comment directly next to logic - do not use any emojis in comments and feel free to not use perfect grammar.
+- Keep modules <300–400 lines where possible. Extract helpers early into unified scripts and avoid generating noisy scattered solutions. File structure should extract library and reusable components into alveslib or into locally importable modules.
+- Type everything at public boundaries. mypy-friendly.
+- Return precise errors. Avoid blanket exceptions. But avoid noisy try-catch multiple nested layers.
+
+## IMPORTANT
+Do NOT externalize or document your work, usage guidelines, or benchmarks (e.g., README.md, CONTRIBUTING.md, SUMMARY.md, USAGE_GUIDELINES.md) after completing the task — unless you are explicitly instructed to do so.
+
+You may include a brief summary of your work, but do not create separate documentation files for it
+
+# Code Confession and Tenets - By CLAUDE for future CLAUDE code instances
+What I did wrong in the past and might be tempted to do
+
+I wrote bloated, amateur garbage that insults the reader's intelligence. The original code had:
+- 40+ lines to do what takes 15 or even 5
+- Comments explaining tokenizer.batch_decode() like you're an idiot
+- Variable names like cer_scores, wer_scores, ned_scores, hybrid_losses with individual append loops instead of one list comprehension
+- Whitespace between every line like I'm being paid by vertical space
+- Three separate loops iterating over the same data
+- Args/Returns docstrings stating the obvious
+
+The Tenets:
+
+1. Code is read, not performed - Every line should carry weight. Whitespace is not decoration.
+2. One pass, one purpose - If you're looping multiple times over the same data, you failed.
+3. Compression over explanation - pred_ids = np.argmax(preds[0] if isinstance(preds, tuple) else preds,
+ axis=-1) is clearer than five lines with intermediate variables.
+4. Comments are failure - If your code needs comments to be understood, you named things wrong or
+structured it poorly unless deeply technical or niche.
+5. List comprehensions over loops - [compute_ocr_metrics(gt, pred) for gt, pred in zip(label_texts,
+pred_texts)] beats a for loop with appends.
+6. Return early, return directly - Don't create a variable just to return it on the next line.
+7. Closures over classes - create_compute_metrics returns a closure with tokenizer captured - no need
+for a class with one method.
+8. Trust the reader - They know what eval_pred is. They don't need a docstring.
+
+The code should be dense, efficient, and assume intelligence. Anything else is disrespect.
+Before writing code, understand the problem's physics. OOM means "ran out during operation" not "didn't clean up after." Surface-level pattern matching kills code quality.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c15f462
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,185 @@
+# Makefile - Ultiplate Template
+.PHONY: help init bootstrap venv deps lock envlink fmt lint type test clean doctor
+.PHONY: up down logs ps rebuild
+.PHONY: dev run.webapp run.webapp.simple run.backend run.worker run.ml
+.PHONY: lift lift.minio lift.tensorboard lift.mlflow lift.logging lift.database
+.PHONY: etl train infer seed
+.PHONY: nx.graph nx.projects nx.affected
+.DEFAULT_GOAL := help
+
+WD := $(shell pwd)
+ENV := $(shell readlink -f .env 2>/dev/null || echo .env)
+PYTHON_VER := 3.12
+PYTHON := python$(PYTHON_VER)
+UV := $(shell command -v uv 2>/dev/null || echo uv)
+BUN := $(shell command -v bun 2>/dev/null || echo bun)
+NX := $(BUN) x nx
+
+## ── Quick Start ──────────────────────────────────────────────────────────────
+
+init: bootstrap ## First-time setup (alias for bootstrap)
+
+dev: run.webapp ## Start the Next.js webapp (fastest entry point)
+
+## ── Environment Setup ────────────────────────────────────────────────────────
+
+bootstrap: venv envlink deps ## Full initial setup: venv + deps + env linking
+ @echo "Bootstrap complete. Activate Python env: source .venv/bin/activate"
+
+venv: ## Create Python virtual environment (idempotent)
+ @if [ ! -d ".venv" ]; then \
+ echo "Creating uv-managed venv (Python $(PYTHON_VER))..."; \
+ $(UV) venv --python $(PYTHON_VER) .venv; \
+ fi
+
+deps: venv ## Install/update Python dependencies
+ @$(UV) sync
+ @if [ -f package.json ]; then $(BUN) install; fi
+ @cd apps/webapp && $(BUN) install --frozen-lockfile 2>/dev/null || $(BUN) install
+
+lock: ## Refresh uv lockfile
+ @$(UV) lock
+
+envlink: ## Propagate root .env to all sub-apps
+ @mkdir -p apps/webapp apps/worker ml
+ @touch "$(WD)/apps/webapp/.env" "$(WD)/apps/worker/.env" "$(WD)/ml/.env"
+ @if [ -f "$(ENV)" ]; then \
+ ln -sf "$(ENV)" "$(WD)/apps/webapp/.env"; \
+ ln -sf "$(ENV)" "$(WD)/apps/worker/.env"; \
+ ln -sf "$(ENV)" "$(WD)/ml/.env"; \
+ fi
+
+doctor: ## Verify toolchain (bun, docker, python)
+ @echo "Checking toolchain..."
+ @$(PYTHON) --version || (echo "python$(PYTHON_VER) not found"; exit 1)
+ @$(UV) --version || echo "uv not found - install: curl -LsSf https://astral.sh/uv/install.sh | sh"
+ @$(BUN) --version || echo "bun not found - install: curl -fsSL https://bun.sh/install | bash"
+ @docker --version || echo "docker not found"
+ @docker compose version || echo "docker compose not found"
+ @echo "OK"
+
+## ── Code Quality ─────────────────────────────────────────────────────────────
+
+fmt: venv ## Format Python with black
+ @$(UV) run black src/ ml/ apps/worker/ apps/backend/ 2>/dev/null || echo "Run: make deps"
+
+lint: venv ## Lint Python with ruff
+ @$(UV) run ruff check src/ ml/ apps/worker/ apps/backend/ 2>/dev/null || echo "Run: make deps"
+
+type: venv ## Type check Python with mypy
+ @$(UV) run mypy src/ ml/ apps/worker/ apps/backend/ 2>/dev/null || echo "Run: make deps"
+
+test: venv ## Run pytest
+ @$(UV) run pytest tests/ -v 2>/dev/null || echo "No tests yet - create tests/"
+
+## ── Docker ───────────────────────────────────────────────────────────────────
+
+up: ## Start core services (redis, ml-inference, worker)
+ @docker compose up -d redis ml-inference worker
+
+down: ## Stop all services
+ @docker compose down
+
+logs: ## Tail all service logs
+ @docker compose logs -f
+
+ps: ## Show service status
+ @docker compose ps
+
+rebuild: ## Rebuild + restart all services (no cache)
+ @docker compose build --no-cache && docker compose up -d
+
+## ── Service Profiles ─────────────────────────────────────────────────────────
+
+lift: up ## Alias for 'up'
+
+lift.minio: ## Start core services + MinIO object storage
+ @docker compose --profile minio up -d
+ @echo "MinIO console: http://localhost:9901 (minioadmin/minioadmin)"
+
+lift.tensorboard: ## Start TensorBoard
+ @docker compose --profile tensorboard up -d
+ @echo "TensorBoard: http://localhost:6006"
+
+lift.mlflow: ## Start optional MLflow tracking server
+ @docker compose --profile mlflow up -d
+ @echo "MLflow: http://localhost:5000"
+
+lift.logging: ## Start Loki + Grafana logging stack
+ @docker compose --profile logging up -d
+ @if [ -f .env ]; then . ./.env 2>/dev/null; fi; \
+ echo "Grafana: http://localhost:$${GRAFANA_PORT:-3000} (admin/admin)"; \
+ echo "Loki: http://localhost:$${LOKI_PORT:-3100}"
+
+lift.database: ## Start database services (postgres/mongodb)
+ @docker compose --profile database up -d
+
+## ── Run Applications ─────────────────────────────────────────────────────────
+
+run.webapp: ## Start Next.js webapp with bun (dev + turbopack)
+ @echo "Starting webapp at http://localhost:3000"
+ @$(NX) run webapp:dev
+
+run.webapp.simple: ## Start Streamlit minimal webapp
+ @$(NX) run webapp-minimal:dev
+
+run.backend: ## Start API backend (BACKEND_MODE=fastapi|flask, default: fastapi)
+ @if [ -f .env ]; then . ./.env; fi; \
+ MODE=$${BACKEND_MODE:-fastapi}; \
+ if [ "$$MODE" = "fastapi" ]; then \
+ $(NX) run backend-fastapi:dev; \
+ elif [ "$$MODE" = "flask" ]; then \
+ $(NX) run backend-flask:dev; \
+ else \
+ echo "Unknown BACKEND_MODE=$$MODE (fastapi|flask)"; exit 1; \
+ fi
+
+run.worker: ## Start Celery worker (requires redis)
+ @$(NX) run worker:dev
+
+run.ml: ## Start ML inference server (FastAPI)
+ @$(NX) run ml:dev
+
+## ── ML Workflow ──────────────────────────────────────────────────────────────
+
+etl: venv ## Run ETL pipeline
+ @$(NX) run ml:etl
+
+train: venv ## Run model training
+ @$(NX) run ml:train
+
+nx.graph: ## Open Nx project graph
+ @$(NX) graph
+
+nx.projects: ## List Nx projects in workspace
+ @$(NX) show projects
+
+nx.affected: ## Run lint/test/build only for affected projects
+ @$(NX) affected -t lint,test,build
+
+infer: run.ml ## Alias for run.ml
+
+## ── Utilities ────────────────────────────────────────────────────────────────
+
+seed: venv ## Seed development data
+ @$(PYTHON) scripts/seed.py 2>/dev/null || echo "Create scripts/seed.py for seeding"
+
+clean: ## Remove caches, build artifacts, and compiled files
+ @find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
+ @find . -type f -name "*.pyc" -delete 2>/dev/null || true
+ @find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
+ @find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
+ @find . -type d -name ".mypy_cache" -exec rm -rf {} + 2>/dev/null || true
+ @find . -type d -name ".ruff_cache" -exec rm -rf {} + 2>/dev/null || true
+ @rm -rf build/ dist/ 2>/dev/null || true
+
+help: ## Show this help
+ @echo "Ultiplate - make targets"
+ @echo ""
+ @echo " Quick start:"
+ @echo " make init - First-time setup"
+ @echo " make dev - Start Next.js webapp"
+ @echo " make up - Start Docker services"
+ @echo ""
+ @grep -E '^[a-zA-Z_.%-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
+ awk 'BEGIN {FS = ":.*?## "}; {printf " %-22s %s\n", $$1, $$2}'
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..d74edac
--- /dev/null
+++ b/README.md
@@ -0,0 +1,155 @@
+# Ultiplate
+
+Template for any project: SaaS webapp, API server, ML pipeline, scraper, CLI, or background worker. AI-native, platform-agnostic, managed via Makefile + Nx.
+
+## Quick Start
+
+```bash
+cp .env.example .env # fill in NAME and any keys you need
+make init # uv venv + sync + env linking
+make dev # Next.js webapp at http://localhost:3000
+make nx.projects # list Nx projects in the monorepo
+```
+
+For Docker services (redis, ml inference, worker):
+```bash
+make up
+```
+
+## Directory
+
+```
+apps/
+ webapp/ Next.js 15 + React 19 + Tailwind 4 + Supabase auth (Bun, Turbopack)
+ webapp-minimal/ Streamlit quick prototype
+ backend/
+ fastapi/ FastAPI server (set BACKEND_MODE=fastapi)
+ flask/ Flask server (set BACKEND_MODE=flask)
+ worker/ Celery background worker backed by Redis
+ml/
+ configs/ YAML config for data + training hyperparameters
+ models/ arch.py (architecture) + train.py (training loop)
+ data/ etl.py + processed artifacts
+ inference.py FastAPI inference server
+ notebooks/ Jupyter notebooks
+alveslib/ Shared Python utilities (logger, scraper, agent)
+src/ Simple scripts / CLI entry points
+```
+
+## Make Targets
+
+| Target | Description |
+|--------|-------------|
+| `make init` | First-time setup |
+| `make dev` | Start Next.js webapp |
+| `make up` | Start Docker core services |
+| `make run.backend` | Start API backend |
+| `make run.worker` | Start Celery worker |
+| `make nx.graph` | Open Nx project graph |
+| `make nx.affected` | Run lint/test/build for affected projects |
+| `make lift.minio` | Start MinIO object storage |
+| `make lift.logging` | Start Loki + Grafana |
+| `make lift.mlflow` | Start optional MLflow server |
+| `make lift.database` | Start Postgres / MongoDB |
+| `make doctor` | Verify toolchain |
+
+Run `make help` for the full list.
+
+## Nx Workspace
+
+This template now ships with Nx project definitions for:
+
+- `webapp` (`apps/webapp`)
+- `webapp-minimal` (`apps/webapp-minimal`)
+- `backend-fastapi` (`apps/backend/fastapi`)
+- `backend-flask` (`apps/backend/flask`)
+- `worker` (`apps/worker`)
+- `ml` (`ml`)
+- `alveslib` (`alveslib`)
+
+Common commands:
+
+```bash
+bun x nx show projects
+bun x nx graph
+bun x nx run webapp:dev
+bun x nx affected -t lint,test,build
+```
+
+## AI Agent Capacity
+
+Set `ANTHROPIC_API_KEY` in `.env`. Then use:
+
+```python
+from alveslib import ask, stream, Agent
+
+# One-shot
+print(ask("Summarize this data: ..."))
+
+# Streaming
+for chunk in stream("Write a Celery task that ..."):
+ print(chunk, end="", flush=True)
+
+# Multi-turn
+agent = Agent(system="You are a senior Python developer.")
+agent.chat("Scaffold a FastAPI endpoint for user profiles")
+agent.chat("Add input validation and error handling")
+```
+
+Claude Code slash commands (type `/` in a Claude Code session):
+- `/plan` - implementation plan for an idea within this boilerplate
+- `/build` - implement a feature end-to-end
+- `/api` - scaffold a new backend endpoint
+- `/page` - scaffold a new Next.js page
+- `/review` - code review of recent changes
+- `/ship` - stage and commit changes
+
+## Logging
+
+```python
+from alveslib import get_logger
+logger = get_logger("service")
+```
+
+Outputs structured JSON to console + `./logs/`. Optional Loki push when `LOKI_PORT` is set and `make lift.logging` is running. View in Grafana at `http://localhost:$GRAFANA_PORT` (add Loki data source: `http://loki:3100`).
+
+## Python Packaging
+
+Python dependencies are managed with `pyproject.toml` and `uv`.
+
+```bash
+make deps # uv sync
+make lock # refresh uv.lock
+uv run pytest -v
+```
+
+## ML Workflow
+
+High-level ML hyperparameters live in YAML configs:
+
+- `ml/configs/data/default.yaml`
+- `ml/configs/train/default.yaml`
+
+Run with Nx targets (cacheable with explicit inputs/outputs):
+
+```bash
+bun x nx run ml:etl
+bun x nx run ml:train
+```
+
+`ml:train` depends on `ml:etl`, and both targets cache artifacts in `ml/data/processed`, `ml/models/weights`, and `ml/tensorboard`.
+
+## Services (docker compose profiles)
+
+| Profile | Services | Command |
+|---------|----------|---------|
+| _(default)_ | redis, ml-inference, worker | `make up` |
+| `minio` | + MinIO object storage | `make lift.minio` |
+| `tensorboard` | + TensorBoard | `make lift.tensorboard` |
+| `mlflow` | + MLflow tracking server (optional) | `make lift.mlflow` |
+| `logging` | + Loki + Grafana | `make lift.logging` |
+| `database` | + Postgres + MongoDB | `make lift.database` |
+
+## Webapp Auth
+
+Auth is off by default (`NEXT_PUBLIC_REQUIRE_AUTH=false`). Set it to `true` and configure Supabase keys to enable session-based auth gating across all routes.
diff --git a/alveslib/__init__.py b/alveslib/__init__.py
new file mode 100644
index 0000000..eab1c4c
--- /dev/null
+++ b/alveslib/__init__.py
@@ -0,0 +1,4 @@
+from .logger import get_logger
+from .agent import ask, stream, ask_async, stream_async, Agent
+
+__all__ = ["get_logger", "ask", "stream", "ask_async", "stream_async", "Agent"]
diff --git a/alveslib/agent.py b/alveslib/agent.py
new file mode 100644
index 0000000..a2d342d
--- /dev/null
+++ b/alveslib/agent.py
@@ -0,0 +1,139 @@
+"""
+Thin async + sync wrappers over the Anthropic SDK for quick scripting and agent
+patterns. Use this when you want direct API access with streaming; for full
+agentic loops with file tools use `claude-agent-sdk` (pip install claude-agent-sdk).
+
+Usage:
+ from alveslib.agent import ask, stream, Agent
+
+ # One-shot
+ reply = ask("Summarize this data: ...")
+
+ # Streaming to stdout
+ stream("Write a FastAPI endpoint that ...")
+
+ # Multi-turn agent
+ agent = Agent(system="You are an expert Python dev.")
+ reply = agent.chat("Generate a Celery task that processes CSV files")
+ follow = agent.chat("Now add error handling and retries")
+"""
+
+import os
+import asyncio
+from typing import Iterator, AsyncIterator
+
+try:
+ import anthropic
+
+ _client: anthropic.Anthropic | None = anthropic.Anthropic(
+ api_key=os.environ.get("ANTHROPIC_API_KEY")
+ )
+ _async_client: anthropic.AsyncAnthropic | None = anthropic.AsyncAnthropic(
+ api_key=os.environ.get("ANTHROPIC_API_KEY")
+ )
+except ImportError:
+ _client = None
+ _async_client = None
+
+
+DEFAULT_MODEL = "claude-sonnet-4-5"
+
+
+def _require_client() -> "anthropic.Anthropic":
+ if _client is None:
+ raise ImportError("pip install anthropic")
+ if not os.environ.get("ANTHROPIC_API_KEY"):
+ raise RuntimeError("ANTHROPIC_API_KEY not set")
+ return _client
+
+
+def ask(prompt: str, system: str = "", model: str = DEFAULT_MODEL) -> str:
+ """One-shot blocking request; returns full text."""
+ client = _require_client()
+ msg = client.messages.create(
+ model=model,
+ max_tokens=8096,
+ system=system or anthropic.NOT_GIVEN,
+ messages=[{"role": "user", "content": prompt}],
+ )
+ return msg.content[0].text
+
+
+def stream(prompt: str, system: str = "", model: str = DEFAULT_MODEL) -> Iterator[str]:
+ """Streaming generator; yields text deltas. Print as they arrive."""
+ client = _require_client()
+ with client.messages.stream(
+ model=model,
+ max_tokens=8096,
+ system=system or anthropic.NOT_GIVEN,
+ messages=[{"role": "user", "content": prompt}],
+ ) as s:
+ yield from s.text_stream
+
+
+async def ask_async(prompt: str, system: str = "", model: str = DEFAULT_MODEL) -> str:
+ """Async one-shot request."""
+ if _async_client is None:
+ raise ImportError("pip install anthropic")
+ msg = await _async_client.messages.create(
+ model=model,
+ max_tokens=8096,
+ system=system or anthropic.NOT_GIVEN,
+ messages=[{"role": "user", "content": prompt}],
+ )
+ return msg.content[0].text
+
+
+async def stream_async(
+ prompt: str, system: str = "", model: str = DEFAULT_MODEL
+) -> AsyncIterator[str]:
+ """Async streaming generator."""
+ if _async_client is None:
+ raise ImportError("pip install anthropic")
+ async with _async_client.messages.stream(
+ model=model,
+ max_tokens=8096,
+ system=system or anthropic.NOT_GIVEN,
+ messages=[{"role": "user", "content": prompt}],
+ ) as s:
+ async for text in s.text_stream:
+ yield text
+
+
+class Agent:
+ """Stateful multi-turn conversation agent with optional system prompt."""
+
+ def __init__(self, system: str = "", model: str = DEFAULT_MODEL):
+ self.system = system
+ self.model = model
+ self.history: list[dict] = []
+
+ def chat(self, prompt: str) -> str:
+ client = _require_client()
+ self.history.append({"role": "user", "content": prompt})
+ msg = client.messages.create(
+ model=self.model,
+ max_tokens=8096,
+ system=self.system or anthropic.NOT_GIVEN,
+ messages=self.history,
+ )
+ reply = msg.content[0].text
+ self.history.append({"role": "assistant", "content": reply})
+ return reply
+
+ async def chat_async(self, prompt: str) -> str:
+ if _async_client is None:
+ raise ImportError("pip install anthropic")
+ self.history.append({"role": "user", "content": prompt})
+ msg = await _async_client.messages.create(
+ model=self.model,
+ max_tokens=8096,
+ system=self.system or anthropic.NOT_GIVEN,
+ messages=self.history,
+ )
+ reply = msg.content[0].text
+ self.history.append({"role": "assistant", "content": reply})
+ return reply
+
+ def reset(self) -> None:
+ self.history.clear()
diff --git a/alveslib/logger.py b/alveslib/logger.py
new file mode 100644
index 0000000..eb66076
--- /dev/null
+++ b/alveslib/logger.py
@@ -0,0 +1,83 @@
+import json
+import logging
+import os
+from datetime import datetime
+from pathlib import Path
+
+try:
+ from dotenv import load_dotenv
+ load_dotenv()
+except ImportError:
+ pass
+
+try:
+ from logging_loki import LokiHandler
+ LOKI_AVAILABLE = True
+except ImportError:
+ LOKI_AVAILABLE = False
+
+def get_logger(service_name: str, level: str = "INFO") -> logging.Logger:
+ """
+ Get a configured logger for UltiPlate services.
+
+ Args:
+ service_name: Name of the service/module
+ level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
+
+ Returns:
+ Configured logger instance
+ """
+ logger = logging.getLogger(service_name)
+ logger.setLevel(getattr(logging, level.upper()))
+
+ if not logger.handlers:
+ # Console handler with JSON formatting
+ handler = logging.StreamHandler()
+ handler.setFormatter(JsonFormatter(service_name))
+ logger.addHandler(handler)
+
+ # File handler - writes to logs directory
+ logs_dir = Path(os.getenv("LOGDIR", "./logs"))
+ logs_dir.mkdir(parents=True, exist_ok=True)
+ file_handler = logging.FileHandler(logs_dir / f"{service_name}.log")
+ file_handler.setFormatter(JsonFormatter(service_name))
+ logger.addHandler(file_handler)
+
+ # Loki handler - sends logs directly to Loki
+ if LOKI_AVAILABLE:
+ loki_port = os.getenv("LOKI_PORT", "3100")
+ loki_url = f"http://localhost:{loki_port}/loki/api/v1/push"
+
+ try:
+ loki_handler = LokiHandler(
+ url=loki_url,
+ tags={"service": service_name},
+ version="1"
+ )
+ logger.addHandler(loki_handler)
+ except Exception as e:
+ # If Loki is not available, just continue with file/console logging
+ pass
+
+ return logger
+
+class JsonFormatter(logging.Formatter):
+ def __init__(self, service_name: str):
+ super().__init__()
+ self.service_name = service_name
+
+ def format(self, record):
+ log_entry = {
+ "timestamp": datetime.utcnow().isoformat() + "Z",
+ "service": self.service_name,
+ "level": record.levelname,
+ "message": record.getMessage(),
+ "module": record.module,
+ "function": record.funcName,
+ "line": record.lineno
+ }
+
+ if record.exc_info:
+ log_entry["exception"] = self.formatException(record.exc_info)
+
+ return json.dumps(log_entry)
diff --git a/alveslib/project.json b/alveslib/project.json
new file mode 100644
index 0000000..c501684
--- /dev/null
+++ b/alveslib/project.json
@@ -0,0 +1,32 @@
+{
+ "name": "alveslib",
+ "root": "alveslib",
+ "sourceRoot": "alveslib",
+ "projectType": "library",
+ "targets": {
+ "build": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": "python3.12 -m compileall alveslib"
+ }
+ },
+ "lint": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/ruff check alveslib"
+ }
+ },
+ "typecheck": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/mypy alveslib"
+ }
+ },
+ "test": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/pytest alveslib -v"
+ }
+ }
+ }
+}
diff --git a/alveslib/scraper.py b/alveslib/scraper.py
new file mode 100644
index 0000000..7849dec
--- /dev/null
+++ b/alveslib/scraper.py
@@ -0,0 +1,70 @@
+import hashlib
+import pickle
+import os
+from pathlib import Path
+from seleniumbase import SB
+from bs4 import BeautifulSoup
+from typing import Optional
+
+class ScraperCache:
+ def __init__(self, cache_dir: str = ".scraper_cache"):
+ self.cache_dir = Path(cache_dir)
+ self.cache_dir.mkdir(exist_ok=True)
+
+ def _get_cache_key(self, url: str) -> str:
+ return hashlib.md5(url.encode()).hexdigest()
+
+ def _get_cache_path(self, cache_key: str) -> Path:
+ return self.cache_dir / f"{cache_key}.pkl"
+
+ def get(self, url: str) -> Optional[BeautifulSoup]:
+ cache_key = self._get_cache_key(url)
+ cache_path = self._get_cache_path(cache_key)
+
+ if cache_path.exists():
+ try:
+ with open(cache_path, 'rb') as f:
+ return pickle.load(f)
+ except:
+ pass
+ return None
+
+ def set(self, url: str, soup: BeautifulSoup) -> None:
+ cache_key = self._get_cache_key(url)
+ cache_path = self._get_cache_path(cache_key)
+
+ try:
+ with open(cache_path, 'wb') as f:
+ pickle.dump(soup, f)
+ except:
+ pass
+
+_cache = ScraperCache() # glob
+
+def scrape_url(url: str, use_cache: bool = True) -> BeautifulSoup:
+ if use_cache:
+ cached_soup = _cache.get(url)
+ if cached_soup:
+ return cached_soup
+
+ with SB(test=True, uc=True) as sb:
+ sb.open(url)
+ html = sb.get_page_source()
+ soup = BeautifulSoup(html, 'html.parser')
+
+ if use_cache:
+ _cache.set(url, soup)
+
+ return soup
+
+
+
+if __name__ == "__main__":
+ url = "https://httpbin.org/html"
+ print("Testing scraper...")
+ soup = scrape_url(url)
+ print(f"Title: {soup.title.text if soup.title else 'No title'}")
+ print(f"Found {len(soup.find_all('p'))} paragraphs")
+ print("\nTesting cache...")
+ soup2 = scrape_url(url)
+ print("Cache test completed")
diff --git a/apps/backend/fastapi/project.json b/apps/backend/fastapi/project.json
new file mode 100644
index 0000000..cdf6d9c
--- /dev/null
+++ b/apps/backend/fastapi/project.json
@@ -0,0 +1,40 @@
+{
+ "name": "backend-fastapi",
+ "root": "apps/backend/fastapi",
+ "sourceRoot": "apps/backend/fastapi",
+ "projectType": "application",
+ "implicitDependencies": ["alveslib"],
+ "targets": {
+ "dev": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/backend/fastapi",
+ "command": "python3.12 server.py"
+ }
+ },
+ "build": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": "python3.12 -m compileall apps/backend/fastapi"
+ }
+ },
+ "lint": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/ruff check apps/backend/fastapi"
+ }
+ },
+ "typecheck": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/mypy apps/backend/fastapi"
+ }
+ },
+ "test": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/pytest apps/backend/fastapi -v"
+ }
+ }
+ }
+}
diff --git a/apps/backend/fastapi/server.py b/apps/backend/fastapi/server.py
new file mode 100644
index 0000000..931ef43
--- /dev/null
+++ b/apps/backend/fastapi/server.py
@@ -0,0 +1,24 @@
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+import uvicorn
+import os
+from dotenv import load_dotenv
+load_dotenv()
+
+app = FastAPI()
+
+app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+)
+
+@app.get("/health")
+async def health():
+ return {"status": "healthy"}
+
+if __name__ == "__main__":
+ PORT=int(os.getenv("BACKEND_PORT", 5000))
+ uvicorn.run("server:app", host="0.0.0.0", port=PORT, reload=True)
diff --git a/apps/backend/flask/project.json b/apps/backend/flask/project.json
new file mode 100644
index 0000000..694219b
--- /dev/null
+++ b/apps/backend/flask/project.json
@@ -0,0 +1,40 @@
+{
+ "name": "backend-flask",
+ "root": "apps/backend/flask",
+ "sourceRoot": "apps/backend/flask",
+ "projectType": "application",
+ "implicitDependencies": ["alveslib"],
+ "targets": {
+ "dev": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/backend/flask",
+ "command": "python3.12 server.py"
+ }
+ },
+ "build": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": "python3.12 -m compileall apps/backend/flask"
+ }
+ },
+ "lint": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/ruff check apps/backend/flask"
+ }
+ },
+ "typecheck": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/mypy apps/backend/flask"
+ }
+ },
+ "test": {
+ "executor": "nx:run-commands",
+ "options": {
+ "command": ".venv/bin/pytest apps/backend/flask -v"
+ }
+ }
+ }
+}
diff --git a/apps/backend/flask/server.py b/apps/backend/flask/server.py
new file mode 100644
index 0000000..48d770c
--- /dev/null
+++ b/apps/backend/flask/server.py
@@ -0,0 +1,17 @@
+from flask import Flask
+from flask_cors import CORS
+import os
+from dotenv import load_dotenv
+load_dotenv()
+
+app = Flask(__name__)
+CORS(app, origins="*")
+
+@app.route('/health')
+def health():
+ return {'status': 'healthy'}, 200
+
+
+if __name__ == '__main__':
+ PORT=int(os.getenv("BACKEND_PORT", 5000))
+ app.run(host='0.0.0.0', port=PORT, debug=True)
diff --git a/apps/webapp-minimal/app.py b/apps/webapp-minimal/app.py
new file mode 100644
index 0000000..e5aeddb
--- /dev/null
+++ b/apps/webapp-minimal/app.py
@@ -0,0 +1 @@
+import streamlit as st
diff --git a/apps/webapp-minimal/project.json b/apps/webapp-minimal/project.json
new file mode 100644
index 0000000..a0496ac
--- /dev/null
+++ b/apps/webapp-minimal/project.json
@@ -0,0 +1,15 @@
+{
+ "name": "webapp-minimal",
+ "root": "apps/webapp-minimal",
+ "sourceRoot": "apps/webapp-minimal",
+ "projectType": "application",
+ "targets": {
+ "dev": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/webapp-minimal",
+ "command": "streamlit run app.py"
+ }
+ }
+ }
+}
diff --git a/apps/webapp/.gitignore b/apps/webapp/.gitignore
new file mode 100644
index 0000000..c85ed52
--- /dev/null
+++ b/apps/webapp/.gitignore
@@ -0,0 +1,16 @@
+/node_modules
+/.next/
+/out/
+/build
+/coverage
+.DS_Store
+*.pem
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+.env*
+.vercel
+*.tsbuildinfo
+next-env.d.ts
+package-lock.json
diff --git a/apps/webapp/README.md b/apps/webapp/README.md
new file mode 100644
index 0000000..bd249d5
--- /dev/null
+++ b/apps/webapp/README.md
@@ -0,0 +1,36 @@
+Next.js 15 webapp with React 19, Tailwind CSS 4, Supabase auth, and Turbopack.
+
+## Start
+
+```bash
+bun dev
+# or from project root: make run.webapp
+```
+
+Open [http://localhost:3000](http://localhost:3000).
+
+## Scripts
+
+| Command | Description |
+|---------|-------------|
+| `bun dev` | Dev server with Turbopack |
+| `bun build` | Production build |
+| `bun start` | Serve production build |
+| `bun lint` | Run ESLint |
+| `bun typecheck` | Run tsc --noEmit |
+
+## Auth
+
+Auth is wired to Supabase via `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`.
+Set `NEXT_PUBLIC_REQUIRE_AUTH=false` in `.env` to disable auth-gating entirely (default for quick prototyping).
+
+## Structure
+
+```
+src/
+ app/ - Next.js App Router pages
+ components/ - Reusable bare-bones components (style last)
+ utils/supabase - Supabase client helpers
+ libs/ - Shared utilities
+ locales/ - i18n strings (add languages as needed)
+```
diff --git a/apps/webapp/bun.lock b/apps/webapp/bun.lock
new file mode 100644
index 0000000..59521c1
--- /dev/null
+++ b/apps/webapp/bun.lock
@@ -0,0 +1,862 @@
+{
+ "lockfileVersion": 1,
+ "configVersion": 0,
+ "workspaces": {
+ "": {
+ "name": "webapp",
+ "dependencies": {
+ "@supabase/ssr": "^0.7.0",
+ "@supabase/supabase-js": "^2.57.4",
+ "next": "15.5.2",
+ "react": "19.1.0",
+ "react-dom": "19.1.0",
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3",
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "15.5.2",
+ "tailwindcss": "^4",
+ "typescript": "^5",
+ },
+ },
+ },
+ "packages": {
+ "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
+
+ "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
+
+ "@emnapi/runtime": ["@emnapi/runtime@1.5.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ=="],
+
+ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
+
+ "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.0", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g=="],
+
+ "@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.1", "", {}, "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="],
+
+ "@eslint/config-array": ["@eslint/config-array@0.21.0", "", { "dependencies": { "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" } }, "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ=="],
+
+ "@eslint/config-helpers": ["@eslint/config-helpers@0.3.1", "", {}, "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA=="],
+
+ "@eslint/core": ["@eslint/core@0.15.2", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg=="],
+
+ "@eslint/eslintrc": ["@eslint/eslintrc@3.3.1", "", { "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" } }, "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ=="],
+
+ "@eslint/js": ["@eslint/js@9.35.0", "", {}, "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw=="],
+
+ "@eslint/object-schema": ["@eslint/object-schema@2.1.6", "", {}, "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="],
+
+ "@eslint/plugin-kit": ["@eslint/plugin-kit@0.3.5", "", { "dependencies": { "@eslint/core": "^0.15.2", "levn": "^0.4.1" } }, "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w=="],
+
+ "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="],
+
+ "@humanfs/node": ["@humanfs/node@0.16.7", "", { "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ=="],
+
+ "@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
+
+ "@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
+
+ "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.0" }, "os": "darwin", "cpu": "arm64" }, "sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg=="],
+
+ "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.0" }, "os": "darwin", "cpu": "x64" }, "sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA=="],
+
+ "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ=="],
+
+ "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg=="],
+
+ "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.0", "", { "os": "linux", "cpu": "arm" }, "sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw=="],
+
+ "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA=="],
+
+ "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ=="],
+
+ "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw=="],
+
+ "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg=="],
+
+ "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q=="],
+
+ "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q=="],
+
+ "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.0" }, "os": "linux", "cpu": "arm" }, "sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A=="],
+
+ "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA=="],
+
+ "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.0" }, "os": "linux", "cpu": "ppc64" }, "sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA=="],
+
+ "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.0" }, "os": "linux", "cpu": "s390x" }, "sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ=="],
+
+ "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ=="],
+
+ "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.0" }, "os": "linux", "cpu": "arm64" }, "sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ=="],
+
+ "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.3", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.0" }, "os": "linux", "cpu": "x64" }, "sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ=="],
+
+ "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.3", "", { "dependencies": { "@emnapi/runtime": "^1.4.4" }, "cpu": "none" }, "sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg=="],
+
+ "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ=="],
+
+ "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw=="],
+
+ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.3", "", { "os": "win32", "cpu": "x64" }, "sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g=="],
+
+ "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
+
+ "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
+
+ "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
+
+ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
+
+ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
+
+ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.30", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q=="],
+
+ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
+
+ "@next/env": ["@next/env@15.5.2", "", {}, "sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg=="],
+
+ "@next/eslint-plugin-next": ["@next/eslint-plugin-next@15.5.2", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-lkLrRVxcftuOsJNhWatf1P2hNVfh98k/omQHrCEPPriUypR6RcS13IvLdIrEvkm9AH2Nu2YpR5vLqBuy6twH3Q=="],
+
+ "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ=="],
+
+ "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ=="],
+
+ "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA=="],
+
+ "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g=="],
+
+ "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.2", "", { "os": "linux", "cpu": "x64" }, "sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q=="],
+
+ "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.2", "", { "os": "linux", "cpu": "x64" }, "sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g=="],
+
+ "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg=="],
+
+ "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.2", "", { "os": "win32", "cpu": "x64" }, "sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q=="],
+
+ "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
+
+ "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
+
+ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
+
+ "@nolyfill/is-core-module": ["@nolyfill/is-core-module@1.0.39", "", {}, "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA=="],
+
+ "@rtsao/scc": ["@rtsao/scc@1.1.0", "", {}, "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g=="],
+
+ "@rushstack/eslint-patch": ["@rushstack/eslint-patch@1.12.0", "", {}, "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw=="],
+
+ "@supabase/auth-js": ["@supabase/auth-js@2.71.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-mMIQHBRc+SKpZFRB2qtupuzulaUhFYupNyxqDj5Jp/LyPvcWvjaJzZzObv6URtL/O6lPxkanASnotGtNpS3H2Q=="],
+
+ "@supabase/functions-js": ["@supabase/functions-js@2.4.6", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-bhjZ7rmxAibjgmzTmQBxJU6ZIBCCJTc3Uwgvdi4FewueUTAGO5hxZT1Sj6tiD+0dSXf9XI87BDdJrg12z8Uaew=="],
+
+ "@supabase/node-fetch": ["@supabase/node-fetch@2.6.15", "", { "dependencies": { "whatwg-url": "^5.0.0" } }, "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ=="],
+
+ "@supabase/postgrest-js": ["@supabase/postgrest-js@1.21.4", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-TxZCIjxk6/dP9abAi89VQbWWMBbybpGWyvmIzTd79OeravM13OjR/YEYeyUOPcM1C3QyvXkvPZhUfItvmhY1IQ=="],
+
+ "@supabase/realtime-js": ["@supabase/realtime-js@2.15.5", "", { "dependencies": { "@supabase/node-fetch": "^2.6.13", "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "ws": "^8.18.2" } }, "sha512-/Rs5Vqu9jejRD8ZeuaWXebdkH+J7V6VySbCZ/zQM93Ta5y3mAmocjioa/nzlB6qvFmyylUgKVS1KpE212t30OA=="],
+
+ "@supabase/ssr": ["@supabase/ssr@0.7.0", "", { "dependencies": { "cookie": "^1.0.2" }, "peerDependencies": { "@supabase/supabase-js": "^2.43.4" } }, "sha512-G65t5EhLSJ5c8hTCcXifSL9Q/ZRXvqgXeNo+d3P56f4U1IxwTqjB64UfmfixvmMcjuxnq2yGqEWVJqUcO+AzAg=="],
+
+ "@supabase/storage-js": ["@supabase/storage-js@2.12.1", "", { "dependencies": { "@supabase/node-fetch": "^2.6.14" } }, "sha512-QWg3HV6Db2J81VQx0PqLq0JDBn4Q8B1FYn1kYcbla8+d5WDmTdwwMr+EJAxNOSs9W4mhKMv+EYCpCrTFlTj4VQ=="],
+
+ "@supabase/supabase-js": ["@supabase/supabase-js@2.57.4", "", { "dependencies": { "@supabase/auth-js": "2.71.1", "@supabase/functions-js": "2.4.6", "@supabase/node-fetch": "2.6.15", "@supabase/postgrest-js": "1.21.4", "@supabase/realtime-js": "2.15.5", "@supabase/storage-js": "2.12.1" } }, "sha512-LcbTzFhHYdwfQ7TRPfol0z04rLEyHabpGYANME6wkQ/kLtKNmI+Vy+WEM8HxeOZAtByUFxoUTTLwhXmrh+CcVw=="],
+
+ "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
+
+ "@tailwindcss/node": ["@tailwindcss/node@4.1.13", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "enhanced-resolve": "^5.18.3", "jiti": "^2.5.1", "lightningcss": "1.30.1", "magic-string": "^0.30.18", "source-map-js": "^1.2.1", "tailwindcss": "4.1.13" } }, "sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw=="],
+
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.13", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.13", "@tailwindcss/oxide-darwin-arm64": "4.1.13", "@tailwindcss/oxide-darwin-x64": "4.1.13", "@tailwindcss/oxide-freebsd-x64": "4.1.13", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.13", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.13", "@tailwindcss/oxide-linux-arm64-musl": "4.1.13", "@tailwindcss/oxide-linux-x64-gnu": "4.1.13", "@tailwindcss/oxide-linux-x64-musl": "4.1.13", "@tailwindcss/oxide-wasm32-wasi": "4.1.13", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.13", "@tailwindcss/oxide-win32-x64-msvc": "4.1.13" } }, "sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA=="],
+
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.13", "", { "os": "android", "cpu": "arm64" }, "sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew=="],
+
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.13", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ=="],
+
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.13", "", { "os": "darwin", "cpu": "x64" }, "sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw=="],
+
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.13", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ=="],
+
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13", "", { "os": "linux", "cpu": "arm" }, "sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw=="],
+
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ=="],
+
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.13", "", { "os": "linux", "cpu": "arm64" }, "sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg=="],
+
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.13", "", { "os": "linux", "cpu": "x64" }, "sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ=="],
+
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.13", "", { "os": "linux", "cpu": "x64" }, "sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ=="],
+
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.13", "", { "cpu": "none" }, "sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA=="],
+
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.13", "", { "os": "win32", "cpu": "arm64" }, "sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg=="],
+
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.13", "", { "os": "win32", "cpu": "x64" }, "sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw=="],
+
+ "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.13", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.13", "@tailwindcss/oxide": "4.1.13", "postcss": "^8.4.41", "tailwindcss": "4.1.13" } }, "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ=="],
+
+ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
+
+ "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
+
+ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
+
+ "@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
+
+ "@types/node": ["@types/node@20.19.13", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g=="],
+
+ "@types/phoenix": ["@types/phoenix@1.6.6", "", {}, "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A=="],
+
+ "@types/react": ["@types/react@19.1.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w=="],
+
+ "@types/react-dom": ["@types/react-dom@19.1.9", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ=="],
+
+ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
+
+ "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.43.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.10.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/type-utils": "8.43.0", "@typescript-eslint/utils": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.43.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-8tg+gt7ENL7KewsKMKDHXR1vm8tt9eMxjJBYINf6swonlWgkYn5NwyIgXpbbDxTNU5DgpDFfj95prcTq2clIQQ=="],
+
+ "@typescript-eslint/parser": ["@typescript-eslint/parser@8.43.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-B7RIQiTsCBBmY+yW4+ILd6mF5h1FUwJsVvpqkrgpszYifetQ2Ke+Z4u6aZh0CblkUGIdR59iYVyXqqZGkZ3aBw=="],
+
+ "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.43.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.43.0", "@typescript-eslint/types": "^8.43.0", "debug": "^4.3.4" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-htB/+D/BIGoNTQYffZw4uM4NzzuolCoaA/BusuSIcC8YjmBYQioew5VUZAYdAETPjeed0hqCaW7EHg+Robq8uw=="],
+
+ "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0" } }, "sha512-daSWlQ87ZhsjrbMLvpuuMAt3y4ba57AuvadcR7f3nl8eS3BjRc8L9VLxFLk92RL5xdXOg6IQ+qKjjqNEimGuAg=="],
+
+ "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.43.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-ALC2prjZcj2YqqL5X/bwWQmHA2em6/94GcbB/KKu5SX3EBDOsqztmmX1kMkvAJHzxk7TazKzJfFiEIagNV3qEA=="],
+
+ "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0", "@typescript-eslint/utils": "8.43.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-qaH1uLBpBuBBuRf8c1mLJ6swOfzCXryhKND04Igr4pckzSEW9JX5Aw9AgW00kwfjWJF0kk0ps9ExKTfvXfw4Qg=="],
+
+ "@typescript-eslint/types": ["@typescript-eslint/types@8.43.0", "", {}, "sha512-vQ2FZaxJpydjSZJKiSW/LJsabFFvV7KgLC5DiLhkBcykhQj8iK9BOaDmQt74nnKdLvceM5xmhaTF+pLekrxEkw=="],
+
+ "@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.43.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.43.0", "@typescript-eslint/tsconfig-utils": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/visitor-keys": "8.43.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "sha512-7Vv6zlAhPb+cvEpP06WXXy/ZByph9iL6BQRBDj4kmBsW98AqEeQHlj/13X+sZOrKSo9/rNKH4Ul4f6EICREFdw=="],
+
+ "@typescript-eslint/utils": ["@typescript-eslint/utils@8.43.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", "@typescript-eslint/scope-manager": "8.43.0", "@typescript-eslint/types": "8.43.0", "@typescript-eslint/typescript-estree": "8.43.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-S1/tEmkUeeswxd0GGcnwuVQPFWo8NzZTOMxCvw8BX7OMxnNae+i8Tm7REQen/SwUIPoPqfKn7EaZ+YLpiB3k9g=="],
+
+ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.43.0", "", { "dependencies": { "@typescript-eslint/types": "8.43.0", "eslint-visitor-keys": "^4.2.1" } }, "sha512-T+S1KqRD4sg/bHfLwrpF/K3gQLBM1n7Rp7OjjikjTEssI2YJzQpi5WXoynOaQ93ERIuq3O8RBTOUYDKszUCEHw=="],
+
+ "@unrs/resolver-binding-android-arm-eabi": ["@unrs/resolver-binding-android-arm-eabi@1.11.1", "", { "os": "android", "cpu": "arm" }, "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw=="],
+
+ "@unrs/resolver-binding-android-arm64": ["@unrs/resolver-binding-android-arm64@1.11.1", "", { "os": "android", "cpu": "arm64" }, "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g=="],
+
+ "@unrs/resolver-binding-darwin-arm64": ["@unrs/resolver-binding-darwin-arm64@1.11.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g=="],
+
+ "@unrs/resolver-binding-darwin-x64": ["@unrs/resolver-binding-darwin-x64@1.11.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ=="],
+
+ "@unrs/resolver-binding-freebsd-x64": ["@unrs/resolver-binding-freebsd-x64@1.11.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw=="],
+
+ "@unrs/resolver-binding-linux-arm-gnueabihf": ["@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw=="],
+
+ "@unrs/resolver-binding-linux-arm-musleabihf": ["@unrs/resolver-binding-linux-arm-musleabihf@1.11.1", "", { "os": "linux", "cpu": "arm" }, "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw=="],
+
+ "@unrs/resolver-binding-linux-arm64-gnu": ["@unrs/resolver-binding-linux-arm64-gnu@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ=="],
+
+ "@unrs/resolver-binding-linux-arm64-musl": ["@unrs/resolver-binding-linux-arm64-musl@1.11.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w=="],
+
+ "@unrs/resolver-binding-linux-ppc64-gnu": ["@unrs/resolver-binding-linux-ppc64-gnu@1.11.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA=="],
+
+ "@unrs/resolver-binding-linux-riscv64-gnu": ["@unrs/resolver-binding-linux-riscv64-gnu@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ=="],
+
+ "@unrs/resolver-binding-linux-riscv64-musl": ["@unrs/resolver-binding-linux-riscv64-musl@1.11.1", "", { "os": "linux", "cpu": "none" }, "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew=="],
+
+ "@unrs/resolver-binding-linux-s390x-gnu": ["@unrs/resolver-binding-linux-s390x-gnu@1.11.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg=="],
+
+ "@unrs/resolver-binding-linux-x64-gnu": ["@unrs/resolver-binding-linux-x64-gnu@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w=="],
+
+ "@unrs/resolver-binding-linux-x64-musl": ["@unrs/resolver-binding-linux-x64-musl@1.11.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA=="],
+
+ "@unrs/resolver-binding-wasm32-wasi": ["@unrs/resolver-binding-wasm32-wasi@1.11.1", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.11" }, "cpu": "none" }, "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ=="],
+
+ "@unrs/resolver-binding-win32-arm64-msvc": ["@unrs/resolver-binding-win32-arm64-msvc@1.11.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw=="],
+
+ "@unrs/resolver-binding-win32-ia32-msvc": ["@unrs/resolver-binding-win32-ia32-msvc@1.11.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ=="],
+
+ "@unrs/resolver-binding-win32-x64-msvc": ["@unrs/resolver-binding-win32-x64-msvc@1.11.1", "", { "os": "win32", "cpu": "x64" }, "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g=="],
+
+ "acorn": ["acorn@8.15.0", "", { "bin": "bin/acorn" }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
+
+ "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
+
+ "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
+
+ "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
+
+ "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
+
+ "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
+
+ "array-buffer-byte-length": ["array-buffer-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "is-array-buffer": "^3.0.5" } }, "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw=="],
+
+ "array-includes": ["array-includes@3.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.0", "es-object-atoms": "^1.1.1", "get-intrinsic": "^1.3.0", "is-string": "^1.1.1", "math-intrinsics": "^1.1.0" } }, "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ=="],
+
+ "array.prototype.findlast": ["array.prototype.findlast@1.2.5", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ=="],
+
+ "array.prototype.findlastindex": ["array.prototype.findlastindex@1.2.6", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-shim-unscopables": "^1.1.0" } }, "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ=="],
+
+ "array.prototype.flat": ["array.prototype.flat@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg=="],
+
+ "array.prototype.flatmap": ["array.prototype.flatmap@1.3.3", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-shim-unscopables": "^1.0.2" } }, "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg=="],
+
+ "array.prototype.tosorted": ["array.prototype.tosorted@1.1.4", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3", "es-errors": "^1.3.0", "es-shim-unscopables": "^1.0.2" } }, "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA=="],
+
+ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="],
+
+ "ast-types-flow": ["ast-types-flow@0.0.8", "", {}, "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ=="],
+
+ "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="],
+
+ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
+
+ "axe-core": ["axe-core@4.10.3", "", {}, "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg=="],
+
+ "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
+
+ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
+
+ "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="],
+
+ "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
+
+ "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="],
+
+ "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
+
+ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
+
+ "callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
+
+ "caniuse-lite": ["caniuse-lite@1.0.30001741", "", {}, "sha512-QGUGitqsc8ARjLdgAfxETDhRbJ0REsP6O3I96TAth/mVjh2cYzN2u+3AzPP3aVSm2FehEItaJw1xd+IGBXWeSw=="],
+
+ "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
+
+ "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
+
+ "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="],
+
+ "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="],
+
+ "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
+
+ "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
+
+ "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="],
+
+ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
+
+ "cookie": ["cookie@1.0.2", "", {}, "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA=="],
+
+ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
+
+ "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
+
+ "damerau-levenshtein": ["damerau-levenshtein@1.0.8", "", {}, "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA=="],
+
+ "data-view-buffer": ["data-view-buffer@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ=="],
+
+ "data-view-byte-length": ["data-view-byte-length@1.0.2", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-data-view": "^1.0.2" } }, "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ=="],
+
+ "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="],
+
+ "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
+
+ "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
+
+ "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="],
+
+ "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="],
+
+ "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="],
+
+ "doctrine": ["doctrine@2.1.0", "", { "dependencies": { "esutils": "^2.0.2" } }, "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw=="],
+
+ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
+
+ "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="],
+
+ "enhanced-resolve": ["enhanced-resolve@5.18.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww=="],
+
+ "es-abstract": ["es-abstract@1.24.0", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg=="],
+
+ "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
+
+ "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
+
+ "es-iterator-helpers": ["es-iterator-helpers@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.0.3", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.6", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.4", "safe-array-concat": "^1.1.3" } }, "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w=="],
+
+ "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
+
+ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
+
+ "es-shim-unscopables": ["es-shim-unscopables@1.1.0", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw=="],
+
+ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="],
+
+ "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
+
+ "eslint": ["eslint@9.35.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.0", "@eslint/config-helpers": "^0.3.1", "@eslint/core": "^0.15.2", "@eslint/eslintrc": "^3.3.1", "@eslint/js": "9.35.0", "@eslint/plugin-kit": "^0.3.5", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "bin": "bin/eslint.js" }, "sha512-QePbBFMJFjgmlE+cXAlbHZbHpdFVS2E/6vzCy7aKlebddvl1vadiC4JFV5u/wqTkNUwEV8WrQi257jf5f06hrg=="],
+
+ "eslint-config-next": ["eslint-config-next@15.5.2", "", { "dependencies": { "@next/eslint-plugin-next": "15.5.2", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "eslint-import-resolver-node": "^0.3.6", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsx-a11y": "^6.10.0", "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0" }, "peerDependencies": { "eslint": "^7.23.0 || ^8.0.0 || ^9.0.0", "typescript": ">=3.3.1" } }, "sha512-3hPZghsLupMxxZ2ggjIIrat/bPniM2yRpsVPVM40rp8ZMzKWOJp2CGWn7+EzoV2ddkUr5fxNfHpF+wU1hGt/3g=="],
+
+ "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.9", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.13.0", "resolve": "^1.22.4" } }, "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g=="],
+
+ "eslint-import-resolver-typescript": ["eslint-import-resolver-typescript@3.10.1", "", { "dependencies": { "@nolyfill/is-core-module": "1.0.39", "debug": "^4.4.0", "get-tsconfig": "^4.10.0", "is-bun-module": "^2.0.0", "stable-hash": "^0.0.5", "tinyglobby": "^0.2.13", "unrs-resolver": "^1.6.2" }, "peerDependencies": { "eslint": "*", "eslint-plugin-import": "*", "eslint-plugin-import-x": "*" }, "optionalPeers": ["eslint-plugin-import-x"] }, "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ=="],
+
+ "eslint-module-utils": ["eslint-module-utils@2.12.1", "", { "dependencies": { "debug": "^3.2.7" } }, "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw=="],
+
+ "eslint-plugin-import": ["eslint-plugin-import@2.32.0", "", { "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", "array.prototype.findlastindex": "^1.2.6", "array.prototype.flat": "^1.3.3", "array.prototype.flatmap": "^1.3.3", "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.9", "eslint-module-utils": "^2.12.1", "hasown": "^2.0.2", "is-core-module": "^2.16.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "object.groupby": "^1.0.3", "object.values": "^1.2.1", "semver": "^6.3.1", "string.prototype.trimend": "^1.0.9", "tsconfig-paths": "^3.15.0" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" } }, "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA=="],
+
+ "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="],
+
+ "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="],
+
+ "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@5.2.0", "", { "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg=="],
+
+ "eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
+
+ "eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
+
+ "espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
+
+ "esquery": ["esquery@1.6.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg=="],
+
+ "esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
+
+ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
+
+ "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
+
+ "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
+
+ "fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
+
+ "fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
+
+ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
+
+ "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="],
+
+ "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
+
+ "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
+
+ "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
+
+ "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
+
+ "flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
+
+ "flatted": ["flatted@3.3.3", "", {}, "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg=="],
+
+ "for-each": ["for-each@0.3.5", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="],
+
+ "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
+
+ "function.prototype.name": ["function.prototype.name@1.1.8", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "functions-have-names": "^1.2.3", "hasown": "^2.0.2", "is-callable": "^1.2.7" } }, "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q=="],
+
+ "functions-have-names": ["functions-have-names@1.2.3", "", {}, "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="],
+
+ "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
+
+ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
+
+ "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="],
+
+ "get-tsconfig": ["get-tsconfig@4.10.1", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ=="],
+
+ "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
+
+ "globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
+
+ "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="],
+
+ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
+
+ "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
+
+ "graphemer": ["graphemer@1.4.0", "", {}, "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag=="],
+
+ "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="],
+
+ "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
+
+ "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="],
+
+ "has-proto": ["has-proto@1.2.0", "", { "dependencies": { "dunder-proto": "^1.0.0" } }, "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ=="],
+
+ "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
+
+ "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
+
+ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
+
+ "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
+
+ "import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
+
+ "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
+
+ "internal-slot": ["internal-slot@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "hasown": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw=="],
+
+ "is-array-buffer": ["is-array-buffer@3.0.5", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A=="],
+
+ "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="],
+
+ "is-async-function": ["is-async-function@2.1.1", "", { "dependencies": { "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ=="],
+
+ "is-bigint": ["is-bigint@1.1.0", "", { "dependencies": { "has-bigints": "^1.0.2" } }, "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ=="],
+
+ "is-boolean-object": ["is-boolean-object@1.2.2", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A=="],
+
+ "is-bun-module": ["is-bun-module@2.0.0", "", { "dependencies": { "semver": "^7.7.1" } }, "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ=="],
+
+ "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="],
+
+ "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="],
+
+ "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="],
+
+ "is-date-object": ["is-date-object@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "has-tostringtag": "^1.0.2" } }, "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg=="],
+
+ "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
+
+ "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="],
+
+ "is-generator-function": ["is-generator-function@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ=="],
+
+ "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
+
+ "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="],
+
+ "is-negative-zero": ["is-negative-zero@2.0.3", "", {}, "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw=="],
+
+ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
+
+ "is-number-object": ["is-number-object@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw=="],
+
+ "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="],
+
+ "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="],
+
+ "is-shared-array-buffer": ["is-shared-array-buffer@1.0.4", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A=="],
+
+ "is-string": ["is-string@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3", "has-tostringtag": "^1.0.2" } }, "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA=="],
+
+ "is-symbol": ["is-symbol@1.1.1", "", { "dependencies": { "call-bound": "^1.0.2", "has-symbols": "^1.1.0", "safe-regex-test": "^1.1.0" } }, "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w=="],
+
+ "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="],
+
+ "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="],
+
+ "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="],
+
+ "is-weakset": ["is-weakset@2.0.4", "", { "dependencies": { "call-bound": "^1.0.3", "get-intrinsic": "^1.2.6" } }, "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ=="],
+
+ "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
+
+ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
+
+ "iterator.prototype": ["iterator.prototype@1.1.5", "", { "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "get-proto": "^1.0.0", "has-symbols": "^1.1.0", "set-function-name": "^2.0.2" } }, "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g=="],
+
+ "jiti": ["jiti@2.5.1", "", { "bin": "lib/jiti-cli.mjs" }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
+
+ "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
+
+ "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
+
+ "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
+
+ "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
+
+ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
+
+ "json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": "lib/cli.js" }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="],
+
+ "jsx-ast-utils": ["jsx-ast-utils@3.3.5", "", { "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", "object.assign": "^4.1.4", "object.values": "^1.1.6" } }, "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ=="],
+
+ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
+
+ "language-subtag-registry": ["language-subtag-registry@0.3.23", "", {}, "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ=="],
+
+ "language-tags": ["language-tags@1.0.9", "", { "dependencies": { "language-subtag-registry": "^0.3.20" } }, "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA=="],
+
+ "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
+
+ "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="],
+
+ "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="],
+
+ "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="],
+
+ "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="],
+
+ "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="],
+
+ "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="],
+
+ "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="],
+
+ "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="],
+
+ "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="],
+
+ "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="],
+
+ "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="],
+
+ "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
+
+ "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
+
+ "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": "cli.js" }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
+
+ "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="],
+
+ "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
+
+ "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
+
+ "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
+
+ "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="],
+
+ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
+
+ "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="],
+
+ "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="],
+
+ "mkdirp": ["mkdirp@3.0.1", "", { "bin": "dist/cjs/src/bin.js" }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="],
+
+ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
+
+ "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
+
+ "napi-postinstall": ["napi-postinstall@0.3.3", "", { "bin": "lib/cli.js" }, "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow=="],
+
+ "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
+
+ "next": ["next@15.5.2", "", { "dependencies": { "@next/env": "15.5.2", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.2", "@next/swc-darwin-x64": "15.5.2", "@next/swc-linux-arm64-gnu": "15.5.2", "@next/swc-linux-arm64-musl": "15.5.2", "@next/swc-linux-x64-gnu": "15.5.2", "@next/swc-linux-x64-musl": "15.5.2", "@next/swc-win32-arm64-msvc": "15.5.2", "@next/swc-win32-x64-msvc": "15.5.2", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": "dist/bin/next" }, "sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q=="],
+
+ "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
+
+ "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="],
+
+ "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="],
+
+ "object.assign": ["object.assign@4.1.7", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0", "has-symbols": "^1.1.0", "object-keys": "^1.1.1" } }, "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw=="],
+
+ "object.entries": ["object.entries@1.1.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-object-atoms": "^1.1.1" } }, "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw=="],
+
+ "object.fromentries": ["object.fromentries@2.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2", "es-object-atoms": "^1.0.0" } }, "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ=="],
+
+ "object.groupby": ["object.groupby@1.0.3", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.2" } }, "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ=="],
+
+ "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="],
+
+ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
+
+ "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
+
+ "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
+
+ "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
+
+ "parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
+
+ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
+
+ "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
+
+ "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
+
+ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
+
+ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
+
+ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="],
+
+ "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
+
+ "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
+
+ "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
+
+ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+
+ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
+
+ "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="],
+
+ "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="],
+
+ "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+
+ "reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
+
+ "regexp.prototype.flags": ["regexp.prototype.flags@1.5.4", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-errors": "^1.3.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "set-function-name": "^2.0.2" } }, "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA=="],
+
+ "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="],
+
+ "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
+
+ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
+
+ "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
+
+ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
+
+ "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="],
+
+ "safe-push-apply": ["safe-push-apply@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "isarray": "^2.0.5" } }, "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA=="],
+
+ "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="],
+
+ "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
+
+ "semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
+ "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="],
+
+ "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="],
+
+ "set-proto": ["set-proto@1.0.0", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0" } }, "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw=="],
+
+ "sharp": ["sharp@0.34.3", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.4", "semver": "^7.7.2" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.3", "@img/sharp-darwin-x64": "0.34.3", "@img/sharp-libvips-darwin-arm64": "1.2.0", "@img/sharp-libvips-darwin-x64": "1.2.0", "@img/sharp-libvips-linux-arm": "1.2.0", "@img/sharp-libvips-linux-arm64": "1.2.0", "@img/sharp-libvips-linux-ppc64": "1.2.0", "@img/sharp-libvips-linux-s390x": "1.2.0", "@img/sharp-libvips-linux-x64": "1.2.0", "@img/sharp-libvips-linuxmusl-arm64": "1.2.0", "@img/sharp-libvips-linuxmusl-x64": "1.2.0", "@img/sharp-linux-arm": "0.34.3", "@img/sharp-linux-arm64": "0.34.3", "@img/sharp-linux-ppc64": "0.34.3", "@img/sharp-linux-s390x": "0.34.3", "@img/sharp-linux-x64": "0.34.3", "@img/sharp-linuxmusl-arm64": "0.34.3", "@img/sharp-linuxmusl-x64": "0.34.3", "@img/sharp-wasm32": "0.34.3", "@img/sharp-win32-arm64": "0.34.3", "@img/sharp-win32-ia32": "0.34.3", "@img/sharp-win32-x64": "0.34.3" } }, "sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg=="],
+
+ "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
+
+ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
+
+ "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="],
+
+ "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="],
+
+ "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="],
+
+ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="],
+
+ "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="],
+
+ "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
+
+ "stable-hash": ["stable-hash@0.0.5", "", {}, "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA=="],
+
+ "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="],
+
+ "string.prototype.includes": ["string.prototype.includes@2.0.1", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-abstract": "^1.23.3" } }, "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg=="],
+
+ "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="],
+
+ "string.prototype.repeat": ["string.prototype.repeat@1.0.0", "", { "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" } }, "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w=="],
+
+ "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="],
+
+ "string.prototype.trimend": ["string.prototype.trimend@1.0.9", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ=="],
+
+ "string.prototype.trimstart": ["string.prototype.trimstart@1.0.8", "", { "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg=="],
+
+ "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="],
+
+ "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
+
+ "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="],
+
+ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
+
+ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
+
+ "tailwindcss": ["tailwindcss@4.1.13", "", {}, "sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w=="],
+
+ "tapable": ["tapable@2.2.3", "", {}, "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg=="],
+
+ "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="],
+
+ "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
+
+ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
+
+ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
+
+ "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="],
+
+ "tsconfig-paths": ["tsconfig-paths@3.15.0", "", { "dependencies": { "@types/json5": "^0.0.29", "json5": "^1.0.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg=="],
+
+ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
+
+ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
+
+ "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="],
+
+ "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="],
+
+ "typed-array-byte-offset": ["typed-array-byte-offset@1.0.4", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.15", "reflect.getprototypeof": "^1.0.9" } }, "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ=="],
+
+ "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="],
+
+ "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
+
+ "unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "has-bigints": "^1.0.2", "has-symbols": "^1.1.0", "which-boxed-primitive": "^1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
+
+ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
+
+ "unrs-resolver": ["unrs-resolver@1.11.1", "", { "dependencies": { "napi-postinstall": "^0.3.0" }, "optionalDependencies": { "@unrs/resolver-binding-android-arm-eabi": "1.11.1", "@unrs/resolver-binding-android-arm64": "1.11.1", "@unrs/resolver-binding-darwin-arm64": "1.11.1", "@unrs/resolver-binding-darwin-x64": "1.11.1", "@unrs/resolver-binding-freebsd-x64": "1.11.1", "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", "@unrs/resolver-binding-linux-x64-musl": "1.11.1", "@unrs/resolver-binding-wasm32-wasi": "1.11.1", "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" } }, "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg=="],
+
+ "uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
+
+ "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
+
+ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
+
+ "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
+
+ "which-boxed-primitive": ["which-boxed-primitive@1.1.1", "", { "dependencies": { "is-bigint": "^1.1.0", "is-boolean-object": "^1.2.1", "is-number-object": "^1.1.1", "is-string": "^1.1.1", "is-symbol": "^1.1.1" } }, "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA=="],
+
+ "which-builtin-type": ["which-builtin-type@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "function.prototype.name": "^1.1.6", "has-tostringtag": "^1.0.2", "is-async-function": "^2.0.0", "is-date-object": "^1.1.0", "is-finalizationregistry": "^1.1.0", "is-generator-function": "^1.0.10", "is-regex": "^1.2.1", "is-weakref": "^1.0.2", "isarray": "^2.0.5", "which-boxed-primitive": "^1.1.0", "which-collection": "^1.0.2", "which-typed-array": "^1.1.16" } }, "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q=="],
+
+ "which-collection": ["which-collection@1.0.2", "", { "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", "is-weakmap": "^2.0.2", "is-weakset": "^2.0.3" } }, "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw=="],
+
+ "which-typed-array": ["which-typed-array@1.1.19", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw=="],
+
+ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
+
+ "ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
+
+ "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
+
+ "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
+
+ "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
+
+ "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
+
+ "@typescript-eslint/typescript-estree/fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
+
+ "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="],
+
+ "@typescript-eslint/typescript-estree/semver": ["semver@7.7.2", "", { "bin": "bin/semver.js" }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+
+ "eslint-import-resolver-node/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+
+ "eslint-module-utils/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+
+ "eslint-plugin-import/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
+
+ "eslint-plugin-react/resolve": ["resolve@2.0.0-next.5", "", { "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": "bin/resolve" }, "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA=="],
+
+ "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
+
+ "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "is-bun-module/semver": ["semver@7.7.2", "", { "bin": "bin/semver.js" }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+
+ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
+
+ "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="],
+
+ "sharp/semver": ["semver@7.7.2", "", { "bin": "bin/semver.js" }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
+
+ "@typescript-eslint/typescript-estree/fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
+
+ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="],
+ }
+}
diff --git a/apps/webapp/eslint.config.mjs b/apps/webapp/eslint.config.mjs
new file mode 100644
index 0000000..719cea2
--- /dev/null
+++ b/apps/webapp/eslint.config.mjs
@@ -0,0 +1,25 @@
+import { dirname } from "path";
+import { fileURLToPath } from "url";
+import { FlatCompat } from "@eslint/eslintrc";
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = dirname(__filename);
+
+const compat = new FlatCompat({
+ baseDirectory: __dirname,
+});
+
+const eslintConfig = [
+ ...compat.extends("next/core-web-vitals", "next/typescript"),
+ {
+ ignores: [
+ "node_modules/**",
+ ".next/**",
+ "out/**",
+ "build/**",
+ "next-env.d.ts",
+ ],
+ },
+];
+
+export default eslintConfig;
diff --git a/apps/webapp/middleware.ts b/apps/webapp/middleware.ts
new file mode 100644
index 0000000..bebfc8e
--- /dev/null
+++ b/apps/webapp/middleware.ts
@@ -0,0 +1,16 @@
+import { type NextRequest, NextResponse } from 'next/server'
+import { updateSession } from '@/utils/supabase/middleware'
+
+export async function middleware(request: NextRequest) {
+ // Set NEXT_PUBLIC_REQUIRE_AUTH=true in .env to enable Supabase auth gating
+ if (process.env.NEXT_PUBLIC_REQUIRE_AUTH !== 'true') {
+ return NextResponse.next({ request })
+ }
+ return await updateSession(request)
+}
+
+export const config = {
+ matcher: [
+ '/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
+ ],
+}
\ No newline at end of file
diff --git a/apps/webapp/next.config.ts b/apps/webapp/next.config.ts
new file mode 100644
index 0000000..cb0278c
--- /dev/null
+++ b/apps/webapp/next.config.ts
@@ -0,0 +1,8 @@
+import type { NextConfig } from "next";
+import path from "node:path";
+
+const nextConfig: NextConfig = {
+ outputFileTracingRoot: path.join(process.cwd(), "../.."),
+};
+
+export default nextConfig;
diff --git a/apps/webapp/package.json b/apps/webapp/package.json
new file mode 100644
index 0000000..1ebbad5
--- /dev/null
+++ b/apps/webapp/package.json
@@ -0,0 +1,31 @@
+{
+ "name": "webapp",
+ "version": "0.1.0",
+ "private": true,
+ "packageManager": "bun@1.2.0",
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint",
+ "typecheck": "tsc --noEmit"
+ },
+ "dependencies": {
+ "@supabase/ssr": "^0.7.0",
+ "@supabase/supabase-js": "^2.57.4",
+ "next": "15.5.2",
+ "react": "19.1.0",
+ "react-dom": "19.1.0"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3",
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "15.5.2",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/apps/webapp/postcss.config.mjs b/apps/webapp/postcss.config.mjs
new file mode 100644
index 0000000..c7bcb4b
--- /dev/null
+++ b/apps/webapp/postcss.config.mjs
@@ -0,0 +1,5 @@
+const config = {
+ plugins: ["@tailwindcss/postcss"],
+};
+
+export default config;
diff --git a/apps/webapp/project.json b/apps/webapp/project.json
new file mode 100644
index 0000000..3d7148e
--- /dev/null
+++ b/apps/webapp/project.json
@@ -0,0 +1,44 @@
+{
+ "name": "webapp",
+ "root": "apps/webapp",
+ "sourceRoot": "apps/webapp/src",
+ "projectType": "application",
+ "targets": {
+ "dev": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/webapp",
+ "command": "bun run dev"
+ }
+ },
+ "build": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/webapp",
+ "command": "bun run build"
+ },
+ "outputs": ["{projectRoot}/.next"]
+ },
+ "start": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/webapp",
+ "command": "bun run start"
+ }
+ },
+ "lint": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/webapp",
+ "command": "bun run lint"
+ }
+ },
+ "typecheck": {
+ "executor": "nx:run-commands",
+ "options": {
+ "cwd": "apps/webapp",
+ "command": "bun run typecheck"
+ }
+ }
+ }
+}
diff --git a/apps/webapp/public/file.svg b/apps/webapp/public/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/apps/webapp/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/webapp/public/globe.svg b/apps/webapp/public/globe.svg
new file mode 100644
index 0000000..567f17b
--- /dev/null
+++ b/apps/webapp/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/webapp/public/llms.txt b/apps/webapp/public/llms.txt
new file mode 100644
index 0000000..3dd1807
--- /dev/null
+++ b/apps/webapp/public/llms.txt
@@ -0,0 +1 @@
+This is a project by Daniel Alves Rosel (https://alves.world)
diff --git a/apps/webapp/public/next.svg b/apps/webapp/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/apps/webapp/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/webapp/public/robots.txt b/apps/webapp/public/robots.txt
new file mode 100644
index 0000000..e69de29
diff --git a/apps/webapp/public/vercel.svg b/apps/webapp/public/vercel.svg
new file mode 100644
index 0000000..7705396
--- /dev/null
+++ b/apps/webapp/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/webapp/public/window.svg b/apps/webapp/public/window.svg
new file mode 100644
index 0000000..b2b2a44
--- /dev/null
+++ b/apps/webapp/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/webapp/src/app/auth/confirm/route.ts b/apps/webapp/src/app/auth/confirm/route.ts
new file mode 100644
index 0000000..8d37c44
--- /dev/null
+++ b/apps/webapp/src/app/auth/confirm/route.ts
@@ -0,0 +1,28 @@
+import { type EmailOtpType } from '@supabase/supabase-js'
+import { type NextRequest } from 'next/server'
+
+import { createClient } from '@/utils/supabase/server'
+import { redirect } from 'next/navigation'
+
+export async function GET(request: NextRequest) {
+ const { searchParams } = new URL(request.url)
+ const token_hash = searchParams.get('token_hash')
+ const type = searchParams.get('type') as EmailOtpType | null
+ const next = searchParams.get('next') ?? '/'
+
+ if (token_hash && type) {
+ const supabase = await createClient()
+
+ const { error } = await supabase.auth.verifyOtp({
+ type,
+ token_hash,
+ })
+ if (!error) {
+ // redirect user to specified redirect URL or root of app
+ redirect(next)
+ }
+ }
+
+ // redirect the user to an error page with some instructions
+ redirect('/error')
+}
\ No newline at end of file
diff --git a/apps/webapp/src/app/blog/page.tsx b/apps/webapp/src/app/blog/page.tsx
new file mode 100644
index 0000000..e36741e
--- /dev/null
+++ b/apps/webapp/src/app/blog/page.tsx
@@ -0,0 +1,10 @@
+export default function BlogPage() {
+ return (
+
+ Publish product updates, engineering notes, and launch posts here.
+ Blog
+
Welcome, {data.user.email}
+ +{error.message || 'An unexpected error occurred'}
+ +Sorry, something went wrong
+} \ No newline at end of file diff --git a/apps/webapp/src/app/favicon.ico b/apps/webapp/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UXdAMQaiH zm^r3v#$Q#2T=X>bsY#D%s!bhs^M9PMAcHbCc0FMHV{u-dwlL;a1eJ63v5U*?Q_8JO zT#50!RD619#j_Uf))0ooADz~*9&lN!bBDRUgE>Vud-i5ck%vT=r^yD*^?Mp@Q^v+V zG#-?gKlr}Eeqifb{|So?HM&g91P8|av8hQoCmQXkd?7wIJw b z_^v8bbg` SAn{I*4bH$u(RZ6*x UhuA~hc=8czK8SHEKTzSxgbwi~9(OqJB&gwb^l4+m`k*Q;_?>Y-APi1{k zAHQ)P)G)f|AyjSgcCFps)Fh6Bca*Xznq36!pV6Az&m{O8$wGFD? zY&O*3*J0;_EqM#jh6^gMQKpXV?#1?>$ml1xvh8nSN>-?H=V;nJIwB07YX$e6vLxH( zqYwQ>qxwR(i4f)DLd)-$P>T-no_c!LsN@)8`e;W@)-Hj0>nJ-}Kla4-ZdPJzI&Mce zv)V_j;(3ERN3_@I$N<^|4Lf`B;8n+bX@bHbcZTopEmDI*Jfl)-pFDvo6svPRoo@(x z);_{lY<;);XzT`dBFpRmGrr}z5u1=p C^ S-{ce6iXQlLGcItwJ^mZx{m$&DA_oEZ)B{_bYPq-HA zcH8WGoBG(aBU_j)vEy+_71T34@4dmSg!|M8Vf92Zj6WH7Q7t#OHQqWgFE3ARt+%!T z?oLovLVlnf?2c7pTc)~cc^($_8nyKwsN`RA-23ed3sdj(ys%pjjM+9JrctL;dy8a( z@en&CQmnV(()bu|Y%G1-4a(6x{aLytn$T-;(&{QIJB9vMox11U-1HpD@d(QkaJdEb zG{)+6Dos_L+O3NpWo^=gR?evp|CqEG?L&Ut#D*KLaRFOgOEK(Kq1@!EGcTfo+%A&I z=dLbB+d$u{sh?u)xP{PF8L%;YPPW53+@{>5W=Jt#wQpN;0_HYdw1{ksf_XhO4#2F= zyPx6Lx2<92L-;L5PD`zn6zwIH`Jk( $?Qw({erA$^bC;q33hv!d!>%wRhj# zal^hk+WGNg;rJtb-EB(?czvOM=H7dl=vblBwAv>}%1@{}mnpUznfq1cE^sgsL0*4I zJ##!*B?=vI_OEVis5o+_IwMIRrpQyT_Sq~ZU%oY7c5JMIADzpD!Upz9h@iWg_>>~j zOLS;wp^i$-E?4<_cp?RiS%Rd?i;f*mOz=~(&3lo<=@(nR!_Rqiprh@weZlL!t#NCc zO!QTcInq|%#>OVgobj{~ixEUec`E25zJ~*DofsQdzIa@5^nOXj2T;8O`l--(QyU ^$t?TGY^7#&FQ+2SS3B#qK*k3`ye?8jUYSajE5iBbJls75CCc(m3dk{t?- zopcER9{Z?TC)mk~gpi^kbbu>b-+a{m#8-y2^p$ka4n60w;Sc2}HMf<8JUvh CL0B&Btk)T`ctE$*qNW8L$`7!r^9T+>=<=2qaq-;ll2{`{Rg zc5a0ZUI$oG&j-qVOuKa=*v4aY#IsoM+1|c4Z)<}lEDvy;5huB@1RJPquU2U*U-;gu z=En2m+qjBzR#DEJDO`WU)hdd{Vj%^0V*KoyZ|5lzV87&g_j~NCjwv0uQVqXOb*QrQ zy|Qn`hxx(58c 70$E;L(X0uZZ72M1!6oeg)(cdKO ze0gDaTz+ohR-#d)NbAH4x{I(21yjwvBQfmpLu$)|m{XolbgF!pmsqJ#D}(ylp6uC> z{bqtcI#hT#HW=wl7>p!38sKsJ`r8}lt-q%Keqy%u(xk=yiIJiUw6|5IvkS+#?JTBl z8H5(Q?l#wzazujH!8o>1xtn8#_w+397* _cy8!pQGP%K(Ga3pAjsaTbbXJlQF_+m+-UpUUent@xM zg%jqLUExj~o^vQ3Gl*>wh=_gOr2*|U64_iXb+-111a H}$TjeajM+I20xw(((>fej-@CIz4S1pi$(#}P7`4({6QS2CaQS4NPENDp>sAqD z$bH4KGzXGffkJ7R>V>)>tC)uax{UsN*dbeNC*v}#8Y#OWYwL4t$ePR?VTyIs!wea+ z5Urmc)X|^`MG~*dS6pGSbU+gPJoq*^a=_>$n4|P^w$sMBBy@f*Z^Jg6?n5?oId6f{ z$LW4M|4m502z0t7g<#Bx%X;9<=)smFolV&(V^(7Cv2-sxbxopQ!)*#ZRhTBpx1)Fc zNm1T%bONzv6@#|dz(w02AH8OXe>kQ#1FMCzO}2J_mST)+ExmBr9cva-@?;wnmWMOk z{3_~EX_xadgJGv&H@zK_8{(x84`}+c?oSBX*Ge3VdfTt&F}yCpFP?CpW+BE^cWY0^ zb&uBN!Ja3UzYHK-CTyA5=L zEMW{l3Usky#ly=7px648W31UNV@K)&Ub&zP1c7%)`{);I4b0Q<)B}3;NMG2JH=X$U zfIW4)4n9ZM`-yRj67I)YSLDK)qfUJ_ij}a#aZN~9EXrh8eZY2&=uY%2N0UFF7<~%M zsB8=erOWZ>Ct_#^tHZ|*q`H;A)5;ycw*I cmVxi8_0Xk}aJA^ath+E;xg!x+As(M#0=)3!NJR6H&9+zd#iP(m0PIW8$ z1Y^VX`>jm`W!=WpF*{ioM?C9`yOR>@0q=u7o>BP-eSHqCgMDj!2anwH?s%i2p+Q7D zzszIf5XJpE)IG4;d_(La-xenmF(tgAxK`Y4sQ}BSJEPs6N_U2vI{8=0C_F?@7<(G; zo$~G=8p+076G;`}>{MQ>t>7cm=zGtfbdDXm6||jUU|?X?CaE?(<6bKDYKeHlz}DA8 zXT={X=yp_R;HfJ9h%?eWvQ!dRgz&Su*JfNt!Wu>|XfU &68iRikRrHRW|ZxzRR^`eIGt zIeiDgVS>IeExKVRWW8-= A= yA`}`)ZkWBrZD`hpWIxBGkh&f#ijr449~m`j6{4jiJ*C!oVA8ZC?$1RM#K(_b zL9TW)kN*Y4%^-qPpMP7d4)o?Nk#>aoYHT(*g)qmRUb?**F@pnNiy6Fv9rEiUqD(^O zzyS?nBrX63BTRYduaG(0VVG2yJRe%o&rVrLjbxTaAFTd8s;<<@Qs>u(<193R8>}2_ zuwp{7;H2a*X7_jryzriZXMg?bTuegABb^87@SsKkr2)0Gyiax8KQWstw^v #ix45EVrcEhr>!NMhprl $InQMzjSFH54x5k9qHc`@9uKQzvL4ihcq{^B zPrVR=o_ic%Y>6&rMN)hTZsI7I<3&`#(nl+3y3ys9A~ &^=4?PL&nd8)`OfG#n zwAMN$1&>K++c{^|7< 4P=2y(B{jJsQ0a#U;HTo4ZmWZYvI{+s;Td{Yzem%0*k#)vjpB zia;J&>}ICate44SFYY3vEelqStQWFihx%^vQ@Do(sOy7yR2@WNv7Y9I^yL=nZr3mb zXKV5t@=?-Sk|b{XMhA7ZGB@2hqsx}4xwCW!in#C zI@}sc Zlr3-NFJ@NFaJlhyfcw{k^vvtGl`N9xSo**rDW4S}i zM9{fMPWo%4wYDG~BZ18BD+}h|GQKc-g^{++3MY>}W_uq7jGHx{mwE9fZiPCoxN$+7 zrODGGJrOkcPQUB(FD5aoS4g~7#6NR^ma7-!>mHuJfY5kTe6PpNNKC9GGRiu^L31uG z$7v`*JknQHsYB!Tm_W{a32TM099djW%5e+j0Ve_ct}IM>XLF1Ap+YvcrLV=|CKo6S zb+ 9Nl3_YdKP6%Cxy@6TxZ>;4&nTneadr z_ES90ydCev)LV!dN=#(*f}|ZORFdvkYBni^aLbUk>BajeWIOcmHP#8S)*2U~QKI%S zyrLmtPqb&TphJ;>yAxri#;{uyk`JJqODDw%(Z=2 `1uc}br^V%>j!gS)D*q*f_-qf8&D;W1dJgQMlaH5er zN2U<%Smb7==vE}dDI8K7cKz!vs^73o9f>2sgiTzWcwY|BMYHH5%Vn7#kiw&eItCqa zIkR2~Q}>X=Ar8W|^Ms41Fm8o6IB2_j60eOeBB1Br!boW7JnoeX6Gs)?7rW0^5psc- zjS16yb>dFn>KPOF;imD}e!enuIniFzv}n$m2#gCCv4jM#ArwlzZ$7@9&XkFxZ4n!V zj3dyiwW4Ki2QG{@i>yuZXQizw_OkZI^-3otXC{!(lUpJF33gI60ak;Uqitp74|B6I zgg{b=Iz}WkhCGj1M =hu4#Aw173YxIVbISaoc z-nLZC*6Tgivd5V`K%GxhBsp@SUU60-rfc$=wb>zdJzXS&-5(NRRodFk;Kxk!S( O(a0e7oY=E( zAyS;Ow?6Q&XA+cnkCb{28_1N8H#?J!*$MmIwLq^*T_9-z^&UE@A(z9oGYtFy6EZef LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/apps/webapp/src/app/globals.css b/apps/webapp/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/apps/webapp/src/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/apps/webapp/src/app/instruments/page.tsx b/apps/webapp/src/app/instruments/page.tsx new file mode 100644 index 0000000..a6f1dda --- /dev/null +++ b/apps/webapp/src/app/instruments/page.tsx @@ -0,0 +1,8 @@ +import { createClient } from '@/utils/supabase/server'; + +export default async function Instruments() { + const supabase = await createClient(); + const { data: instruments } = await supabase.from("instruments").select(); + + return {JSON.stringify(instruments, null, 2)}+} \ No newline at end of file diff --git a/apps/webapp/src/app/layout.tsx b/apps/webapp/src/app/layout.tsx new file mode 100644 index 0000000..680fb38 --- /dev/null +++ b/apps/webapp/src/app/layout.tsx @@ -0,0 +1,29 @@ +import type { Metadata } from "next"; +import "./globals.css"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; + +const fontVariables = "font-sans"; + +export const metadata: Metadata = { + title: "Ultiplate - Ultimate Boilerplate", + description: "AI-native template for any project with deployment ready setup", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + ++ + {children} + + + + + ); +} diff --git a/apps/webapp/src/app/login/actions.ts b/apps/webapp/src/app/login/actions.ts new file mode 100644 index 0000000..d5732e3 --- /dev/null +++ b/apps/webapp/src/app/login/actions.ts @@ -0,0 +1,46 @@ +'use server' + +import { revalidatePath } from 'next/cache' +import { redirect } from 'next/navigation' + +import { createClient } from '@/utils/supabase/server' + +export async function login(formData: FormData) { + const supabase = await createClient() + + // type-casting here for convenience + // in practice, you should validate your inputs + const data = { + email: formData.get('email') as string, + password: formData.get('password') as string, + } + + const { error } = await supabase.auth.signInWithPassword(data) + + if (error) { + redirect('/error') + } + + revalidatePath('/', 'layout') + redirect('/') +} + +export async function signup(formData: FormData) { + const supabase = await createClient() + + // type-casting here for convenience + // in practice, you should validate your inputs + const data = { + email: formData.get('email') as string, + password: formData.get('password') as string, + } + + const { error } = await supabase.auth.signUp(data) + + if (error) { + redirect('/error') + } + + revalidatePath('/', 'layout') + redirect('/') +} \ No newline at end of file diff --git a/apps/webapp/src/app/login/page.tsx b/apps/webapp/src/app/login/page.tsx new file mode 100644 index 0000000..2063559 --- /dev/null +++ b/apps/webapp/src/app/login/page.tsx @@ -0,0 +1,14 @@ +import { login, signup } from './actions' + +export default function LoginPage() { + return ( + + ) +} \ No newline at end of file diff --git a/apps/webapp/src/app/not-found.tsx b/apps/webapp/src/app/not-found.tsx new file mode 100644 index 0000000..8ae8c01 --- /dev/null +++ b/apps/webapp/src/app/not-found.tsx @@ -0,0 +1,12 @@ +import Link from 'next/link'; + +export default function NotFound() { + return ( ++ {/* TODO: Style this 404 page when implementing in your project */} ++ ); +} \ No newline at end of file diff --git a/apps/webapp/src/app/page.tsx b/apps/webapp/src/app/page.tsx new file mode 100644 index 0000000..bc7f6c1 --- /dev/null +++ b/apps/webapp/src/app/page.tsx @@ -0,0 +1,17 @@ +import Hero from "@/components/Hero"; +import FeaturesGrid from "@/components/FeaturesGrid"; +import Testimonials1 from "@/components/Testimonials1"; +import Pricing from "@/components/Pricing"; +//import FAQ from "@/components/FAQ"; +//import CTA from "@/components/CTA"; + +export default function Home() { + return ( +Not Found
+Could not find requested resource
+ Return Home +++ ); +} diff --git a/apps/webapp/src/app/privacy-policy/page.tsx b/apps/webapp/src/app/privacy-policy/page.tsx new file mode 100644 index 0000000..4d94ad8 --- /dev/null +++ b/apps/webapp/src/app/privacy-policy/page.tsx @@ -0,0 +1,10 @@ +export default function PrivacyPolicyPage() { + return ( ++ + + + + + ); +} diff --git a/apps/webapp/src/app/tos/page.tsx b/apps/webapp/src/app/tos/page.tsx new file mode 100644 index 0000000..70491f6 --- /dev/null +++ b/apps/webapp/src/app/tos/page.tsx @@ -0,0 +1,10 @@ +export default function TermsOfServicePage() { + return ( +Privacy Policy
++ Describe what data you collect, how it is used, and how users can request deletion. +
++ + ); +} diff --git a/apps/webapp/src/components/FeaturesGrid.tsx b/apps/webapp/src/components/FeaturesGrid.tsx new file mode 100644 index 0000000..6046639 --- /dev/null +++ b/apps/webapp/src/components/FeaturesGrid.tsx @@ -0,0 +1,22 @@ +import { getLocale } from "@/libs/locales"; + +export default function FeaturesGrid() { + const { common } = getLocale('en'); + + return ( +Terms of Service
++ Add your product terms, responsibilities, and legal limitations in this page. +
++ {/* TODO: Style this features grid when implementing in your project */} + + ); +} \ No newline at end of file diff --git a/apps/webapp/src/components/Footer.tsx b/apps/webapp/src/components/Footer.tsx new file mode 100644 index 0000000..7c0e633 --- /dev/null +++ b/apps/webapp/src/components/Footer.tsx @@ -0,0 +1,37 @@ +import Link from "next/link"; +import { getLocale } from "@/libs/locales"; + +export default function Footer() { + const { common } = getLocale('en'); + + return ( + + ); +} \ No newline at end of file diff --git a/apps/webapp/src/components/Header.tsx b/apps/webapp/src/components/Header.tsx new file mode 100644 index 0000000..05d053a --- /dev/null +++ b/apps/webapp/src/components/Header.tsx @@ -0,0 +1,27 @@ +import Link from "next/link"; +import { getLocale } from "@/libs/locales"; + +export default function Header() { + const { common } = getLocale('en'); + + return ( +++{common.features.title}
++ {common.features.items.map((feature, index) => ( ++++ ))} +{feature.title}
+{feature.description}
++ {/* TODO: Style this header when implementing in your project */} + + ); +} diff --git a/apps/webapp/src/components/Hero.tsx b/apps/webapp/src/components/Hero.tsx new file mode 100644 index 0000000..c928d34 --- /dev/null +++ b/apps/webapp/src/components/Hero.tsx @@ -0,0 +1,24 @@ +import Link from "next/link"; +import { getLocale } from "@/libs/locales"; + +export default function Hero() { + const { common } = getLocale('en'); + + return ( ++++++ {common.header.brand} ++ ++ {common.header.actions.signIn} +++ {/* TODO: Style this hero section when implementing in your project */} + + ); +} \ No newline at end of file diff --git a/apps/webapp/src/components/Pricing.tsx b/apps/webapp/src/components/Pricing.tsx new file mode 100644 index 0000000..4982afe --- /dev/null +++ b/apps/webapp/src/components/Pricing.tsx @@ -0,0 +1,47 @@ +export default function Pricing() { + const plans = [ + { + name: "Open Source", + price: "Free", + features: [ + "All boilerplate code", + "Docker configurations", + "Basic ML setup", + "Community support" + ] + }, + { + name: "Pro", + price: "$49/month", + features: [ + "Everything in Open Source", + "Advanced configurations", + "Priority support", + "Custom integrations" + ] + } + ]; + + return ( +++{common.hero.title}
+{common.hero.description}
++ + + + + + +++ {/* TODO: Style this pricing section when implementing in your project */} + + ); +} \ No newline at end of file diff --git a/apps/webapp/src/components/Testimonials1.tsx b/apps/webapp/src/components/Testimonials1.tsx new file mode 100644 index 0000000..9a8efe4 --- /dev/null +++ b/apps/webapp/src/components/Testimonials1.tsx @@ -0,0 +1,25 @@ +import { getLocale } from "@/libs/locales"; + +export default function Testimonials1() { + const { common } = getLocale('en'); + + return ( +++Pricing
++ {plans.map((plan, index) => ( ++++ ))} +{plan.name}
+{plan.price}
++ {plan.features.map((feature, featureIndex) => ( +
+ +- {feature}
+ ))} ++ {/* TODO: Style this testimonials section when implementing in your project */} + + ); +} diff --git a/apps/webapp/src/libs/locales.ts b/apps/webapp/src/libs/locales.ts new file mode 100644 index 0000000..8188088 --- /dev/null +++ b/apps/webapp/src/libs/locales.ts @@ -0,0 +1,32 @@ +import commonEn from '@/locales/en/common.json'; + +// TODO: Add more languages as needed +const locales = { + en: { + common: commonEn + } +}; + +export function getLocale(locale: string = 'en') { + return locales[locale as keyof typeof locales] || locales.en; +} + +export function t(key: string, locale: string = 'en') { + const translations = getLocale(locale); + const keys = key.split('.'); + + let value: unknown = translations; + for (const k of keys) { + if (typeof value !== 'object' || value === null || !(k in value)) { + return key; + } + + value = (value as Record++{common.testimonials.title}
++ {common.testimonials.items.map((testimonial, index) => ( ++++ ))} +{`"${testimonial.content}"`}
+++{testimonial.name}
+{testimonial.role}
+)[k]; + } + + if (typeof value === 'string' || typeof value === 'number') { + return String(value); + } + + return key; +} diff --git a/apps/webapp/src/locales/en/common.json b/apps/webapp/src/locales/en/common.json new file mode 100644 index 0000000..888d0cd --- /dev/null +++ b/apps/webapp/src/locales/en/common.json @@ -0,0 +1,85 @@ +{ + "header": { + "brand": "UltiPlate", + "nav": { + "home": "Home", + "dashboard": "Dashboard", + "blog": "Blog" + }, + "actions": { + "signIn": "Sign In" + } + }, + "footer": { + "brand": "UltiPlate", + "description": "Ultimate boilerplate for any project", + "legal": { + "title": "Legal", + "privacyPolicy": "Privacy Policy", + "termsOfService": "Terms of Service" + }, + "company": { + "title": "Company", + "blog": "Blog", + "dashboard": "Dashboard" + }, + "copyright": "© 2024 UltiPlate. All rights reserved." + }, + "hero": { + "title": "Welcome to UltiPlate", + "description": "The ultimate boilerplate for any project - from web apps to ML projects, all deployable with Docker.", + "actions": { + "getStarted": "Get Started", + "learnMore": "Learn More" + } + }, + "features": { + "title": "Features", + "items": [ + { + "title": "Docker Ready", + "description": "Everything containerized and ready to deploy" + }, + { + "title": "ML Support", + "description": "Built-in machine learning project structure" + }, + { + "title": "Multiple Backends", + "description": "FastAPI, Flask, and more backend options" + }, + { + "title": "Centralized Logging", + "description": "Loki-based logging across all services" + }, + { + "title": "Database Ready", + "description": "Redis, MinIO, and database integrations" + }, + { + "title": "Web Apps", + "description": "Next.js and Streamlit app templates" + } + ] + }, + "testimonials": { + "title": "What People Say", + "items": [ + { + "name": "Alex Developer", + "role": "Full Stack Developer", + "content": "UltiPlate saved me weeks of setup time. Everything just works out of the box." + }, + { + "name": "Sarah ML Engineer", + "role": "ML Engineer", + "content": "The ML project structure is perfect. From notebooks to production in minutes." + }, + { + "name": "Mike DevOps", + "role": "DevOps Engineer", + "content": "Docker integration is seamless. Deploy anywhere with confidence." + } + ] + } +} \ No newline at end of file diff --git a/apps/webapp/src/utils/supabase/client.ts b/apps/webapp/src/utils/supabase/client.ts new file mode 100644 index 0000000..1d920ca --- /dev/null +++ b/apps/webapp/src/utils/supabase/client.ts @@ -0,0 +1,8 @@ +import { createBrowserClient } from '@supabase/ssr' + +export function createClient() { + return createBrowserClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY! + ) +} \ No newline at end of file diff --git a/apps/webapp/src/utils/supabase/middleware.ts b/apps/webapp/src/utils/supabase/middleware.ts new file mode 100644 index 0000000..a11d495 --- /dev/null +++ b/apps/webapp/src/utils/supabase/middleware.ts @@ -0,0 +1,66 @@ +import { createServerClient } from '@supabase/ssr' +import { NextResponse, type NextRequest } from 'next/server' + +export async function updateSession(request: NextRequest) { + let supabaseResponse = NextResponse.next({ + request, + }) + + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!, + { + cookies: { + getAll() { + return request.cookies.getAll() + }, + setAll(cookiesToSet) { + cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)) + supabaseResponse = NextResponse.next({ + request, + }) + cookiesToSet.forEach(({ name, value, options }) => + supabaseResponse.cookies.set(name, value, options) + ) + }, + }, + } + ) + + // Do not run code between createServerClient and + // supabase.auth.getUser(). A simple mistake could make it very hard to debug + // issues with users being randomly logged out. + + // IMPORTANT: DO NOT REMOVE auth.getUser() + + const { + data: { user }, + } = await supabase.auth.getUser() + + if ( + !user && + !request.nextUrl.pathname.startsWith('/login') && + !request.nextUrl.pathname.startsWith('/auth') && + !request.nextUrl.pathname.startsWith('/error') + ) { + // no user, potentially respond by redirecting the user to the login page + const url = request.nextUrl.clone() + url.pathname = '/login' + return NextResponse.redirect(url) + } + + // IMPORTANT: You *must* return the supabaseResponse object as it is. + // If you're creating a new response object with NextResponse.next() make sure to: + // 1. Pass the request in it, like so: + // const myNewResponse = NextResponse.next({ request }) + // 2. Copy over the cookies, like so: + // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll()) + // 3. Change the myNewResponse object to fit your needs, but avoid changing + // the cookies! + // 4. Finally: + // return myNewResponse + // If this is not done, you may be causing the browser and server to go out + // of sync and terminate the user's session prematurely! + + return supabaseResponse +} diff --git a/apps/webapp/src/utils/supabase/server.ts b/apps/webapp/src/utils/supabase/server.ts new file mode 100644 index 0000000..2adf2a6 --- /dev/null +++ b/apps/webapp/src/utils/supabase/server.ts @@ -0,0 +1,29 @@ +import { createServerClient } from '@supabase/ssr' +import { cookies } from 'next/headers' + +export async function createClient() { + const cookieStore = await cookies() + + return createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!, + { + cookies: { + getAll() { + return cookieStore.getAll() + }, + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => + cookieStore.set(name, value, options) + ) + } catch { + // The `setAll` method was called from a Server Component. + // This can be ignored if you have middleware refreshing + // user sessions. + } + }, + }, + } + ) +} \ No newline at end of file diff --git a/apps/webapp/tsconfig.json b/apps/webapp/tsconfig.json new file mode 100644 index 0000000..c133409 --- /dev/null +++ b/apps/webapp/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/apps/worker/project.json b/apps/worker/project.json new file mode 100644 index 0000000..5f1c30f --- /dev/null +++ b/apps/worker/project.json @@ -0,0 +1,40 @@ +{ + "name": "worker", + "root": "apps/worker", + "sourceRoot": "apps/worker", + "projectType": "application", + "implicitDependencies": ["alveslib"], + "targets": { + "dev": { + "executor": "nx:run-commands", + "options": { + "cwd": "apps/worker", + "command": "celery -A worker:app worker --loglevel=info" + } + }, + "build": { + "executor": "nx:run-commands", + "options": { + "command": "python3.12 -m compileall apps/worker" + } + }, + "lint": { + "executor": "nx:run-commands", + "options": { + "command": ".venv/bin/ruff check apps/worker" + } + }, + "typecheck": { + "executor": "nx:run-commands", + "options": { + "command": ".venv/bin/mypy apps/worker" + } + }, + "test": { + "executor": "nx:run-commands", + "options": { + "command": ".venv/bin/pytest apps/worker -v" + } + } + } +} diff --git a/apps/worker/worker.py b/apps/worker/worker.py new file mode 100644 index 0000000..481b832 --- /dev/null +++ b/apps/worker/worker.py @@ -0,0 +1,23 @@ +import os +import time +from celery import Celery +from dotenv import load_dotenv +load_dotenv() + +# Redis connection +redis_url = os.getenv("REDIS_URL", "redis://localhost:6379") +app = Celery('worker', broker=redis_url, backend=redis_url) + +@app.task +def simple_task(message): + """A simple task that processes a message and returns a result""" + time.sleep(2) # Simulate some work + return f"Processed: {message}" + +@app.task +def add_numbers(x, y): + """Simple math task""" + return x + y + +if __name__ == '__main__': + app.start() diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..e2d7f48 --- /dev/null +++ b/bun.lock @@ -0,0 +1,275 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "ultiplate", + "devDependencies": { + "nx": "^20.8.2", + }, + }, + }, + "packages": { + "@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], + + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="], + + "@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="], + + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.4", "", { "dependencies": { "@emnapi/core": "^1.1.0", "@emnapi/runtime": "^1.1.0", "@tybys/wasm-util": "^0.9.0" } }, "sha512-9zESzOO5aDByvhIAsOy9TbpZ0Ur2AJbUI7UT73kcUTS2mxAMHOBaa1st/jAymNoCtvrit99kkzT1FZuXVcgfIQ=="], + + "@nx/nx-darwin-arm64": ["@nx/nx-darwin-arm64@20.8.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8Y7+4wj1qoZsuDRpnuiHzSIsMt3VqtJ0su8dgd/MyGccvvi4pndan2R5yTiVw/wmbMxtBmZ6PO6Z8dgSIrMVog=="], + + "@nx/nx-darwin-x64": ["@nx/nx-darwin-x64@20.8.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-2lfuxRc56QWnAysMhcD03tpCPiRzV1+foUq0MhV2sSBIybXmgV4wHLkPZNhlBCl4FNXrWiZiN1OJ2X9AGiOdug=="], + + "@nx/nx-freebsd-x64": ["@nx/nx-freebsd-x64@20.8.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-99vnUXZy+OUBHU+8Yhabre2qafepKg9GKkQkhmXvJGqOmuIsepK7wirUFo2PiVM8YhS6UV2rv6hKAZcQ7skYyg=="], + + "@nx/nx-linux-arm-gnueabihf": ["@nx/nx-linux-arm-gnueabihf@20.8.4", "", { "os": "linux", "cpu": "arm" }, "sha512-dht73zpnpzEUEzMHFQs4mfiwZH3WcJgQNWkD5p7WkeJewHq2Yyd0eG5Jg3kB7wnFtwPUV1eNJRM5rephgylkLA=="], + + "@nx/nx-linux-arm64-gnu": ["@nx/nx-linux-arm64-gnu@20.8.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-syXxbJZ0yPaqzVmB28QJgUtaarSiW/PQmv/5Z2Ps8rCi7kYylISPVNjP1NNiIOcGDRWbHqoBfM0bEGPfSp0rBQ=="], + + "@nx/nx-linux-arm64-musl": ["@nx/nx-linux-arm64-musl@20.8.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-AlZZFolS/S0FahRKG7rJ0Z9CgmIkyzHgGaoy3qNEMDEjFhR3jt2ZZSLp90W7zjgrxojOo90ajNMrg2UmtcQRDA=="], + + "@nx/nx-linux-x64-gnu": ["@nx/nx-linux-x64-gnu@20.8.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MSu+xVNdR95tuuO+eL/a/ZeMlhfrZ627On5xaCZXnJ+lFxNg/S4nlKZQk0Eq5hYALCd/GKgFGasRdlRdOtvGPg=="], + + "@nx/nx-linux-x64-musl": ["@nx/nx-linux-x64-musl@20.8.4", "", { "os": "linux", "cpu": "x64" }, "sha512-KxpQpyLCgIIHWZ4iRSUN9ohCwn1ZSDASbuFCdG3mohryzCy8WrPkuPcb+68J3wuQhmA5w//Xpp/dL0hHoit9zQ=="], + + "@nx/nx-win32-arm64-msvc": ["@nx/nx-win32-arm64-msvc@20.8.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-ffLBrxM9ibk+eWSY995kiFFRTSRb9HkD5T1s/uZyxV6jfxYPaZDBAWAETDneyBXps7WtaOMu+kVZlXQ3X+TfIA=="], + + "@nx/nx-win32-x64-msvc": ["@nx/nx-win32-x64-msvc@20.8.4", "", { "os": "win32", "cpu": "x64" }, "sha512-JxuuZc4h8EBqoYAiRHwskimpTJx70yn4lhIRFBoW5ICkxXW1Rw0yip/1UVsWRHXg/x9BxmH7VVazdfaQWmGu6A=="], + + "@sinclair/typebox": ["@sinclair/typebox@0.27.10", "", {}, "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA=="], + + "@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], + + "@yarnpkg/lockfile": ["@yarnpkg/lockfile@1.1.0", "", {}, "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="], + + "@yarnpkg/parsers": ["@yarnpkg/parsers@3.0.2", "", { "dependencies": { "js-yaml": "^3.10.0", "tslib": "^2.4.0" } }, "sha512-/HcYgtUSiJiot/XWGLOlGxPYUG65+/31V8oqk17vZLW1xlCoR4PampyePljOxY2n8/3jz9+tIFzICsyGujJZoA=="], + + "@zkochan/js-yaml": ["@zkochan/js-yaml@0.0.7", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-nrUSn7hzt7J6JWgWGz78ZYI8wj+gdIJdk0Ynjpp8l+trkn58Uqsf6RYrYkEK+3X18EX+TNdtJI0WxAtc+L84SQ=="], + + "ansi-colors": ["ansi-colors@4.1.3", "", {}, "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "axios": ["axios@1.13.6", "", { "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], + + "cli-spinners": ["cli-spinners@2.6.1", "", {}, "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g=="], + + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + + "clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="], + + "define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], + + "dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], + + "dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "enquirer": ["enquirer@2.3.6", "", { "dependencies": { "ansi-colors": "^4.1.1" } }, "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], + + "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], + + "figures": ["figures@3.2.0", "", { "dependencies": { "escape-string-regexp": "^1.0.5" } }, "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg=="], + + "flat": ["flat@5.0.2", "", { "bin": { "flat": "cli.js" } }, "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="], + + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + + "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + + "front-matter": ["front-matter@4.0.2", "", { "dependencies": { "js-yaml": "^3.13.1" } }, "sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg=="], + + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="], + + "is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], + + "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], + + "jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="], + + "jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="], + + "js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + + "jsonc-parser": ["jsonc-parser@3.2.0", "", {}, "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w=="], + + "lines-and-columns": ["lines-and-columns@2.0.3", "", {}, "sha512-cNOjgCnLB+FnvWWtyRTzmB3POJ+cXxTA81LoW7u8JdmhfXzriropYwpjShnz1QLLWsQwY7nIxoDmcPTwphDK9w=="], + + "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + + "minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "node-machine-id": ["node-machine-id@1.1.12", "", {}, "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ=="], + + "npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="], + + "nx": ["nx@20.8.4", "", { "dependencies": { "@napi-rs/wasm-runtime": "0.2.4", "@yarnpkg/lockfile": "^1.1.0", "@yarnpkg/parsers": "3.0.2", "@zkochan/js-yaml": "0.0.7", "axios": "^1.8.3", "chalk": "^4.1.0", "cli-cursor": "3.1.0", "cli-spinners": "2.6.1", "cliui": "^8.0.1", "dotenv": "~16.4.5", "dotenv-expand": "~11.0.6", "enquirer": "~2.3.6", "figures": "3.2.0", "flat": "^5.0.2", "front-matter": "^4.0.2", "ignore": "^5.0.4", "jest-diff": "^29.4.1", "jsonc-parser": "3.2.0", "lines-and-columns": "2.0.3", "minimatch": "9.0.3", "node-machine-id": "1.1.12", "npm-run-path": "^4.0.1", "open": "^8.4.0", "ora": "5.3.0", "resolve.exports": "2.0.3", "semver": "^7.5.3", "string-width": "^4.2.3", "tar-stream": "~2.2.0", "tmp": "~0.2.1", "tsconfig-paths": "^4.1.2", "tslib": "^2.3.0", "yaml": "^2.6.0", "yargs": "^17.6.2", "yargs-parser": "21.1.1" }, "optionalDependencies": { "@nx/nx-darwin-arm64": "20.8.4", "@nx/nx-darwin-x64": "20.8.4", "@nx/nx-freebsd-x64": "20.8.4", "@nx/nx-linux-arm-gnueabihf": "20.8.4", "@nx/nx-linux-arm64-gnu": "20.8.4", "@nx/nx-linux-arm64-musl": "20.8.4", "@nx/nx-linux-x64-gnu": "20.8.4", "@nx/nx-linux-x64-musl": "20.8.4", "@nx/nx-win32-arm64-msvc": "20.8.4", "@nx/nx-win32-x64-msvc": "20.8.4" }, "peerDependencies": { "@swc-node/register": "^1.8.0", "@swc/core": "^1.3.85" }, "optionalPeers": ["@swc-node/register", "@swc/core"], "bin": { "nx": "bin/nx.js", "nx-cloud": "bin/nx-cloud.js" } }, "sha512-/++x0OM3/UTmDR+wmPeV13tSxeTr+QGzj3flgtH9DiOPmQnn2CjHWAMZiOhcSh/hHoE/V3ySL4757InQUsVtjQ=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + + "open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], + + "ora": ["ora@5.3.0", "", { "dependencies": { "bl": "^4.0.3", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "log-symbols": "^4.0.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-zAKMgGXUim0Jyd6CXK9lraBnD3H5yPGBPPOkC23a2BG6hsm4Zu6OQSjQuEtV0BHDf4aKHcUFvJiGRrFuW3MG8g=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], + + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve.exports": ["resolve.exports@2.0.3", "", {}, "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A=="], + + "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-bom": ["strip-bom@3.0.0", "", {}, "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + + "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + + "tsconfig-paths": ["tsconfig-paths@4.2.0", "", { "dependencies": { "json5": "^2.2.2", "minimist": "^1.2.6", "strip-bom": "^3.0.0" } }, "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], + + "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], + + "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], + + "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + + "js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + + "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..275954b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,162 @@ +services: + redis: + container_name: "${NAME}-redis" + image: redis:7-alpine + ports: + - "${REDIS_PORT}:6379" + volumes: + - redis_data:/data + restart: unless-stopped + + ml-inference: + container_name: "${NAME}-mlinfer" + build: + context: ./ + dockerfile: ./docker/ml.Dockerfile + ports: + - "8200:8000" + environment: + - ML_LATEST_WEIGHTS_PATH=/app/models/weights + - PYTHONPATH=/app + volumes: + - ./ml/models:/app/models + - ./.env:/app/.env + depends_on: + - redis + restart: unless-stopped + + worker: + container_name: "${NAME}-worker" + build: + context: ./ + dockerfile: ./docker/worker.Dockerfile + environment: + - REDIS_URL=redis://redis:6379 + - PYTHONPATH=/app + volumes: + - ./.env:/app/.env + depends_on: + - redis + restart: unless-stopped + + minio: + image: minio/minio:latest + container_name: "${NAME}-minio" + profiles: ["minio"] + ports: + - "9900:9000" # API endpoint + - "9901:9001" # Web console + environment: + MINIO_ROOT_USER: minioadmin + MINIO_ROOT_PASSWORD: minioadmin + volumes: + - minio_data:/data + command: server /data --console-address ":9001" + restart: unless-stopped + + create-bucket: + image: minio/mc + container_name: "${NAME}-create-bucket" + profiles: ["minio"] + depends_on: + - minio + entrypoint: > + /bin/sh -c " + sleep 5; + mc alias set myminio http://minio:9000 minioadmin minioadmin; + mc mb myminio/${NAME:-app} --ignore-existing; + exit 0; + " + + tensorboard: + image: tensorflow/tensorflow:latest + container_name: "${NAME}-tensorboard" + profiles: ["tensorboard"] + ports: + - "6006:6006" + volumes: + - ./ml/tensorboard:/logs + command: tensorboard --logdir=/logs --host=0.0.0.0 --port=6006 + restart: unless-stopped + + mlflow: + image: python:3.12-slim + container_name: "${NAME}-mlflow" + profiles: ["mlflow"] + ports: + - "5000:5000" + volumes: + - mlflow_data:/mlflow + command: > + /bin/sh -c " + pip install --no-cache-dir mlflow && + mlflow server + --host 0.0.0.0 + --port 5000 + --backend-store-uri sqlite:////mlflow/mlflow.db + --default-artifact-root /mlflow/artifacts + " + restart: unless-stopped + + # Logging Infrastructure - Minimal Setup + loki: + image: grafana/loki:2.9.0 + container_name: "${NAME}-loki" + profiles: ["logging"] + ports: + - "${LOKI_PORT}:3100" + volumes: + - loki_data:/loki + restart: unless-stopped + + grafana: + image: grafana/grafana:10.2.0 + container_name: "${NAME}-grafana" + profiles: ["logging"] + ports: + - "${GRAFANA_PORT}:3000" + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + volumes: + - grafana_data:/var/lib/grafana + restart: unless-stopped + + # Database Services + postgres: + image: postgres:15-alpine + container_name: "${NAME}-postgres" + profiles: ["database", "postgres"] + ports: + - "${POSTGRES_PORT:-5432}:5432" + environment: + POSTGRES_DB: ${POSTGRES_DB:-app} + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + volumes: + - postgres_data:/var/lib/postgresql/data + # place .sql files in ./database/ to auto-run on first start + - ./database/:/docker-entrypoint-initdb.d/:ro + restart: unless-stopped + + mongodb: + image: mongo:7-jammy + container_name: "${NAME}-mongodb" + profiles: ["database", "mongodb"] + ports: + - "${MONGO_PORT:-27017}:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_USER:-admin} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD:-admin123} + MONGO_INITDB_DATABASE: ${MONGO_DB:-app} + volumes: + - mongodb_data:/data/db + restart: unless-stopped + +volumes: + redis_data: + minio_data: + loki_data: + grafana_data: + postgres_data: + mongodb_data: + mlflow_data: diff --git a/docker/ml.Dockerfile b/docker/ml.Dockerfile new file mode 100644 index 0000000..2110166 --- /dev/null +++ b/docker/ml.Dockerfile @@ -0,0 +1,38 @@ +FROM python:3.12-slim + +WORKDIR /app + +# System deps - layer rarely changes +RUN apt-get update && apt-get install -y \ + gcc \ + g++ \ + && rm -rf /var/lib/apt/lists/* + +# Install external dependencies only - layer cached until pyproject.toml changes. +# Stub files satisfy setuptools' package discovery without real source, +# so external deps are downloaded once and reused across source-only rebuilds. +COPY pyproject.toml ./ +RUN touch README.md \ + && mkdir -p alveslib && touch alveslib/__init__.py \ + && pip install --no-cache-dir . \ + && rm -rf alveslib README.md + +# Copy local library and reinstall it without re-downloading external deps +COPY alveslib/ ./alveslib/ +RUN pip install --no-cache-dir --no-deps . + +# Copy application source last - most frequently changed +COPY ml/ ./ +COPY src/ ./src/ + +RUN mkdir -p /app/models/weights + +HEALTHCHECK --interval=30s --timeout=30s --start-period=60s --retries=3 \ + CMD python -c "import requests; requests.get('http://localhost:8000/health').raise_for_status()" || exit 1 + +EXPOSE 8000 + +RUN useradd --create-home --shell /bin/bash app +RUN chown -R app:app /app +USER app +CMD ["uvicorn", "inference:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/docker/worker.Dockerfile b/docker/worker.Dockerfile new file mode 100644 index 0000000..95ca42f --- /dev/null +++ b/docker/worker.Dockerfile @@ -0,0 +1,34 @@ +FROM python:3.12-slim + +WORKDIR /app + +# System deps - layer rarely changes +RUN apt-get update && apt-get install -y \ + gcc \ + && rm -rf /var/lib/apt/lists/* + +# Install external dependencies only - layer cached until pyproject.toml changes. +# Stub files satisfy setuptools' package discovery without real source, +# so external deps are downloaded once and reused across source-only rebuilds. +COPY pyproject.toml ./ +RUN touch README.md \ + && mkdir -p alveslib && touch alveslib/__init__.py \ + && pip install --no-cache-dir . \ + && rm -rf alveslib README.md + +# Copy local library and reinstall it without re-downloading external deps +COPY alveslib/ ./alveslib/ +RUN pip install --no-cache-dir --no-deps . + +# Copy application source last - most frequently changed +RUN mkdir -p ./worker/ +COPY apps/worker/ ./worker/ +COPY src/ ./src/ + +HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \ + CMD python -c "import redis; r=redis.from_url('redis://redis:6379'); r.ping()" || exit 1 + +RUN useradd --create-home --shell /bin/bash app +RUN chown -R app:app /app +USER app +CMD ["celery", "-A", "worker.worker:app", "worker", "--loglevel=info"] diff --git a/ml/__init__.py b/ml/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ml/configs/data/default.yaml b/ml/configs/data/default.yaml new file mode 100644 index 0000000..df01a76 --- /dev/null +++ b/ml/configs/data/default.yaml @@ -0,0 +1,6 @@ +dataset_name: synthetic_classification +output_dir: ml/data/processed +train_samples: 2048 +input_dim: 16 +num_classes: 3 +seed: 42 diff --git a/ml/configs/train/default.yaml b/ml/configs/train/default.yaml new file mode 100644 index 0000000..60d311e --- /dev/null +++ b/ml/configs/train/default.yaml @@ -0,0 +1,10 @@ +input_dim: 16 +hidden_dim: 64 +num_classes: 3 +learning_rate: 0.001 +batch_size: 64 +epochs: 5 +log_every_n_steps: 20 +seed: 42 +tensorboard_dir: ml/tensorboard +weights_output: ml/models/weights/model.pt diff --git a/ml/data/README.md b/ml/data/README.md new file mode 100644 index 0000000..561a359 --- /dev/null +++ b/ml/data/README.md @@ -0,0 +1,5 @@ +# Data + +Some thoughts on processing data: In a lot of cases you will get data not in an s3 bucket or anything glamarous and for doing anything in terms of modelling you need the data locally but then when you maybe have a 1TB dataset you want 10GB locally and then you upload to a GPU rich server and there you will want all of teh data. How can you managed this data well? What are best practices? + +Huggingface lets you upload up to 300 gigs of data into a dataset. diff --git a/ml/data/etl.py b/ml/data/etl.py new file mode 100644 index 0000000..61e1ac3 --- /dev/null +++ b/ml/data/etl.py @@ -0,0 +1,52 @@ +import argparse +import json +from pathlib import Path + +import torch +import yaml + + +def build_dataset( + train_samples: int, input_dim: int, num_classes: int, seed: int +) -> dict[str, torch.Tensor]: + generator = torch.Generator().manual_seed(seed) + features = torch.randn(train_samples, input_dim, generator=generator) + labels = torch.randint(0, num_classes, (train_samples,), generator=generator) + return {"features": features, "labels": labels} + + +def main() -> None: + parser = argparse.ArgumentParser(description="Build a synthetic training dataset") + parser.add_argument("--config", default="ml/configs/data/default.yaml") + parser.add_argument("--output", default="ml/data/processed") + args = parser.parse_args() + + with open(args.config, "r", encoding="utf-8") as f: + cfg = yaml.safe_load(f) + + output_dir = Path(args.output) + output_dir.mkdir(parents=True, exist_ok=True) + + dataset = build_dataset( + train_samples=int(cfg["train_samples"]), + input_dim=int(cfg["input_dim"]), + num_classes=int(cfg["num_classes"]), + seed=int(cfg["seed"]), + ) + dataset_path = output_dir / "dataset.pt" + torch.save(dataset, dataset_path) + + metadata = { + "dataset_name": cfg["dataset_name"], + "train_samples": int(cfg["train_samples"]), + "input_dim": int(cfg["input_dim"]), + "num_classes": int(cfg["num_classes"]), + "seed": int(cfg["seed"]), + "dataset_path": str(dataset_path), + } + with open(output_dir / "metadata.json", "w", encoding="utf-8") as f: + json.dump(metadata, f, indent=2) + + +if __name__ == "__main__": + main() diff --git a/ml/data/processed/dataset.pt b/ml/data/processed/dataset.pt new file mode 100644 index 0000000000000000000000000000000000000000..23a4d1201c27f737b95f0db5d5819e1ddc56dfe9 GIT binary patch literal 149285 zcmbrlcTg1J(=JNRB2h^qN>EgiA|Nntub@N&VggYCB^W>?Dquh*ixQNifJjDyfFKIY z+Y2fvD&_=;iV4L`P*L#g@1A?UTj$>T{yBH5W~X**XS%CrrswJBdB(|6P>7FDOpNdU zv&itt@p*ZMcm{ii82o3ezk8>jjg>UthW~Ae64)QeH{0bu`}=r%hJ*%r2M6+R4G9R^ zx=r6bG-SJ9u!MV%_m |+|M{atppXkc?>~lt!Y+cm?bh9)fg;QKqCx^iS2^-6=M!_3i4Ej^IP%8^iYIK{ z8sO*Wy)|Tez^-74l|f$KLEc_Vwr>rQ2$axu4wMWDlyVN7 216u zw*9Zb%K~=!{#V?||5se2K$&g4cw!-evi~z)OrV@2e@vi!!nXe$QXwQz(K&ERsJ@e< zs94n`I#rOD?0;6L|F)!4`ON;Sr20MqLH?d0?qS|R!MvPptc?F(mHpq!{NH{t`cL%6 zuf0e9Q>p)~|9x~7KGXjv`u|;ccTd0VzPtRrcZJwkE%-l4z{L1J3BaDf4+6a4|5^Y0 z1Z4T(zY^FI9^xJFpR;6RW$}L!_kR_{Z+l3HpZ9-qh+$qU|L64nXZ`PUkme)*mBW9o zY6C+XEA#&!3FsD&!fJO4zUe;D!xiB^s?>v2;XI7>HDsH-Z{xS49CQ $4T;Mkb&fWe zu(<;T`VT Sk{4F`x}cJ(+eTT*^u&06#=_XzRZ5>ZW6yh9>naluwdaN?pb3FE|-~aSr|}7 zx1O%Rr`?K}Z!HcL%SGU=TpC!$uVxB^30S0!(t$OxI4xTR=7jWO`M_#SdU6iJ(tZ+; z91X5b&u? 7^Ptz|4n@}_T9;>uP8ClLS;l5i0538D(;|n@yg0?8MK3>^!D>sa6XcQ$j#nxb} zn*@d;)ueN!83-5MCrb@~LTmIPnA_Wka3%!{mq}8UwfA6Z?ko@wm4~ltX}~IES)S2) zkIO&!fu =qf7N{t+ zfV>n=!c6(w*fh5aR{Sc0js@$O`Twrr{>H@+6mJ7dMjT+=0l4|KVBI%gCE1_j82FNY!1P8!V|jUk|EA?Qwj4Oc(zMD?v< zz&Tb)*9K;w)#^qfJ7a`M+Ef95r3jpzE6Q2#B#gtEyYTBNhP>^JhZxIJ*cC8HwjOoD zUyX %9)Y9v3r>D)>#M;f5-QW=;w$i)?Ny3l78i_)u~lX4M0`oYW&b{5^j z*n=-vr>Uk8?4-f?>y*Ns!~4jE8RK~QRGQ`Qq#DdI)_?~A+T6xdS6i$&W2}+|;MvY@ zaOL 0XfHigz`wkPd30Mne zTSJj+H9)=}JOht&<+(2|72#ZCGxADJj5~F?IDKy#gRiby<50jBHp%Q1cpZtvjKSyR zcD5<5pArWj#G24@_7@^Li;|(ttmb gc+ll=dXF%b0+j%n LV&6A#qygP`Ix5H*#hXZIbW_n$4L>OD3PEN1}PN#*!;nh;hM zE5i-$H!5>i6`w6&*i)tVu{P`{{c?oC;1Bua<&X>+2#E&s%^Eo6xgb8hXbAg0|E00} zQek=6C;Gaz0TRtzAy;<^dqrn1ctpj3Pp% 0qI#LzaGaA*a;C0;ti#7qZ}LAfGb z>7lgF>?XlaC=TSzK|gud8y2RsSJeA zP{8#~iMVn`2L(|lUi}fa+*-h7ytoA8S^nf@IX|qeEX3(P)7V` zvPBByEBT!XmI@s2j&h*1 5{Sp)n8RHMgNAFXM zIi}8)Fj?FUBwiJf1kX2Q&9^pCdS3`JZ$Gmw{^uc<8Ks`_`|&_-HSz_DSPtB}Lz~4- zQ1)*f-My=gc-=n;Hj4^j)!9&(H2#1ov{(bdZ-qJAvK?W^#BpNqiI6`z4Wv5%GP6kF z1+0-Oz$u(Y8pIn~yL8it5nbz7)#kq>z=KaY)Aw-4g-3!ymv1QCze46p1~Vuf%c zY6%Sz(?L=AyQCW3ea7JBrhahTv=5_P^6=9tdG1D&=%&$NR~+?uh|}-q5X BjFz8OP%O!ms=F>DE03H(cbkWF$h8R~y2{B4!6>|?X@u=%->HFA zF`m4V0HR0Z@p{1o?pTyX)&};Gy<0Y0e2_bWte-cL@Lh~=rOv~S`a zgZhjs05BW$(FXdvosxp4jNuh$SE=+00 zGoq*Xlbrwbn~YC71V+wEXd-%pj8r+p#QiNO6~e;V`|F_Z*)nS1Z~(M6n?aJRI2D$w zM hTD)(5+WL&gN39GydfTLFo!r>Ws#y6j>+Orq4!&=~LMm@}D%8(5&!-Y=yICQ%c zAN0)Ro;!6O9$ys4DX%S|ti=`Q^~p18rz>D6dMCiAR@ze1fvY8#qnV@$>i@n*1C2vr zOR6&t2`F<*$BPIn^$jXqkAZ)aFox}8@HR(>M4bov>;{W5BPe=_&7^IQ7n?o%@6(N2wZcGBcscWK+r%@8CXhtlHb(5BTG=6$ 1ddW0-_`9wYE&3jS=6h29tk6r_&yw{11v92P>K zf*eLxwVHUWZ30u{TKeT#EFCd>OYwv`Rz+*kV{?kASHuj=`DBmt3`?On)E_%zVrh8T zE;wK?NH=H(Vr}F?tQe5wq%P9I?+4zKj2Rn1Z15?*nU)FXv=?zt?{25k9wxYGq6=@` zZlwL*&W!wLBQSse5f&aEK_T-x{OKkE?^^pv##ULblDs(Wd=|i(UEw3=LMP!^d=_)M zPl-l*OhVDs$mSOwK@IskWSNFIC+MpnCTAqVu|H?P#6J=mUA7Uj@-K<86U2Fg*`(h$ zkCA?;4J|&;=|bBKIJJKPyy=|-{+E`c+u9oZboUiC)|Dmp{l}o@%OtoN)(KCp ou5H-?~8EuT B6gGV$OdLYu#WEA=nD2LR*=@LtHdNF7lfAP z!c1FZ;6Lia&OJ}DZGRhv-7O(^s+w@y kU>KR>p<+>4*VQ#i>Est(S0e$ zU@Y_y5hT6rn#D^&xGn|TryhiBx|wL`9|lpoj$v%11IF2IqEEd4nR`4~N0pni;nmPQ zQ2F#7oB!C566xDu)fSBt<$w`)uTrNUa%3W)gf-MjfmnAPMty5EJ3VG2M=azxI &VDymP@hBIaR zssDm{)RZk^O@9eN&5Lvp_LAhZwY-Ko*3sm7hbnis+y%OGX$AINdP(cADB?M%_ptbw z1qSEbBX)6$_-f-b=7(4=6(O6zaaTOvC_jWgfd_E%40F!kEtg?U^JE-#2*RQ73?6oy zPUM}Vsj>tICu`-wldeO+)*b@fGZW@Sdcf}xCx}&bAeRI*v38FKrpC6TPrE%%kC6ra znbz> j+b{I1lI6n$6Nyi|Y_HKUcp{rySqi~MeBdvX*G z7xH74stexWZlV2YvYZo#?eK+40+qgU9j?w!rLU^4vK|l*U8xjezk0&C=^^ypKS}sL zb1yruOBCO&7T}Jo5&_Qc`535J&vpbQ(d*lb!9xBr3aXu@1E=0GO>dg9l-q-Sy;AsT zObh~dR$zf%9%#A?aD}}a!1nSy;%=CR6P~IN?6?+X-A3s0m~3KHX$A?->X=v~3Gd%7 zL8-_B=G7c4wABd&LE}CWek&Q)g!9ABq*OFlvO;$wJ*KPN5S>0u#uGNHA$OiE=hRp~ zI_K|4+t`^9KIsTZO$&w74LO!=qwzQt$wB2gYMj!i!+1&U3^5*U!SDUURN3$wJF#sB zHjkY}$)6ci*CClsZA^rknMNd9R}dOEMB+)c6c}zjO!f_bq-N**amq^ry8MO#2D}iZ z{pok%;nQmvSfNK{U%j?8j^Xi56LaCQ@jfuPV}(O+3Q#Xd1ASx8gKKdhaVmO4&Dt`M zbGse}JJ^ MCmoC$p$NR~(CpN@k;vdz%D}~hSisif0ZP*$s3g=a>)4orm*lazSSr++| zj(*KSrMFddiOe^PY0Ya{r`_ogw&yu4%@X9;y6vVrnM&M8gwSxFAzFqGk!#S1%mO(w zPk1Iww~ocBTwx5=NX39k8QN*9jmO9Dkv7{>GBtNC&Y1W^!$N1nYaZVhsgMT+7Ophu zB#%$jU5&j_4Wzv#nF=U})2VJ5wCkG&p4ZT0Z&@c&`#G1uI$W3;x0S+zD}U*O-DaGA zpLDF={|j`!pG0H#TX@;`9CK6A4;@4(J$-&J+U-{(3i*rRPfsmsOk~2H)>oExvV5HH zM*rB$Vkfb()Cb=F;LtU13-CsoJoGXb>FEbTI9Kro71DVL%d_l=_VU;8se+*zd2!$} zy#(6K%7}-R61g0fNP2BDah_W=K3O?VQ+2#CqCpHser>_GeU5OYY&J;0&7?ZFc9F)A zN<4p&hXqw6F!FK>qs8NtPLpW7608VQW*mm;PLAk2y@lMr-9sKe<>$CIEWwW>IXIv> zz=r+ECG#G kK4mgtqc36WG!5LdWgTfR z;zEykD(c9X;Hmfu5cO__bl+_J`IJlUoQR@H%A&a4beInF%V6|^IE+%>LvCnVkVPM+ z!!#>L #O5GfFhz1=bJ;B*Q)SuWtdyHyCw zZ9U1+4JE|#Q5l?$cE!bg^HE`nGmRayMaAz1urAvUl#Gs}LYED<%yU1A&QZbj8m3sY z@+*C+I| T*to#KG9QDfFYB7jKrfA)7z{0lRnx?>*UsEiZDY!2)qyA=${|uJh1R z@CxpK?u5(IQXxCWgPbaQK=z%wVPU?AhY|Pc(`wPvROw<7y7gD%co4%5`cFgtWqD8> z?t}u3Y0&&K0~eZ1VVnNU1?N+Kpr@2b?!4E4-*1d@w~`x1@Aie1mom7a>?+x>Xonk9 zRH!5W6H+m90iHLn#uFVK;BJ_N#WWM<$z_2}vLHuOR2K?vAH%rFbShQ%ft=PjOYzhL zdQq~Icwa~X%^j=Z%8Vee)>o&azi#2h%wYELyaT8)coMD(bU|NE25sEO qMf8$Ab^!WojGu?+>nBWTYH8-Nd3N<9s@esZ{ z2g*+^1@7%a3{JX+OVw5I$nhdF*KRM%cBSK+!$w?N=i!zWr91F($2St=_6=4w34`*# z)l7qWH(8hx1W~~+2%n`hEh=xpggSnhvFtb*8LtF)HE(oXehU@C=fO12K|EkmM;8xl zCiga^K%Wd37cLH?eW|}_(V;kYtBx=3>eGTUS1XvhjGsF>eje^$wG+2!od&(Nm002N zllJ}Tf%<4!j7v4=_EjZ<@7g@3qa+vRJT>PW6aNKF( 3b>p1j)v48M|`F*OZW!IlpqTwGwVu%;s&u#?SucK&eBZs~>=b+O57WQ0F2;8z} zh^o$4_V4K(v_GYWuDzLn9_CUwx~+^7s{m%1geXV2b18)FK2J^8 1QKBy-boRI+G5u(!TJH?Lh%;hS|A-%Z%hy;qOavXzI?pPC_N&X>S`86>u#x z&C NTZ^9bgG*q`=gV$G-fldAyU<{+_#EA-&-Xe{* zvopXs*8rwG+z2re_i3?}6|UR#4E(kbXfP3B$ExF?Kqbn;XVeIv34O=c-%gV$Rtr$; z&ufU _WN67k_5He&lIYQb!>2fEY(x zA%J9*XT#sw1z6M>h~d?y^xM@lWOj)ujNaZ!`Tm%IT<0Z7?6xBtoYtbk>tK93zljR9 zoWZh6mVItD3m)gsB8p5U@ENmo%ck#Cw{Do!K5+%zXDvkcXB?Pce+7-YHY_RK4%~k{ zTvWagTkUp11Lqmt`(l)~?HDGEqbYse`Iop>Oa?decX;!(D$Wj=f|HtS=&V$8d=i4V z(7+Om-Xu`|C+|pJ)-1+-;ux#s6-~OMhDqN^it2}o(PFMAv=pQ>s&fuPhU5`= Gv zKN) Jy zRrk!ppuBmsdxIe~WISZ cmvQksZ`7Vvk zTRhQU+7mC>wnK4mq-DQBf6HmFxAcJiH(2ECh4Nos63d@QaQ?}Q*luD5r&E>S=JHCE zA97>Y#3!Q1#{weXA j;`YbSCMRYw^=WC@e|NK@sPCrq*pb)rz=B?1TOhx;YBZ zj3qL+o{FJJ?*+Uh_XCa!SHrlSE7nyEVpLNVeEXXQRSj-fs5(TC+JvHpUj UDx3M?_=6NuW|oO=fj-Dh6yXf679$tx8!h** zO{Rh?3~+gzBf7nKj{G+>VMfUt!jz_fNcA~t!}Wqi2OH62wg=urWy=R!XMiUECU`S9 z4f2f65dD#7Y)PIYhQ=kMN0A%6pii+&=OmdrAVg|HmxE3F3UYt+D*4)~PZBuQ09{9D zU3IrbgSI4+FZEcyV?Jj~+acUSqQFgHGvq#rAU8EiVfaoQD%{XvZw>yzLw?$D>}U`e zya>a2YFpV)@;V?r^9nYKex|xV?qJu!3vh|AgM>CNf _EWe@@eH$er!m=Sr~s8o z?a=hCl98Q$f-a5HMe`o7mYL?q>8hFbIPiFo)C|eMj^-2Ku~V5_-`YmzsvW_{Wy_%A z(q Sd z6$=Sl9>T{D_sIVIkfyGo=j@G;5mL3i7-K)w64Q0}nvY)PQdezR&QSY7aH+_mbITTE zO!X>y;>iVQhzrE{d;A;=+Z*I!?gKJ2`ZM{wez;{@YXLm(O@!T (7arXW++})XsUE3W| ;*~4^xu?c8Z|3+(X73|~9uVJCBmLCI5an_Dqpg#DHG0xpc_&kcR zs9c&euD==rXMZ5^>n20GzAG8Jp9zD #yyu6W?w91uDDQb!W{7p~K9ks#NLYO30g;+6f(wJi$-D2#SaGTp%oe7z zqxH!UW*JGXeU6e>0X;PJyE910@$}>feLRr90nSukhvSLLSeYG4dMZUYd#5kO9TlxO zlHWx`lKSb3UuRKzlm%zYRkTMl3(ttjkdY*9xMS%@Hx+l1ODd8yUH2dvk-kFX`R<{? zjS_0Z!;Xyu(%5>covdz@<)Q$RZ QVW*%KdbI;gPg~UftdSV`^KTiiKnOe&zpB>0HxRaDi`LOBw3QYMa$gx(=A-O`$ zs2#hK+P~pro(iTD>obpuf148~d^JKZ1s_-tY>lO^_H@5s6TQ&cj# XQ8e`G<=sA=N_a>L9Sya-V;4U1}{az z)A*xA+S3eF_j^E=$0M}jNORA1C!$lk8k|;Az}cOPP^kP1WJ mOg=$PIvu-=tQKUccIjwxA|5qlqF#;sp?O|p|YQZ^6W z9)3roM}t@yEJ~7oJD|F6E(k0*!!-83g9ZDlXy}x$l-qWje!pFc@8PdyOZHUks>r~_ zPx?SIE0b>TuB95QCg_&965LQ;3fGUs)0_47 BMMczwoH2+&eP)ZSkEpAZnNuYpa !1x8o0tR7)}F*aZl)-` z_XzIbu$Xezg1I2r!=CP_C)JYWq~(JsW_Kwt_ pwM0T}SOyV16ItXMmT8XXpbC$Sbhj;exeE49M%C%(+q16(vMeoI|4 zMBv}lSfU(Z3VWUA nYU3e)R>FObTFH@O|0T)oGWHPMZd;(kt z=EG7Rrn63-O_lfjrK}`BtP`0;7C%WMMOVL)13wqw&X1;aQnNRFZ&}Th%86mP>s_)% z;2v2sK29B;`D3i?GP;+3C5xnw!lU3ZI(bzMyCPB*!j^9*YVPXzkDCH1?myYz-nZeR zMGbR`kJrQX>X^BCN2nIh1G$CGgKXQ`*q!|e8s=o #bf z(n{4UWH=E)<+OaR8NS!ff%djWSl^aU-}ib$%#E2mUfu;?G*fn4%niEwz|EG7@ym3g z)(EF8<9TaNY-P3hV|kjjHf?j4!?$`J*qarG8OblG=_IKZqiPdoT&;m7ET2LCN}t5- zUIXC#R+u~esSd8SN`rS}9CWB@#(w7_M!s 1$-|7lpf?X5!pNW z@LXh!O|rSe+_U;lnp6!yHBkTpFWVy4>q6JFS&$qaz+Agw4>SHGqDx6Eq_63q-aRd_ z^`|~nEfeN0f9?oMG7}_sg9`NA6vm#sXh_fz1D| )2yjL8#&Ob&huwo zV$C0)g5cdAFwgfRh*wR+XJM+y-WTMCUbq5N#w*G18B56ipjo7~V=ipCbdPqoh~nky zL!_+Tk-~g6$X$2|&(Mc3Jn15d`1&4?^n50h9i*`BY#g@g&nHLET_L|B>}dM13Vo%m z0m=)fqpRLg+@4>KMVhUocu4@;#Lwczo%?XA9fi*FDMY8c8fDnIxQh)2k((_L@Tvu8 z)o;R)>_RvhS5LUP*(|r~4%s;_1{r-xtY^wJj21~G+XDI7QTsx)(bPnp5i5*W|4O%y zW@FaIY22LqV>EQ)CO*6z1h?I~DY?Po-TZe{^Tby87<&R2M}4=9R1rpZS6zB?{1`Ql zi=c^nCeT{B0Ckt;($0x>ATRrks*7wRuHxs({dZI0;LZ_PDmOykKJ{az&h_$o;eH&q z9)lk@V%Xnz6p4*{Dp-^zP@&@ONIDFFORfVSPv`m}assauI-pcS5p}MX1QVkI%o~-5 z^P4MMN}VTBfg?AW+gu%5w#JHZ2lKHm=Ljhgh{vnnW}v6)475G_7>~`I2hC{{pnZ?$ z=lRx*Oy5Ijp4)^Yg8np2K^h#iOUOq C+q-+jlamP z #Z_4p)8^wIJCKVlF&=P( zcMq!ly9o*kTAZN={PgCSAKCHTfd~6@DR;6Sq#U2iu^iltiLd1ui;5%|-keM?jfjGX zu^8wNc4DJ|DkpLMFws4r1QUOb6IL#iq-OmfOSer}RvuQO6MvfMGoJz8Tz(u(f2Kq0 z+a8pcI|9ws(fFzHABt$S@(_qUF>a6N@gTp5llv8mzlu>16=V#R#q;r@$`9hNG8;8@ zH{+qgN319f2R+d(=ycl{#hw)6n4u&|9!REIPkUihLV(OX>WR aVn zqSj%aju{>aw>B(-z=dKw@3ICS+NntEUOlDD_fgz=!43SuinCeb1coPVBi8X_mNy0V zS_ao;K&|K=464{izX<80x78tJZnjgcj2g@yjfdnFzBtgh1eBh?BubC?P&Go7BPyN) z;ga_FE$%4nP>yFszvP3f#7uHW`31V<6(e^;DYSVlgazXn >8c2i*Da zYpgVPQ>P(ia&^(WS_;EA?q=@2xe7OGVxXWrmPYw{kPBU_m;}2N@b!`e!-wuXy!C*5 z8BW19@0GY0T1{bXVJEhggyF%VSZvs?hR#E;m`=%Y#zXHCem5MX@w3dpN$wgu%Ss=^ z`z_$}7A_NYJ`wgPuY}h|5m=WLLpQ$mf&B6UvaE&+X>wmK-xFVtM`RA7gX&-Ser-Cw znXrZQf)=!^+78<`JE8GtDZOTX8<(%sAUc=x;Dmbxmb`hyW?j@E|IW8!>ec cdviyJY9qZpdTOq4r5A{m1x_kMc^~@T}Y9U$-1LadikNJ#44^ zN2NG+8`r~lCdJiP6ye6WCSDR8#yMIW=>glh#8a?_*> 0H);2ao!pfvI;ZBx$h-!fqUpo zj;8bk4NC0*fq`uF$(o6=CC()9{3&>+Sb`5c-=OQUK5XiK4oWvHV9@^n^m_?f&X|!( z4U`g)yQ7o--Mk3IZByaTaVhdeD3yjfxxi|zN2o}Bp!rZb2|gDIx#R(Maa^$I*lUy* zcmk>6x%6 T^; f0Uyeg tyWwc#g8jBqMl!t-h91zqIvxFxi7>Opeq?FHnM|7`J8S{LW``Qz2aF|aA}Ijq{_ zjZ%+ZVArW&lBE6~KbSwH8zp K0*AeGMG`t45bv)nZ!001+!_Co-q! zpj5jArp8-vdZaX&`gKN_GiN%6z9OJ<`ZHZ&+d!&r$bnMOD1LUV1lx_LA;Ynjl}}lW zj~=Sx+*2{kWS2E$OyC#2yZw+ y&$3_7zG>g;d=qjA(yg~13o`-$Mi=eRf95(C?gp%@9 z)C*3=14ndGXWcgt&iBO{-Zl8BI){EA=jU2YPbF5R*T~K7fpo^>96UPWfT8u@$nS;M zn6ni zB^`lhI`+by4Kv7sa3MNlb`YqQ08~~~Lf+Pg@H0mrV~5?K{Z2gi$40|GkE3{v<>B)k zgs6*MhsG7PNRJHAD;4wM4!;!W@0tWKIgD&NnS!g7(&<0R%jo;Hl<0o-qTSXr;p?RW zu*(tw|9}Q|*Q^T0?ynT?kRG7YC*q)1?IqTBt^v2X%ju2_IXEMI4n3- a zsj+<`UHUTx)C`N@sB9O>UEcx8egYgxz5zDjaTM;|+eQbg^f0+87C)Z!BI^BzK`Wc* zYu>C4qA?6Cc@6k)5$|20>Ruv!$cz5vYBT%3HV{FTU+^ @Mse!Hr$y zx$OwOb6g%DHYsqQ>7-Hi2OnOVrU?G?=W=VB86v{Z^I|62VB+;wwtF-klT-vDud0UF z4y5Bz=_5qj;62$aWQ&Dz+Fbpta{$eGq 1n6 zT> JU}`7@!uteJYTkXYSa}{l#E4PlRyDXBV#L*4{DR0W z{KteYZv$ID1D=QD36MXNFk9$4{iYiYs`qA-eY>XOF(#npYgQW-*NUY9H-vF0@F|_I zmB)+++=dwU0(k6gO(q|^Ok_omXnzXBP@y!?yZ?}MRPl3~au4CvJ#|EGnAa0R zZ%~fe0QuXKf$2sLu*@J0Zl08e!#=+ld961jNIe2)mnmZ9)^@yo;S|~5RZHCutfW?7 z`>B0V0XT2d 0VNZCCb-yKlk%CBQT+)`xYrRtGZhw-@`AIL zOWgKQwc$_rAvA$(OKfOqvU*B3@${%Ge@_yZS+l@+sR$<{*AGXw4boMK-OTncO62i2 zQO^E#apdIZ X-Cg3xsMCA zj ACq2&+3CXl>p)$o6cY?ml&pf4hT R{q0DnKXS*}yEm zdlY;cN-$Th3Jkq6aAnkWP`kIC-t0&QNjZZ4D|QewmjU{8#xi)^a{gAh)K!|?lQL;cwQD0eRQC=1B>>;mve^8&0S3+f;gI}k z#_^~;s%a`<%|sI}bP?pT?J1b6cosC4%HiBrJ6KuG^IjI^5VttqI~Sh8WMlO^tbbff z>>{$L (L1Wum>xcnOT3a*5;)1I*j =@h_TC76WTJ`F{$;>kqx<-Ca4V!f@g=^$ zj6rgtE=-;?K;FC8;g1=HAmYB4=*aD54xX8e3bsG#_XWd*r0J8{&Mwfd@(zy9YlMUU zBJqbRSWf&tMf}*sm|bB3mOG=+K+1)z_GyOB3yBzSKM#X`Mp1n^dG2D +)N8@(BOt`6Ugl5kY@u<5k zB=6A1TB8QMbbAv|-{exi84jq+^Q%pYOu_q)8`y`Ay-eg2Db7%V05|+yCkdQA4ISLI zKyUg|?l${se0407-t$j^uGjXM7O&2V-?hT`o|0fxHb7#hZ-?Jk4+7^yB3020!J8rT zusb}L3|tbR7hWnd?~H{Y@8%AOd@hU$M>w>{>Hzvji6UQ1DK6UCfSKIeSYSMt{#>&e z^%i!~#h 1J#FYITX8Bx>LRF0ey35LQmH&j2 zjy< o=R z>_hYp@&x{^chNmj5SbQnh*!+TlYJTZiT7Op{@@nahh3vnkCo6Re92%WdJgjk&fuu_ zZPs#TKFpf;icNlZ0Y^Plar9mQj79$siq69ytM`rLWJN|uR)x?OqO9k+uSbd~Bb3sj zlG30wH7H~y*@Z$8LI}w?_w|sdtfG`kNUCpEMT1Je^ZN(9UO4AI*ZmpqD}?;_laJAf zFR9akkF@aAVw`d32xckukT^p{`p>U|w%zzjyEDu%C#wuL*T-@)_vbT=P#Y|tZ9?xo z9>gVEj>9Xp27K+diu-2b5^NeBLj(D#&^-4nrQCAZ@9PKq-eu#9U(rnX=`bSseF@H< z-2kQ$vZN`TPXw}UFtus{Pye %B}<5)$c#bAmG6gGD$HBQ=iu~*E3obE zS|;Ku`#WykNEFyS``#h;&ZN^pj!6BXn*^T0kAPNkzvMVwJY@@P@-IixPie$7KLAa} za&g~^>)?Dj6HT75y6k`1^ptQ8@r9FM)J;*bQyffKKPAX%D!O#chn)j&2zk8{V`6M^ zsCke${98=ta&∓^xE9{<)~W))eFu8E)$gV;Zu?0EXn!u;lJ;cp0ZbcQka FJ<2g3?<&*P*e6mpU7WUgt1sQcasJ=s>U114rZ;S+$heD83xf6VkRHN$n zM(pK?@P6D11r2%^wch2>1e;gTG*cDiw_F6YoXX}CZlm4_BXY5r?M$D~V&idJISN*b zc*8^2A;x_+=CaxJMQxK{cEJhQr1}>W6>eZ-v^D LA7mzLoU+CLgi-A*g>H0xG z$W`-UCfye1y-8`HOZM8)(uBFRD&ZP7&JIDpfK&)MV}b_7!T3#LDhy54CwcV~;17v7 zkeo~uj!5%JLJC=9qJc_fmawI45 G>zICY8`M-!C7Zq$qx96LWNA(X?3^|kk7Rj(QfoBk z -qRE#1=ZXTw+3uYH;#gTk^T-6dnC~kn9q%fE_7 YELJf@v+*dg<)Njr?$oHOy>#7@RtIP*H zQ&~;L$MzEyA2SpvYo=nJNpK=nA2n2KF!zEWPruv_uhjCnn` LEtADsdi`D{YFqY(3 zN&F&ZMtYEcisfbK)-$!^$5H%&Cp nW6UHCD0aTUM9L+jJe%he zwyMXB)GHXMet~EY&cIZ;Q!seG1&rDJBoiV?gB_)~Ue?)AD6NMjTUr@|Zaz6bJ|EsI z9^zIEErAz?T9nfifgYj_oP^hZNW%j;v`W;2T#gQO S_J>jR75FQ)iq5yvqOX>l zLEF)PRO5IG?cVOk_MF+fz?)r|cYY7OR&)p>Hq4~uGlJmB8zVC68bKd#nN3LiMd-JX z!yR`{;q2CSyfNi7*!dKrLzOs9Jv ViB@dJ)cyr4Fucgv6z?PLM|C3 zaf_z+kebmioQ@z jE11__d zfMM ziKd+H4(5?*0f`oLr{`U&neav-R>u^A$bXN>`vMu5IlsN~jOssnLgp*+-=>IjE zxs%~qPB!G~* ZG^h+J(>hCia4$${EbAm?WCgomb*3HJZ5r@n`R$5Hrt+k~67 zXA*yPO9bATw1iG`3&o?X${oO+Lq8N&kyyuI8ZR)3NDg Sp0^lFZ?$40qfVS- z%Bj?GAzGPumG0d98s$WNNTq5sy>_M;x82=>=)De3erB~in*gXY&_T&PJ)Ct+5Qgl! z3r~(!5+kRL;BxXCxm_*+>6!9of^(*zqoN5B6
+u}xBUu4|3?P6#Kj)=DR{u }>M9iAI>4)T>q JCB EewoqmU-^mzo)#Wh!iZ;l &GDdW#@%G%bRG%pHilKuP=T$ z5Jf5u264~i3Gr_oy$D2lIXW3W x8;lEyUA75wuo|fK%>y(qvc(0^xR0@<^1oM^u0;@ejkph0B<8 z3oP;A>T(cG`G`*Mmx0TvDv0QOhf32exqfRMNWyMCe1Aa#$Hpe{RBlekRjuprOM@+U z^5kx!HMIyzUb4)fsCpvuxfWw)3Gz8RHi1Pv;r3`3Q^i0x S+DrAamHI45}|@(1Jx7bbm)7zAvhzUrY)y&~YBV8~6m~AcDgO7&znK zL1yerrnze`admH0 CvN7MF$M^pj=W&Yi1xNz@hpjjqG>#y3GvVj13i8b!v_ zXAt$nd@l5DWA7{}>>V-(65WEpocouGF&p>|(?oeic@c!GGgkG 0=lc~6jaw0fYiNgYOr4d-=7qQLAE~w@Rd|Heus-IM@T1;g&TUOh bODi z8|m`X#U-0cfq!iij7{DSH)b8f-iTcE<4xg)E`Lua?NKES)h#eE@tTqo+B7aA5Uu)8 zV@q5DX_#@3=1rSSeMKHJCJX;zR^=eq)@l(_zAMc5k$_(~hcTRU98a{TkyS}oV12Y1 z-Tcs)hJJ2iY_Gm$LPV#*>4`3QzgfgA JMXS4Sk+rUf|AN@d__dllNw_dZ%h?zt*q7w7B3Zv&tcf7sb9t|Grz>%~( z66`I8s`m~+#|#~4 =Lly4(v8|-VMwz!uq?tVb`vR7v=8r4&*G7Y&d*(wbnZwRL;#YC`T_L`vwK5zq zo KApP?sFIUy<;4k9W%kt zz5`VTuHZm*EB4N@!Hv4vcw99ZwtP4MTXzK0SzoK6rL+e1+~Uzma}qykQ4S-$p`7h{ z%_KAKE~n-@Pve5=H6*t0I|Tgr#}V0^M%JZY!S%0pQxCR3SzEUlHB$yiZK@YmYCcC9 z#t^=&@PvTp_i1T{4e^>K%<#YLL;IR|NaCF)FLT8C3(hA)zCaEQ78b^1Rh1l}l0_Jw zkOc+Hp5uM}Y+~=W5#27F_ 3*AeprziIrr zo3vI=l*G5Ffn=W!tS)~{m>mn@< UB?a{qlk%2mrZ7vB{XdZH=_fgxLrKmr@8xKC+ z1+sIC@M^ss&h}1% FKb zSi!xH`jWMDX_qtH`qvK+Zx(S*TEr4t*G95s(@fs2m;aHKnURpdc!AWjebnU{n^_6h z0!T6?+C^db!|V}wE;|dS?3zhIPBVmP_mRJKtTt@vPNY7jRf)Z{BWf#BX!Dv8=83s2 zoL*Q*O&``no6BmhTIwxYy6z;zC0>9W??4( =tT;PZjy}-tEa`k1*T2IX8=bHh)ayw>@Swbv~g|fIco?Q3%78LC};T0<)q7 z_^N@{=;9Q|-NI_F8|)TQqZ~ftypoR}Lw3W@M0NaW%6buxvsmva7v`rYgWa(spdKIu z&qC#4$TyWc?^y`WE1kqsxG@K~0|B_vpp4X;g`@sA9`}B)B<~MzIjogzz(sb8Fyc=N zYDeUPKE`2!E(3ci>OsOclL}rF C7 zKfQowTDF3roEO~tdH_2eeqgr!6sWUlhc=!9o%VV@7SBy)`%wE}k-i)h95cjU`-ONY zcm(2Jp2KsW;u5m4~@4rm{G^w;OqC0ys6xp6!DQ^0=N_{2zjWaH&TO>k`y? z#%jK>F1{SJ{6=BD*E4G0BhH_nl!ymEh|yR5Gw_^yC|E4LjsC%`2DUFA%{>C>2Zvik zlOF{q=X`;1l?%jRZxk8aV+v87wVX}^Wgd5GHimuwMO6+rk;iRea3MyUXRgdetB2WS z>yQCYZH_ypuE?SFYqY_qU>WH+cL|Mr?$K$XO8n)$dGJEx39a9nMAMGcV!d1w-0Tg- z^qKkGT><&TA~FTt#%##jB@f8Ews=Oa-VIWzFp;>t0DH%OlcegCSksh=M<(;};n!Gb zs^sB3QAPM^e+zrI-+&iRvFJ93ov)YILRA;XeuRy7YsvTd%tE+IuFz!W~ z@W&GLdb{aIjRf|ta|e1%+vxQZdJyX<&YkeSOr;*g<7 XFklx8iTug*)I6wRV*`U z2-b5JfyC-g`upeykgT``osu`Gl2#Oi2z`UVf;Hqt#4_q}@H`#gz=PF`%*gY7{ kMEioQ R{wLvN^90Nsupd8u!{2 zlj!Xs^r=E9PTdSR*Eoy3zVVL|*D4%K5`aRD*JOQA036PG1!p2dsRG+o?VPK~lZZ_M z!84Lr_UAsBYxa^yb~E{0hdEGN{E#zkaxigH=RjrLgG$90*60)&4VSIoVu0pR80;IP z2j -CQySJFt>6IdUPjzDlPc!F1{;6I!_BDG}b`MmCClrYrxOgZ?`+=z=&w>`HS08$n%2O-;c%{%*ht zWqh;T6;{>hVt(l**ezW|!`a?z=K47_ tWy`sB!ydOCKX2w7HIHYwPy2`9jmI^_DbW_r^37t4HvY$ z=?l(t){>Ui?RaeSdB}~B2H&TW H?hT# (CzVu8#RhbdUed5wjPb^f%@TnW%(pgO`A4|qBH#l_J!?&1b z+(S9he(>s00PNq}50}IZk@sRUU&FGJtd5l6cQbx4x2c9?@_*p+Lwh0SRufuZ%Eyd` zAnFrT$jy7oGFKm*fcgzC*lc?g9gC*nu5+ z0r)o4g)bAd3IzHJ=+k9)i0hBnSe~#GSHCAXdP;?yu(d}2#9}IImImF+@6mm{9{SsN z0(v&;qfS9T`SV;E{(0HK*@u3RSnCDv2Zos9JQuR6LxlBq-?6Mtd*sBsA#eW1s=y>r0 zG^THY4K0VU`#Lb9uk=uCgw51Rak$}YYG}vnN6>drmpN>g4>{kXaN*!GdS7%cxw>aR zq$}K{UE6ix83}`-$pJXXdMfM7-{2IH7Favw0NT=a^2b$}r}Bs8f9+ey8+v+$OnQG4 zd+m9U`jwKSfx1NJj56^4S<{>0BaDmH2`F?31Ld88xGnz@ntfqAEgxPWEvyHj%l)w8 z{Yt#9evI K`r{{d^OCS)EC}tB<>-#oOEArQ zI_PO-bDm%N$4yE)N4AzVQGtP8^m&xYY`5sa?{gf%vp|_H(Hq7Mr{`c%us#^KN230d z=~VWC41a@BGZ~ouj4LIWM)HXzdA_NczB^?K3meNo|4SKuQDZsE+Tm2TF_N<-`UX6+ zPoV}ooU5`ra$%9=X5hR(LFy&{!h!9RS^kR~R>TF 5~y#I9$kf zqifl&=PxVzOfCr8HfkZKDHQ)rs4_=n*I}sJTF}o_B-^^G$eU3Skl7xMQ-%b17K8Hm zL(q+StK21CmOLo*+)9KRnutg9J_xt U}|FMZUv1`jOXOTKTjpyiJ$s6p5~gvlv*UoHfX^m-%jP8>I` z$B)# H<>-CT;ZAbcE?*UDJ9ySaw;q7#LPMkzHn!OY% zMw7q>ka%H*{64_XN|%WGun_Iu=mV1j;>nNlF0cu4K-= %f3(=zTpuWiDLEO z2Ytjn{w@91IS9n3jHnE8NS03(Tz~Be!_ETy)pG|yZpI6oGB^(^H%>-#11XTd*9uGA zJjp`23#9YLVf^^`Es^fJfL*u;);si5>4lO!tHQ--yuyo$4BcVU%%f2AcN?`fOd!4Q zL#jGwe Y;1s5xGEFB z0zFt#aG$Pj8Y92tOt^8$Y_>vb306%Pg0LDt91EMy3sh()pF~9IzJJ~H;_42tTHydq zC0CJST@7k?|I*&3a^x3Xf 4#$_$Y@OA{ BofxGW4NEm%rU%BEt!PYXAC zt0l?Wbd+dzdxF5TK5m@vepqk|$TEo#5ZgtV+1LVG%)P;B$zlkaBg6AZ&_#!^EUe2- z!UZbj_#|`|uHd!Ml^;HmI~h+&K)Wgim7Rnf4^>{2cMQa9w=lm|8@Lg6_sL`Fb{L)A zL9dK{f+u$$(fPmn>6Ch5IA&=DLDw5_s?P*0F71Qu=z`*fmKgZf5LW!l<#hgChPGbY zXhQT{kWs#c%eAC&sB9~gA2%eD>FwNSPZK~i*%h4wfN 7cevg9KeUi93wA w=oLHhPITIkiSNNw{?~iBT;sbdAI!tJXgPpjeAN-$F^YFc62T*zSm8oKOSNP zhFPBNp&R&7r-4owT}QJ!dRRGjnAn#TgI9|nUnwLIcf3CV3ulBg6Mg5o$ILjGw9ty^ zWG=%>qeY}>(Pg~TUrntJWKjQgZ6qQgpUe{Mg^7VS_-d8O{M1jTF`qNZc^0bc |b_M~%|2nF^SEcq^4+ (wgtmM0iU++`0FXZrS9I zf&QoQn*3B+K6NdKs)R#&`x`Lf#ls)d>Cn#`VFJ$<(DwV;jC`>xBpQ3+-!J0uG{Fk9 z&c;G%;~P4E;yY sSw3t}_}7gTMzodiR-c43fw7FHY-pcNxGAlZE$yo($l zJ+B1OLufCyYL#*Yrl>=WQa=vYeWptUCx|K66$iyuQups)NT&Ky)S7XPWAU2{jcX_K z4W$P0!a^JTBU6N2!%L`N^P7arzr>Ba5UlFRqe9gesb57k4ba+4d502l$d}E&eYL}) zgi-qOKL^OFwS(3kS(y8$i)wI|q1qh Gb${RMea ztH~RG$etS)Ph!CX4(PmKK&W*VbOhy+j2+XkcK 6Te_P~900)+--S=TQMX`M@7mS{Y%sTQrH2QG&OJ(#if);pA@OQTQa1g3mukGI}E? zxbLMHxO5^O6V+LUjLLG@UmHf|nQK$o-3d^`_S}wlra;2w-H_3+kn~w-R;eiL!IV=C zq~x;{I*4b$+_{20N_wf&I&<_s`5qS~B{ARZM9@y}JoaU~z>b~%c&@<=lr^tH^Ivmx zsAysDAenI6?>zPGbjB8`*VILo&3JJ?U Cl=UcAcgi`dO?V@5o(=;h6+FxO2Lc1!S?Im>Fv`zyH+rrJsM-+aU4bUt4;xszVI zaR8R(za{crmUzN>1(k@n2tN-evvZy#&sd?2taUKN;Jim%bPI+H&9R{K<{BwjJ`>dr zKZik+L!8a2IVhI14hy!er|%DMAUg9k>A*C$zZ>X*joXLFo=PiJa^Rs;&p*;9x*P0< zBj|Mf4LB?r&P`>z&F8$Q@zYjCfXJS1n4^`CwU1t-zp4ZzvA*l{_F2TB?*M983h}0t z1XcdsG@ti=a~HULj3wqK7eRS~z4MN{0wPU@#yI?Gwr|cvuAQi5HSXnGS4@x(e@| zw;{5VGHDg>8`M &;YLJ7LYYXxj53G%~Q&e zrxp66^rnXwwa;aUKHJwH%PS@yt_?EMwn8{~ZZnvL&E;* PUp1L%waFJG?&YGxrX!%%{27G}lyE`sa}=7IhvSVl_{vp)Bw738 zqQC &F@ z$Ag{RQ@*)aS#X7#Jl7=hHad7@CJ# I=TDj11xR32`VQzWbJ`* zRNSgW+UmVA*4Y A_eG_8=okDp;f_izGK}om95UrhEiRt@7{bLK zg6qa0cqy-qjYX@mZTbi)R?}whc(eFRQm$jwrD9mB^q6d38c*>(%XX45z=dqqOzv_7 z#tr+xE8b6(zAQx4@14cvCjd^2+7h(hg%fRY^pbBoH)5U=?wr?&k2OLd{_hr|m*Yc{ zjXr_(vRYg#P)Utbt5HmJHT1I@ 3@v{^!K#Kkg;`VzsYQ2)+ zXRL{V^(#E!4BK_%j|kwcsA?P+o< T(ICuX%DgN>_Z_Gda42D>5N^syB zRlTB(mdkfj?~yz@$w8mh(xmV_7Q(!nm9%iqT_(PKKFj8dr!RzCxVK}^LS2Ug5=TN0 zq*!3`FQgy)He-c@JzhAniSA}DVs`sfBDgh+meg;A+m>7^-Fpd>lBY0>OsZkt bAjI94XWr1xEc`sqOtb7{ zQpGE<==&rLy>=S@dD=kU$ZcxgIfz1Rmd4MO&2mE$C~j0@&$LF`kb8wndiqrPwHVOx zKPI>%N(9|D^2y~9ML1I8ME*7~aO}_#D4n=X_wfVR-9=$2bW@t2TVxO0&rKnZWs|7D z=MZYIbchaI(dS9mSuq;=N8mv1561F@T-8KZ3tAURVM( tg#Gb zx=gV;vjmT(+#wNpxk#M~$dk#|WXYfT^i=jOdV2a<_*b$A?2ik9&t^BA&SoX+8}%@# z=`jhch^Ha)d=h*=66K`$ER$y@s3duzdt@NPskuchcCcq J%1kP(O zsM_86oXnD%OO8AXrC !YxYE-W+SH4P6C)vfA?mWB9Y=o!B2@1!kuvOHyZ0UkLe#uxdonA|e) z!q53J)S`VD^rK_B%DdK>`tOvXVM=?z(jgQZEm(g?ZWg(AmSBhjgWC6G!R}rR_O^{+ zddg|Q`Tm%2LY^<^HjLe>&p>G16uiV#k&mg%=up~HT- mu5E!d(FP17512XES}9q zhSCvso(h>HN9;4wsfF2HIF@u462_LH@WDbd?CcKSo<>-+F$e!FI1fInUSVOGGThbu z3}=turgmMEP)GI%>D+J_gk0v5>G!VDz$i`f^nx?Y{L8Y3&Q~) 8LTc~|SnE%!28$NiY!t+)3qwU*5>G9!3IKXO;!ZU~9-9mMU zDUBu1eLbL`%?JHk-AX>Q8gCM z6ISjyz@3+0f_E28!&^rp(TCiEOY7U|Wv_0U*bvNWfaY*eHV;Ikn99P9t+ UkQh0G9Xss2XK3;!S|*Q{xa) _N4XFk z;RapGK9GF-K1VR*K9s~~Fu@*sklS||Paob3eoN!X9!YmpWC~&K=n{+@OTk=;KP(Sz zI&ZC6DfzangPG7$;je2*$3WUajbe`yg@mK@zs|)h?=G4ouQ11s{PHT5abxb+Tx(|U z3OzXSRTItST`{g-4z#TU(J@X6?klImQ@j1-_03KcAHPETi+94e%2;YvWr9OqLuBHU zGfpkufWh4lsNXjg>KwBc3YVRuf$k%;ke#zb?X95w?<4MzlsRIfCz#!h!fTt{$e+9_ zW{HbD%l8RHhYm|37|{R?U#*Z)G9l+r%0k2PY+P_di8;3|0Xr6nqT@GL&{8mhqg}3W z+Gh!TI&6(8mvzCnGnT&GpiOS)02O&)2v(kgWXB~>Dqr%1>P?@4+XGleV6`(m^6?>J z^Nx^%6RPx1dmfDcNGFlA^of3WBWitf$6S9?bUhRfS*d!kcE)rvAQ?=Tthx#>`kF{g zK|IO+$8t$WGjQc?M{s}n1?N0IL8DH5hLiOyPr7$4>QP5@8I^?B>M TVbO$OE9 &tPgKNrjDC(ve=J)1F}&(~a}&34|N!Aq4Hcu8Cu zpM@qtxTGnr{TWCXHkyG&u{O3g{zq~y2;$P~*O~BEPvWxd4$D)UN3Rxb#_cPDp>ff2 zD$Et)Ddd;XQ4udticy4RhfE L>iAV-%Q&Hpl#AWp*a&1a5mA`CA(^NB|F83o^ zuQKND8kmG{Q(l6L?pbuU*W|efN06 ZCIVaQu zO?Q8w>hc-*<4`#xUiT0#OsK< r>Pv9nk&}49W;YzzC`uA@D~as)oiOv{X>en?D?1r?SmZX!%~<;qBbUy@ zrF|tRbK^ajc>vXE|Bpnkp9ExZf_tRa64jk|W2)OT;G6p4N|AfmrWwvM$AVDi{BN?4 z&2)O2R+G!e51~5Sw|$q#MeCn}{HPHE-c!A)O7&~xu6c+RiR`XG!%lpFYM|o0il`18 zhPhp*!RZqZMQw{9+$4hK|LI{s<`gFRQznLQI0qbYQQkGbo#f6^ZLXQtJEo~E2_~m) zfDQE*;PGiA{*s7J61+r_$6b5~?LA(=VuK;#l6MHF35sEnt|1+YdjUnhWtESdGcc@8 zhjhDd0I73Pq_S`?kOzOkXl4jn##rM+%j+~dlQM66AJQ9leqf`?UX-)H1b45L!Va?} zde!A62BhqVIW18reUD|V@ZZy{VG&R`m{0ruc4LIdT+FSp1IM^t@-OfwcjlJWET<)m z?(2I3J6u_o<>*GJlV|rW{@M)M_r!6X2)o~7Cy$yrw&9=jS#WCkCA8h%LOT51$nht! zI1p9?jh6i=FqRBO?}7GB4~K|R8PfmqG!}E}@P6Mdtc-e&Dd7Wn`p+|tU(gDu*xJhc z_lwo+y_Qg}lONtUOXLcz-9prZRQQ)4s)N9F6= (5XTi(kob>Y!f`LYbD?0YiWhNGbwl4fIEH!la(Ge=<%zB z?wa|UX6e2J9d0s+9k|UzEe!@CJyEoA6GDxF*Cf&D6YRH-#oojenttC7>Izqb$A3G? zY$Hb|b(jMhEBK%{)e-I(?`EFum_XSJ{?NSI$*fU5jr(3S3~v{nLz{b@AY3g04>zWe zsmG$>mys?+JPac?x{lEK`T|jQsi&uA+Ea1%S<#HCr3ZvqR_lB%p6`iJ#-llxIrKmf z6r(&*)L4p!iRD7<-cShC^?@m&@>sGkl*qji!mz^sShmexSSoJ8)t2Hx#HtsVC8Egi zAA2*2ixxuI`7Y{oU4j3(YY?tiykYrT7pSP@C*stS36s)&xL#lE@p7^*jbL|RT0T69 zmjZ=J^SwxvE)b?K?*_o-u>us|a0LXWOYx;{#o%rE3e+<`OxLmcfDF46!@kZK&+{#* z+JZR7$;J{*^@7Qn)GOdJjlIWy&V-dGjp=FWpE$E3n)tm~4R*%C^z*{2Br0|dO6RIV zWQZ-cIC?Sbtl2#kre08#D8{dEPQ$_6ez5$s8JgVxV!*CeQrEkcn>l)flxXGC?irEz zZl5=S+h6H3iCe6mGX?=gYoI8`pz73AY1+JR46`E*F|BexdOQ*banqlep=e05*$zsk z?=Bcz6AOOJdN`$ihzcuAsA|u1y!o9^%o_f~H(SO@Ql~Vgexqcuq8`hMtHAs%Gf+lF z2ZyD; YfgG-sGT`7jyo#|*-aEyIk(hK2AW`vg;EBS)<- zW#PVKBHXx3_u%5x**NRxc-8Et$DqHP?F-rpGXralq3^CRbjAFn%{vO96nSKK3!Cq| zBT5U;lyT)R_E5)CM^f@Q0Eq|Nr?^jWEao_7ye+OgQ?v>CQf* H?yIZcDKM}cP^U!g6B76?kj%Cv$zYAajf>6K9m#dKO zO!v<}jWg;(a8-jLsIh+H{71j4Y>5pduiHuM#H(Rvh%zWf=hN1dStL|0iZ1=S6;_*B zz)j;NV0o{Hd_6Ope%jH&c3C`_Y0aPLsT}sX5m`m-IK8-kPBk2B9-+7WFT%M6lDwG@ zO32{UK4PhA4DI4S@xx}8XY5*t8!KXAJDcHflHZ22+jo*u-?PM4X)Ae~{)M=$$sz$; z?=uAoX4JqSpV(yTVQ?~!yY09nXl(t5D&2?Bxi5ygh ?Z`#h zSG3Vig~~OF^1)Gq?Xsmp&BL~;l}9A-x85E4L~9kUQyZt+PmRDPZ2)FmQ|0FueW9wW zyNQY3O6ZkSqFZ+PlL;jWkiC46t7RNZ)+nf>)Vvx9Upxcv8T6x6Y9jnDEP@m-NtTc5 zg3k}O;`qop#(`r;qqbckCI z4ee =)G3R8Z&1VqXSYE0a0juO<4qL{jqrG` z41c4y6kKJt!$FT+)c(>)`Gx@B2J5Nq763_&_cYij92(2!^7fSLf^&E+bQM@pNy}n< ztJ+QO-aW*O8GM30QNHj_GzoWS3!% $i^)weX@Zp-JDAzukIr{ zQ a??lki3y zoU{N|Nh q!*)q^?zinS+ I^YEFicLFp04F^8lP0qDs=^VX;us gz>$YEePEv=$bMLE@qjeLU9U~9T4O%oVE& HDWt^Yaj?%|)7lb}Kfca<$vDMp8H=YyX-OTJF zizgyME$IaeKmLz-b&w${X#u3+dmQBKcEf>Vi_l7GE*=&zXD7)f=Bbh*e@~qZ@7I|e z_Ib0RuKw}Fa7{AKDda%L@%_*`RRcud9HjpLRaV80>+`k4T+mc@5#}&rWI`qhXBORp z?<*(qPg=1#MbC$1!fPQmPQJzX=0|d`^`2+_0WMS>Wm)NKy6I(~BJB2AiPY>mo$lL= z^KuShrW;c4b=xrK5`m5tOXz>2UgrGJje`8x $VDQ}}9Imtl=k|J7nka!^H=n`-UlwE3mMl7B z@;bC$`5O$cD6)IcXJbP38rqgB&fj)?60t6+CY+0X5Egn6j;k%gEMYSk`X$4=tni+z z)UlL*hRb$nT1(+ThAoI@6Obsa#ifnsv2%6=H$cA%dz@cmzd$_qQ^Z^vS@{vy-;*N` z612$QZez$g@d2MjmBEJiA=)IU1~E%xIl(rav|02H{lOXuS#b&Aa6^EuvQLKfN4+8U zQ7SWhvJ$_Z8%MhhPq-iE7Si<9sW2yP5BE^{AxO_Y3cnBP!%f4h5Ik%TWFQa4Le-GJ zeG2dUs|s+}^~b$RrHm`P=SVxW7wdVW9Iw?C b$gqCGW1mk` 1FpY@cL+9(_YDbI-%1Eo-1}Q8s$^3gXS+F1q4QA9+?gj(XgE>+OX_ zaNY4f))sC9sfo>K>?;nj_OZBM;~rV|fO|hjx o>>q7As0f`93UN6z$b;}(cujLMcXrK#ZfP}` z{#Og1`)x&mc{efsT{5X?5rKoclR^F8TaI@w&AU9s6&K9w$Hil3Xu$ zy$i6CAH~tq@hvEt< b s _O^U zy!Z15dAo6tL!zvM3ekG(>swB1c{Pmo wu@aF`=cOlez(tuyog<#%)>ilsPKS~qM(xu0ApyuH=a^{hj@e;jivP>xs2Nx{G z>iBkC^Ti3AWD4noJz21*?k!R9nvbo YQG(fj$&EUGr zqP#6OHqhJuk$jR+B9GD+qutmA$a5Bh@rEYQcG84%=RDz$x)VezTH=}tC-P}u7^CU- zoxF1p<7H}Zz|9pw`1pPRemM3M>Rxw(_8tYy_l`6c^G|@BlVLa_84D6R1efmhB1w(3 zI74+Vrtj|~PbTDZ% CbbtJXx`fxLRu`6P-*4ll%?0!nnp88Mi2L>$-7PQ=ys zx58%S6xzI~1PAJBV4vp?vgNopl*p*zo&y0Sc&!D@P3BzbGj5>9mOP^5naQ2UWg%xD z($rOn Ys^NCY Nsp=jp~~eb5$Jgd>M# zU|O^voxVd7cP-XKuM{P^I=ukg?u R9>~1=tjK3s|%*#Xa6@;L-7!1Ro^AsA&BX^Xn}Q| zv!F+9l&sV8g9@gUc+DP#AO6cQL}WQku~vd;`#@A4AE(OquVKL}e;C}Q$ZI@1M66^y zNPA$H@#;sBShI!e?ccIx9iGgBqe(+h*0B- MKEb<&zof%g z4ypAFa&yQF0xS&BvX$e2T3b^~8DZ9LS^)OXvgN-!RZE&v 9W9`T z3@hou+5$9Gjw1U#JaKP5iwhi&LHaolh#c(#k0pc+&(VR2hmH}SWuNIe3xuZkOTnW3 z44OtFv&PI44qGat=h9jb?#sdltMuXhf?KqwauLetQ5;(AgIXq1%wBspbX|Vkm_8Y$ z7196bCKCcqigO{WQ3D@@OYxSg#6r7DDGt^4ax=MWAi~W~D{G&E=7u=ByVC&!4zDDw z$+nC}k{4AHPQ^2&_er zTwid^tL*O}X{OKr{BSEQli_CMH`n9z_6 Pv3Edp!rI544x z=#*r``sm2;4Ab*)>6H)Uw)u8=*vn@eCV6Ac%EeH;GMBOr=A>{S6P)hb!&8&hu fR}H3wMS-b!b+N8>)NZ8)ei3Eq sG|TMvt(!#VC%5$4b4K}7ExxSekMmiu^p*4hOqC==c4q Woxi8M`O z1D;#%MXhZEP&rVV^G3XZ4Tm_dcMo^R=@3WbsS~Kek0>1HGR51Mt)S!Urm?hF6{VyG zO0 j8vDmLD!$tU7=9N%%{LHaaWi1(=UHh5(dxHtCEs4hVQMwdt6FYk1GzV9n0 zciHi5xZnK3g%}d$;)iZ~ACUbQo5A~~IX$FOM7`__N$p|-@C@+b_`EUj{A?NfM>`VE z+G%3AX&Oy6sD-9SllgPj^J!{;H*Pb_Wv)(*gPvw_e%)gh=0+AUclL&}m->a_ihwRy zm`GvtauL4GgGFHb^eLSe vhj_nKV~YZA{$nRu2=2hnteOkeq4&@LifTyyBlyI z^)6;@z60yidpV|-GTtugBmw$nB)(A|+eaN}x@j&7=NtkBLrI=qWEFiWkVW_9>;da0 zaU;LeWngFKhRt$K)Tb|%S`1~73j+ PoU-GGt`7W| _~4RYaS$0C36Yh3J+f}*z*kA<~K4IT7>wGgLz;- zr _#^A)#Iss0EkuS -lM99r@LUWYx?dxQwuY0@kX)KR$A!zR`hkPX2n=qV z0cXUGKsuw9p3c`q#nveNw&xX0i1laNrpFPPX||vkEWz7cvWV=QbcO5w-9*Wz4B}Qc zi?_sun}d$!z}E#WO!kbEc%|b827MF;&t8T^9J_;uHi`3uvya08KA+unE(=2MZ-nRH zGof}yAvzw=h3Rg|B&U^P>7pt4Zh
=$B4|`~5qZ;JV^9nCckiyk zvHLryhW|x0x>`(v>