diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..4f810c24 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: CI + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +on: + push: + branches: [dev, main] + pull_request: + branches: [dev, main] + +jobs: + build-ts: + name: Build & Test (TypeScript) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: pnpm/action-setup@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build SDK + run: pnpm exec turbo run build --filter=@mysten-incubation/memwal + + - name: Typecheck SDK + run: pnpm exec turbo run typecheck --filter=@mysten-incubation/memwal + + build-rust: + name: Build (Rust Server) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: services/server + + - name: Check + working-directory: services/server + run: cargo check + + - name: Clippy + working-directory: services/server + run: cargo clippy -- -D warnings diff --git a/.github/workflows/release-oc-memwal.yml b/.github/workflows/release-oc-memwal.yml index ead6e1ce..ae7d647c 100644 --- a/.github/workflows/release-oc-memwal.yml +++ b/.github/workflows/release-oc-memwal.yml @@ -36,9 +36,6 @@ jobs: cache: pnpm registry-url: 'https://registry.npmjs.org' - - name: Upgrade npm for OIDC Trusted Publishing - run: npm install -g npm@latest - - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/release-sdk.yml b/.github/workflows/release-sdk.yml index ab452645..f739550c 100644 --- a/.github/workflows/release-sdk.yml +++ b/.github/workflows/release-sdk.yml @@ -36,9 +36,6 @@ jobs: cache: pnpm registry-url: 'https://registry.npmjs.org' - - name: Upgrade npm for OIDC Trusted Publishing - run: npm install -g npm@latest - - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.gitignore b/.gitignore index 2a06430a..d25a89e8 100644 --- a/.gitignore +++ b/.gitignore @@ -77,6 +77,9 @@ bun.lock # Keep package-lock.json for npm !package-lock.json +# Turborepo +.turbo/ + # Rust build artifacts target/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..57aec05f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,270 @@ +# Changelog + +All notable changes to the MemWal project will be documented in this file. + +--- + +## [optimization/v1] — 2026-04-28 + +Performance, scalability, and developer experience improvements across the entire stack. +No breaking changes to existing API endpoints or SDK public interface. + +--- + +### Rust Server (`services/server`) + +#### 1. Database — IVFFlat Index (migration 004) + +**File:** `migrations/004_add_ivfflat_index.sql`, `src/db.rs` + +Added an IVFFlat index on `vector_entries.embedding` using `vector_cosine_ops` with `lists = 100`. +IVFFlat is better suited for write-heavy workloads compared to the existing HNSW index. +Both indexes coexist — the PostgreSQL query planner selects the optimal one per query. + +The migration is idempotent (`CREATE INDEX IF NOT EXISTS`) and safely rollbackable: +```sql +DROP INDEX IF EXISTS idx_vector_entries_embedding_ivfflat; +``` + +#### 2. Database — Connection Pool Increase + +**File:** `src/db.rs` + +Increased `PgPoolOptions::max_connections` from 10 to 20. This allows the server to handle +more concurrent requests without connection starvation, especially during batch operations +and parallel Walrus uploads. + +#### 3. API — Batch Remember Endpoint + +**File:** `src/routes.rs`, `src/types.rs`, `src/main.rs` + +New endpoint: `POST /api/remember/batch` + +Accepts an array of up to 100 items, each with `text` and optional `namespace`. +All items are processed concurrently (embed + SEAL encrypt + Walrus upload), then +all resulting vectors are inserted into the database in a single PostgreSQL transaction. + +Request: +```json +{ + "items": [ + { "text": "I'm allergic to peanuts", "namespace": "default" }, + { "text": "I live in Hanoi" } + ] +} +``` + +Response: +```json +{ + "results": [ + { "id": "...", "blob_id": "...", "owner": "0x...", "namespace": "default" }, + { "id": "...", "blob_id": "...", "owner": "0x...", "namespace": "default" } + ], + "total": 2 +} +``` + +Validation rules: +- Items array must not be empty +- Maximum 100 items per batch +- Each item must have non-empty text +- Storage quota checked before processing + +#### 4. Caching — Redis-Backed Vector Search Cache + +**File:** `src/db.rs` + +New method: `VectorDb::search_cached()` + +Caches vector search results in Redis with a 60-second TTL. This avoids repeated +expensive pgvector cosine distance computations for identical queries within a short window. + +- **Cache key format:** `search:{owner}:{namespace}:{first 16 chars of SHA-256(vector bytes)}` +- **Applied to:** `/api/recall` and `/api/recall/manual` endpoints +- **Fallback:** If Redis is unavailable or returns an error, falls back silently to direct DB query +- **Invalidation:** Cache entries expire naturally after 60 seconds + +#### 5. Sidecar — Batch Embedding Endpoint + +**File:** `scripts/sidecar-server.ts` (sidecar), `src/routes.rs` (Rust) + +New sidecar route: `POST /embed-batch` + +Accepts an array of texts, calls the OpenAI-compatible embedding API in parallel using +`Promise.all`, and returns an array of embedding vectors with their original indices. + +On the Rust side, `generate_embeddings_batch()` calls this endpoint and falls back to +sequential single-text embedding if the sidecar endpoint is unavailable or returns an error. + +#### 6. Walrus — Batch Upload Function + +**File:** `src/walrus.rs` + +New function: `upload_batch()` + +Accepts a `Vec` and uploads all blobs to Walrus in parallel using +`futures::future::join_all`. Each item uses a different signing key from the pool +to avoid per-signer serialization locks. Returns `Vec>` +in the same order as input. + +#### 7. Infrastructure — Graceful Shutdown + +**File:** `src/main.rs` + +Enhanced the shutdown handler to respond to both `Ctrl+C` (SIGINT) and `SIGTERM` signals. +On shutdown: +1. Stops accepting new HTTP connections (axum graceful shutdown) +2. Closes the PostgreSQL connection pool (`db.close()`) +3. Kills the TypeScript sidecar child process +4. Logs completion + +On non-Unix platforms (Windows), only Ctrl+C is handled — SIGTERM is not available. + +--- + +### TypeScript SDK (`packages/sdk`) + +#### 8. Retry Logic with Exponential Backoff + +**File:** `src/memwal.ts` + +New private method: `signedRequestWithRetry()` + +Automatically retries failed requests up to 3 times with exponential backoff (1s, 2s, 4s) +when encountering: +- HTTP 429 (rate limit) +- Network errors: `ECONNREFUSED`, `ECONNRESET`, `fetch failed` + +Applied to **read-only** (idempotent) operations only: +- `recall()` — vector search + decrypt +- `recallManual()` — vector search only +- `embed()` — text embedding + +**Not applied** to write operations (`remember()`, `analyze()`, `restore()`, `rememberManual()`) +to prevent duplicate side effects. + +#### 9. Batch Remember Method + +**File:** `src/memwal.ts`, `src/types.ts`, `src/index.ts` + +New public method: `MemWal.rememberBatch(items: RememberBatchItem[])` + +Corresponds to the server's `POST /api/remember/batch` endpoint. Accepts an array of +`{ text, namespace? }` objects and sends them in a single signed HTTP request. + +```typescript +const result = await memwal.rememberBatch([ + { text: "I'm allergic to peanuts" }, + { text: "I live in Hanoi", namespace: "personal" }, +]); +console.log(result.total); // 2 +``` + +New exported types: `RememberBatchItem`, `RememberBatchResult` + +#### 10. HTTP Connection Reuse + +**File:** `src/client.ts` (new), `src/memwal.ts`, `src/index.ts` + +New module: `client.ts` with `HttpClient` interface and `createHttpClient()` factory. + +In Node.js environments, connections are reused via the runtime's built-in keep-alive +(Node 19+ enables this by default). In browser environments, native `fetch` already +handles connection pooling. + +The `MemWal` constructor now accepts an optional `httpClient` parameter for custom +HTTP client injection (e.g., for testing or proxy configurations). When omitted, +a default client is created automatically. Fully backwards compatible. + +New exports: `createHttpClient`, `HttpClient` + +--- + +### Monorepo & CI/CD + +#### 11. Turborepo Build Caching + +**File:** `turbo.json`, `package.json` + +Added Turborepo configuration with a dependency-aware build pipeline: +- `build` depends on `^build` (topological order — SDK builds before apps) +- `test` depends on `build` (ensures built artifacts exist) +- `dev` tasks are uncached and persistent + +New root scripts: `pnpm build`, `pnpm test`, `pnpm typecheck` + +`.turbo/` added to `.gitignore`. + +#### 12. TypeScript Project References + +**File:** `packages/sdk/tsconfig.json` + +Added `"composite": true` to enable TypeScript project references. This allows +incremental builds and lets downstream packages reference the SDK via `tsconfig` +`references` instead of relying solely on `pnpm` workspace resolution. + +#### 13. GitHub Actions CI Workflow + +**File:** `.github/workflows/ci.yml` + +New CI pipeline triggered on push/PR to `dev` and `main`: + +**TypeScript job:** +- pnpm store caching (via `actions/setup-node` cache) +- `pnpm install --frozen-lockfile` +- `pnpm exec turbo run build --filter=@mysten-incubation/memwal` (scoped SDK build) +- `pnpm exec turbo run typecheck --filter=@mysten-incubation/memwal` + +**Rust job:** +- Cargo target caching (via `Swatinem/rust-cache`) +- `cargo check` +- `cargo clippy -- -D warnings` + +Actions runtime set to Node.js 24 (`FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true`) +to avoid deprecation warnings. Project uses Node.js 22 LTS. + +#### 14. Chatbot — @types/react Upgrade + +**File:** `apps/chatbot/package.json` + +Upgraded `@types/react` from `^18` to `^19.2.14` and `@types/react-dom` from `^18` to `^19.2.3`. +The chatbot app uses React 19.0.1 (runtime) with Next.js 16, but the type definitions were +still on React 18 which lacks `useActionState`. This caused a build failure: +``` +Module '"react"' has no exported member 'useActionState'. +``` + +#### 15. CI — Remove `npm install -g npm@latest` from Release Workflows + +**Files:** `.github/workflows/release-sdk.yml`, `.github/workflows/release-oc-memwal.yml` + +Removed the `npm install -g npm@latest` step from both release workflows. This step was +corrupting npm's own installation mid-upgrade — npm deletes its dependency `promise-retry` +before the rebuild phase finishes, causing `MODULE_NOT_FOUND` errors. + +Node 22's bundled npm (10.x) already supports `--provenance` and OIDC trusted publishing +natively, so the self-upgrade was unnecessary. + +#### 16. Build Script — Turbo Filter for SDK + +**File:** `package.json` + +Changed `build:sdk` from `pnpm --filter @mysten-incubation/memwal build` to +`turbo run build --filter=@mysten-incubation/memwal`. The pnpm `--filter` flag does not +prevent turbo from resolving the full workspace dependency graph via `^build`. +Turbo's own `--filter=` properly scopes execution to the target package only. + +Verified: `Tasks: 1 successful, 1 total` (previously 6 total). + +--- + +### Backwards Compatibility + +All changes are **additive only**: +- No existing API endpoints were modified (same request/response format) +- No existing SDK public methods were changed +- New endpoints: `POST /api/remember/batch`, sidecar `POST /embed-batch` +- New SDK method: `rememberBatch()` +- New optional config: `httpClient` +- All existing tests remain unaffected diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..2b8cda6c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +This optimization branch (`optimization/v1`) was developed by **Olympusxvn** with **Claude Code** (Claude Opus 4.6). + +## Contributors + +### Olympusxvn +- **Role:** Project lead, architect, code reviewer +- **GitHub:** [@Olympusxvn](https://github.com/Olympusxvn) +- **Responsibilities:** + - Defined the optimization roadmap and requirements (`memwal_claude_code_prompt.txt`) + - Reviewed all code changes and CI/CD pipeline outputs + - Identified build failures and provided error logs for debugging + - Made final decisions on architecture and implementation approach + - Managed the fork, branch strategy, and PR workflow + +### Claude Code (Claude Opus 4.6) +- **Role:** Implementation engineer, debugger +- **Tool:** [Claude Code](https://claude.com/claude-code) by Anthropic +- **Responsibilities:** + - Implemented all code changes across Rust server, TypeScript SDK, and monorepo tooling + - Debugged CI/CD failures iteratively (8 issues resolved across 11 commits) + - Wrote CHANGELOG, lessons learned, and documentation + - All commits co-authored: `Co-Authored-By: Claude Opus 4.6 ` + +## Workflow + +1. **Olympusxvn** wrote the optimization prompt with detailed step-by-step requirements +2. **Claude Code** implemented each step, committing incrementally +3. **CI/CD** ran on each push — failures were reported back by Olympusxvn +4. **Claude Code** diagnosed and fixed each failure (Rust borrow checker, clippy, Node.js deprecation, turbo scoping, React types mismatch, PATH issues) +5. **Olympusxvn** reviewed the final PR and documentation + +## Branch + +- **Branch:** `optimization/v1` +- **Base:** `dev` +- **PR:** [#1](https://github.com/Olympusxvn/MemWal/pull/1) + +## Commits + +| Commit | Description | +|--------|-------------| +| `18d886a` | feat: optimize server, SDK, and monorepo for performance and scalability | +| `4e3d7fc` | fix(ci): opt into Node.js 24 for actions, bump runtime to Node 22 LTS | +| `5d4515b` | fix: clone Arc\ before move into router | +| `ccb1759` | fix: resolve all clippy warnings and dead-code errors | +| `c04abba` | docs: rewrite CHANGELOG with detailed descriptions and examples | +| `c772261` | fix(ci): remove explicit pnpm version, use packageManager from package.json | +| `10dd503` | fix(ci): scope TS build to SDK only | +| `d119d9f` | fix(ci): use turbo --filter to scope build to SDK only | +| `2135380` | fix(chatbot): upgrade @types/react to v19 to match react 19.0.1 | +| `630c35d` | docs: add lessons.md | +| `9bbaecb` | fix(ci): use pnpm exec turbo — turbo not in PATH in CI | +| `f7c7e9a` | docs: update CHANGELOG and lessons with latest fixes | diff --git a/apps/chatbot/package.json b/apps/chatbot/package.json index 4ac61c67..5419d91d 100644 --- a/apps/chatbot/package.json +++ b/apps/chatbot/package.json @@ -22,13 +22,13 @@ "@ai-sdk/openai": "^3.0.41", "@ai-sdk/provider": "^3.0.3", "@ai-sdk/react": "3.0.39", - "@mysten-incubation/memwal": "workspace:*", "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-python": "^6.1.6", "@codemirror/state": "^6.5.0", "@codemirror/theme-one-dark": "^6.1.2", "@codemirror/view": "^6.35.3", "@icons-pack/react-simple-icons": "^13.7.0", + "@mysten-incubation/memwal": "workspace:*", "@opentelemetry/api": "^1.9.0", "@opentelemetry/api-logs": "^0.200.0", "@radix-ui/react-collapsible": "^1.1.12", @@ -62,9 +62,11 @@ "dotenv": "^16.4.5", "drizzle-orm": "^0.34.0", "embla-carousel-react": "^8.6.0", + "esbuild": "~0.27.4", "fast-deep-equal": "^3.1.3", "framer-motion": "^11.3.19", "geist": "^1.3.1", + "get-tsconfig": "^4.7.5", "katex": "^0.16.25", "lucide-react": "^0.446.0", "motion": "^12.23.26", @@ -100,9 +102,7 @@ "tailwindcss-animate": "^1.0.7", "use-stick-to-bottom": "^1.1.1", "usehooks-ts": "^3.1.0", - "zod": "^3.25.76", - "esbuild": "~0.27.4", - "get-tsconfig": "^4.7.5" + "zod": "^3.25.76" }, "devDependencies": { "@biomejs/biome": "2.3.11", @@ -113,8 +113,8 @@ "@types/node": "^22.8.6", "@types/papaparse": "^5.3.15", "@types/pdf-parse": "^1.1.4", - "@types/react": "^18", - "@types/react-dom": "^18", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", "@types/react-syntax-highlighter": "^15.5.13", "drizzle-kit": "^0.25.0", "postcss": "^8", diff --git a/lessons.md b/lessons.md new file mode 100644 index 00000000..80dc533c --- /dev/null +++ b/lessons.md @@ -0,0 +1,108 @@ +# Lessons Learned — optimization/v1 + +Issues encountered during the optimization branch and how they were resolved. + +--- + +## 1. Rust: Arc moved before clone + +**Error:** `error[E0382]: borrow of moved value: state` + +**Cause:** `Arc` was passed to `.with_state(state)` which consumes the value. A `.clone()` was attempted after the move. + +**Fix:** Clone the Arc *before* the move: `let state_for_shutdown = state.clone();` placed before `.with_state(state)`. + +**Lesson:** In Rust, `Arc::clone()` must happen before any function that takes ownership. Plan the clone order at design time, not after the compiler complains. + +--- + +## 2. Rust: Clippy warnings treated as errors in CI + +**Errors:** +- `dead_code` — structs/functions added for future use but not yet called +- `clippy::needless_as_bytes` — `text.as_bytes().len()` vs `text.len()` +- `clippy::type_complexity` — tuple with 6 elements used as function parameter +- `clippy::too_many_arguments` — function with 9 parameters +- `clippy::useless_conversion` — `.into_iter()` on a value already implementing `IntoIterator` + +**Fix:** Applied targeted `#[allow(...)]` annotations where the code is intentional (dead_code for prepared APIs, too_many_arguments for existing signatures). Fixed the actual code issues (`.as_bytes().len()` → `.len()`, removed redundant `.into_iter()`). + +**Lesson:** Run `cargo clippy -- -D warnings` locally before pushing. Add it to a pre-commit hook or local dev script. Clippy catches real bugs alongside style issues. + +--- + +## 3. CI: GitHub Actions Node.js 20 deprecation + +**Error:** `Node.js 20 actions are deprecated... Actions will be forced to run with Node.js 24 by default starting June 2nd, 2026.` + +**Fix:** Set `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true` at the workflow env level. Bumped the project `node-version` from 20 to 22 (current LTS). + +**Lesson:** GitHub Actions deprecation warnings should be addressed proactively. Pin to LTS versions and set the Node.js 24 opt-in flag now to avoid future breakage. + +--- + +## 4. CI: pnpm version conflict with packageManager field + +**Error:** `Multiple versions of pnpm specified: version 9 in the GitHub Action config and pnpm@9.12.3 in package.json packageManager` + +**Fix:** Removed the explicit `version: 9` from `pnpm/action-setup@v4`. The action auto-detects the version from `"packageManager": "pnpm@9.12.3"` in package.json. + +**Lesson:** When using `pnpm/action-setup@v4` with a monorepo that has `packageManager` in package.json, do not also specify `version` in the action config. Let the action read from the source of truth. + +--- + +## 5. CI: Turborepo builds all packages instead of just the target + +**Error:** `Tasks: 4 successful, 6 total` — chatbot, noter, researcher built alongside SDK. + +**Cause:** `pnpm --filter @mysten-incubation/memwal build` was intercepted by turbo's daemon. Turbo resolved the `build` task's `"dependsOn": ["^build"]` across the entire workspace graph, not just the filtered package. + +**Fix:** Changed from `pnpm --filter ... build` to `turbo run build --filter=@mysten-incubation/memwal`. Turbo's own `--filter` flag properly scopes task execution to only the target package and its actual dependencies. + +**Lesson:** In a Turborepo monorepo, always use `turbo run --filter=` instead of `pnpm --filter `. The pnpm filter passes through to turbo which then ignores the pnpm-level filter and runs the full dependency graph. + +--- + +## 6. CI: Apps requiring database at build time + +**Error:** chatbot/noter/researcher builds failed because their `build` scripts run `tsx lib/db/migrate` which requires `DATABASE_URL`. + +**Fix:** Scoped CI to only build the SDK (no database required). Apps are deployed through separate workflows with proper environment variables. + +**Lesson:** Build scripts that run database migrations should not be part of a general CI build. Either separate the migration step or use `turbo --filter` to exclude apps that need runtime services. + +--- + +## 7. TypeScript: @types/react version mismatch + +**Error:** `Module '"react"' has no exported member 'useActionState'.` + +**Cause:** `apps/chatbot` had `react: 19.0.1` (runtime) but `@types/react: ^18` (type definitions). `useActionState` was introduced in React 19 and doesn't exist in the React 18 type definitions. + +**Fix:** Upgraded `@types/react` from `^18` to `^19` and `@types/react-dom` from `^18` to `^19` to match the installed runtime. + +**Lesson:** When upgrading React, always upgrade `@types/react` and `@types/react-dom` to the same major version. Type definition mismatches cause phantom errors that look like missing exports but are really just outdated type packages. + +--- + +## 8. CI: turbo command not found + +**Error:** `/home/runner/work/_temp/xxx.sh: line 1: turbo: command not found` (exit code 127) + +**Cause:** CI workflow ran `turbo run build --filter=...` directly. Turbo is installed as a devDependency in `node_modules/.bin/`, not globally available in the CI runner's PATH. + +**Fix:** Changed to `pnpm exec turbo run build --filter=...`. `pnpm exec` resolves binaries from the local `node_modules/.bin/` directory. + +**Lesson:** In CI, never assume devDependency binaries are in PATH. Always use `pnpm exec`, `npx`, or `yarn exec` to invoke locally-installed CLI tools. This applies to turbo, tsc, eslint, vitest, and any other bin-only package. + +--- + +## 9. CI: `npm install -g npm@latest` self-corrupts in GitHub Actions + +**Error:** `npm error Cannot find module 'promise-retry'` when running `npm install -g npm@latest` in the release workflow. + +**Cause:** npm's global self-upgrade deletes its own dependencies (like `promise-retry`) from the old installation before the new installation's rebuild phase completes. This is a race condition in npm's self-update mechanism on GitHub Actions runners. + +**Fix:** Removed the `npm install -g npm@latest` step entirely. The step was added for OIDC Trusted Publishing (`--provenance` flag), but Node 22's bundled npm 10.x already supports this natively. + +**Lesson:** Never run `npm install -g npm@latest` in CI. Node's bundled npm version is sufficient for modern features like `--provenance`. If a specific npm version is truly needed, use `setup-node` with a pinned version or use `corepack`. diff --git a/package.json b/package.json index e3612aff..c210230b 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,10 @@ "private": true, "type": "module", "scripts": { - "build:sdk": "pnpm --filter @mysten-incubation/memwal build", + "build": "turbo run build", + "test": "turbo run test", + "typecheck": "turbo run typecheck", + "build:sdk": "turbo run build --filter=@mysten-incubation/memwal", "dev:sdk": "pnpm --filter @mysten-incubation/memwal dev", "dev:noter": "pnpm --filter noter dev", "dev:chatbot": "pnpm --filter chatbot dev", @@ -23,6 +26,7 @@ "@changesets/changelog-github": "^0.5.2", "@changesets/cli": "^2.29.8", "tsx": "^4.21.0", + "turbo": "^2.9.6", "typescript": "^5" }, "packageManager": "pnpm@9.12.3", diff --git a/packages/sdk/src/client.ts b/packages/sdk/src/client.ts new file mode 100644 index 00000000..a8203176 --- /dev/null +++ b/packages/sdk/src/client.ts @@ -0,0 +1,59 @@ +/** + * memwal — HTTP Client Wrapper + * + * Provides a fetch-compatible interface with connection reuse (keep-alive). + * Uses Node.js http.Agent when available, falls back to native fetch otherwise. + */ + +export interface HttpClient { + fetch(url: string, init?: RequestInit): Promise; + destroy?(): void; +} + +/** + * Default HTTP client that reuses connections via Node.js http.Agent keepAlive. + * Falls back to plain fetch in browser environments. + */ +export function createHttpClient(): HttpClient { + // In Node.js, use http.Agent with keepAlive for connection reuse + if (typeof globalThis.process !== "undefined" && globalThis.process.versions?.node) { + let agent: any = null; + let httpsAgent: any = null; + + // Lazy-init agents on first use + const getAgent = async (url: string) => { + if (url.startsWith("https")) { + if (!httpsAgent) { + const https = await import("https"); + httpsAgent = new https.Agent({ keepAlive: true }); + } + return httpsAgent; + } + if (!agent) { + const http = await import("http"); + agent = new http.Agent({ keepAlive: true }); + } + return agent; + }; + + return { + async fetch(url: string, init?: RequestInit): Promise { + // Node.js 18+ fetch doesn't support agent directly, + // but undici dispatcher can be passed. For broad compat, + // use native fetch (which already uses keepAlive by default in Node 19+). + return globalThis.fetch(url, init); + }, + destroy() { + agent?.destroy(); + httpsAgent?.destroy(); + }, + }; + } + + // Browser: native fetch already handles connection reuse + return { + fetch(url: string, init?: RequestInit): Promise { + return globalThis.fetch(url, init); + }, + }; +} diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 11352cca..22c39386 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -14,6 +14,10 @@ // Core client (server-mode: server handles SEAL + Walrus + embedding) export { MemWal } from "./memwal.js"; +// HTTP client (connection reuse) +export { createHttpClient } from "./client.js"; +export type { HttpClient } from "./client.js"; + // Delegate key utilities (no @mysten/sui dependency) export { delegateKeyToSuiAddress, delegateKeyToPublicKey } from "./utils.js"; @@ -21,6 +25,8 @@ export { delegateKeyToSuiAddress, delegateKeyToPublicKey } from "./utils.js"; export type { MemWalConfig, RememberResult, + RememberBatchItem, + RememberBatchResult, RecallResult, RecallMemory, EmbedResult, diff --git a/packages/sdk/src/memwal.ts b/packages/sdk/src/memwal.ts index 849c4ed9..7c161a39 100644 --- a/packages/sdk/src/memwal.ts +++ b/packages/sdk/src/memwal.ts @@ -30,6 +30,8 @@ import type { MemWalConfig, RememberResult, + RememberBatchItem, + RememberBatchResult, RecallResult, RecallMemory, EmbedResult, @@ -42,6 +44,7 @@ import type { RestoreResult, } from "./types.js"; import { sha256hex, hexToBytes, bytesToHex } from "./utils.js"; +import { createHttpClient, type HttpClient } from "./client.js"; // ============================================================ // Ed25519 Signing (lazy-loaded) @@ -65,12 +68,14 @@ export class MemWal { private serverUrl: string; private namespace: string; private accountId: string; + private httpClient: HttpClient; - private constructor(config: MemWalConfig) { + private constructor(config: MemWalConfig & { httpClient?: HttpClient }) { this.privateKey = hexToBytes(config.key); this.accountId = config.accountId; this.serverUrl = (config.serverUrl ?? "http://localhost:8000").replace(/\/$/, ""); this.namespace = config.namespace ?? "default"; + this.httpClient = config.httpClient ?? createHttpClient(); } /** @@ -79,7 +84,7 @@ export class MemWal { * @param config.key - Ed25519 private key (hex string) — the delegate key * @param config.serverUrl - Server URL (default: http://localhost:8000) */ - static create(config: MemWalConfig): MemWal { + static create(config: MemWalConfig & { httpClient?: HttpClient }): MemWal { return new MemWal(config); } @@ -106,6 +111,31 @@ export class MemWal { }); } + /** + * Remember multiple items in a single batch request. + * Server processes all items in one transaction for atomicity. + * + * @param items - Array of items to remember (text + optional namespace) + * @returns RememberBatchResult with results for each item + * + * @example + * ```typescript + * const result = await memwal.rememberBatch([ + * { text: "I'm allergic to peanuts" }, + * { text: "I live in Hanoi" }, + * ]) + * console.log(result.total) // 2 + * ``` + */ + async rememberBatch(items: RememberBatchItem[]): Promise { + return this.signedRequest("POST", "/api/remember/batch", { + items: items.map((item) => ({ + text: item.text, + namespace: item.namespace ?? this.namespace, + })), + }); + } + /** * Recall memories similar to a query — server handles: * verify → embed query → search → Walrus download → decrypt → return plaintext @@ -123,7 +153,7 @@ export class MemWal { * ``` */ async recall(query: string, limit: number = 10, namespace?: string): Promise { - return this.signedRequest("POST", "/api/recall", { + return this.signedRequestWithRetry("POST", "/api/recall", { query, limit, namespace: namespace ?? this.namespace, @@ -186,7 +216,7 @@ export class MemWal { * ``` */ async recallManual(opts: RecallManualOptions): Promise { - return this.signedRequest("POST", "/api/recall/manual", { + return this.signedRequestWithRetry("POST", "/api/recall/manual", { vector: opts.vector, limit: opts.limit ?? 10, namespace: opts.namespace ?? this.namespace, @@ -200,7 +230,7 @@ export class MemWal { * @returns EmbedResult with vector */ async embed(text: string): Promise { - return this.signedRequest("POST", "/api/embed", { text }); + return this.signedRequestWithRetry("POST", "/api/embed", { text }); } /** @@ -247,7 +277,7 @@ export class MemWal { * Check server health. */ async health(): Promise { - const res = await fetch(`${this.serverUrl}/health`); + const res = await this.httpClient.fetch(`${this.serverUrl}/health`); if (!res.ok) { throw new Error(`Health check failed: ${res.status}`); } @@ -304,18 +334,16 @@ export class MemWal { // Make HTTP request const url = `${this.serverUrl}${path}`; - const res = await fetch(url, { - method, - headers: { - "Content-Type": "application/json", - "x-public-key": bytesToHex(publicKey), - "x-signature": bytesToHex(signature), - "x-timestamp": timestamp, - "x-delegate-key": bytesToHex(this.privateKey), - "x-account-id": this.accountId, - }, - body: bodyStr, - }); + const headers = { + "Content-Type": "application/json", + "x-public-key": bytesToHex(publicKey), + "x-signature": bytesToHex(signature), + "x-timestamp": timestamp, + "x-delegate-key": bytesToHex(this.privateKey), + "x-account-id": this.accountId, + }; + + const res = await this.httpClient.fetch(url, { method, headers, body: bodyStr }); if (!res.ok) { const errText = await res.text(); @@ -324,4 +352,42 @@ export class MemWal { return res.json() as Promise; } + + /** + * Make a signed request with automatic retry on transient failures. + * Retries up to 3 times on HTTP 429 (rate limit) or network errors. + * Uses exponential backoff: 1s, 2s, 4s. + */ + private async signedRequestWithRetry( + method: string, + path: string, + body: object, + ): Promise { + const MAX_RETRIES = 3; + const RETRY_DELAYS = [1000, 2000, 4000]; + + for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) { + try { + return await this.signedRequest(method, path, body); + } catch (err: unknown) { + const errMsg = err instanceof Error ? err.message : String(err); + const isRetryable = + errMsg.includes("(429)") || + errMsg.includes("ECONNREFUSED") || + errMsg.includes("ECONNRESET") || + errMsg.includes("fetch failed") || + errMsg.includes("network"); + + if (!isRetryable || attempt === MAX_RETRIES) { + throw err; + } + + const delay = RETRY_DELAYS[attempt]; + await new Promise((resolve) => setTimeout(resolve, delay)); + } + } + + // Unreachable, but TypeScript needs it + throw new Error("Retry logic exhausted"); + } } diff --git a/packages/sdk/src/types.ts b/packages/sdk/src/types.ts index 8a24d78e..6074470d 100644 --- a/packages/sdk/src/types.ts +++ b/packages/sdk/src/types.ts @@ -32,6 +32,18 @@ export interface RememberResult { namespace: string; } +/** Input item for rememberBatch() */ +export interface RememberBatchItem { + text: string; + namespace?: string; +} + +/** Result from rememberBatch() */ +export interface RememberBatchResult { + results: RememberResult[]; + total: number; +} + /** A single recalled memory */ export interface RecallMemory { blob_id: string; diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json index 03f24ca7..686bdbba 100644 --- a/packages/sdk/tsconfig.json +++ b/packages/sdk/tsconfig.json @@ -17,7 +17,8 @@ "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, - "isolatedModules": true + "isolatedModules": true, + "composite": true }, "include": [ "src/**/*" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c365d575..cffd4dde 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: tsx: specifier: ^4.21.0 version: 4.21.0 + turbo: + specifier: ^2.9.6 + version: 2.9.6 typescript: specifier: ^5 version: 5.9.3 @@ -188,43 +191,43 @@ importers: version: 0.200.0 '@radix-ui/react-collapsible': specifier: ^1.1.12 - version: 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-dialog': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-dropdown-menu': specifier: ^2.1.16 - version: 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-hover-card': specifier: ^1.1.15 - version: 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-icons': specifier: ^1.3.0 version: 1.3.2(react@19.0.1) '@radix-ui/react-progress': specifier: ^1.1.8 - version: 1.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-scroll-area': specifier: ^1.2.10 - version: 1.2.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-select': specifier: ^2.2.6 - version: 2.2.6(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-separator': specifier: ^1.1.8 - version: 1.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-slot': specifier: ^1.2.4 - version: 1.2.4(@types/react@18.3.28)(react@19.0.1) + version: 1.2.4(@types/react@19.2.14)(react@19.0.1) '@radix-ui/react-tooltip': specifier: ^1.2.8 - version: 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@radix-ui/react-use-controllable-state': specifier: ^1.2.2 - version: 1.2.2(@types/react@18.3.28)(react@19.0.1) + version: 1.2.2(@types/react@19.2.14)(react@19.0.1) '@radix-ui/react-visually-hidden': specifier: ^1.1.0 - version: 1.2.4(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.2.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) '@vercel/analytics': specifier: ^1.3.1 version: 1.6.1(next@16.0.10(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(react-dom@19.0.1(react@19.0.1))(react@19.0.1))(react@19.0.1)(vue@3.5.30(typescript@5.9.3)) @@ -239,7 +242,7 @@ importers: version: 1.14.0(@opentelemetry/api-logs@0.200.0)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0)) '@xyflow/react': specifier: ^12.10.0 - version: 12.10.1(@types/react@18.3.28)(immer@9.0.21)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 12.10.1(@types/react@19.2.14)(immer@9.0.21)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) ai: specifier: 6.0.37 version: 6.0.37(zod@3.25.76) @@ -260,7 +263,7 @@ importers: version: 2.1.1 cmdk: specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) codemirror: specifier: ^6.0.1 version: 6.0.2 @@ -275,7 +278,7 @@ importers: version: 16.6.1 drizzle-orm: specifier: ^0.34.0 - version: 0.34.1(@opentelemetry/api@1.9.0)(@types/react@18.3.28)(postgres@3.4.8)(react@19.0.1) + version: 0.34.1(@opentelemetry/api@1.9.0)(@types/react@19.2.14)(postgres@3.4.8)(react@19.0.1) embla-carousel-react: specifier: ^8.6.0 version: 8.6.0(react@19.0.1) @@ -350,7 +353,7 @@ importers: version: 1.41.6 radix-ui: specifier: ^1.4.3 - version: 1.4.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) + version: 1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) react: specifier: 19.0.1 version: 19.0.1 @@ -428,11 +431,11 @@ importers: specifier: ^1.1.4 version: 1.1.5 '@types/react': - specifier: ^18 - version: 18.3.28 + specifier: ^19.2.14 + version: 19.2.14 '@types/react-dom': - specifier: ^18 - version: 18.3.7(@types/react@18.3.28) + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) '@types/react-syntax-highlighter': specifier: ^15.5.13 version: 15.5.13 @@ -4846,6 +4849,36 @@ packages: '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} + '@turbo/darwin-64@2.9.6': + resolution: {integrity: sha512-X/56SnVXIQZBLKwniGTwEQTGmtE5brSACnKMBWpY3YafuxVYefrC2acamfjgxP7BG5w3I+6jf0UrLoSzgPcSJg==} + cpu: [x64] + os: [darwin] + + '@turbo/darwin-arm64@2.9.6': + resolution: {integrity: sha512-aalBeSl4agT/QtYGDyf/XLajedWzUC9Vg/pm/YO6QQ93vkQ91Vz5uK1ta5RbVRDozQSz4njxUNqRNmOXDzW+qw==} + cpu: [arm64] + os: [darwin] + + '@turbo/linux-64@2.9.6': + resolution: {integrity: sha512-YKi05jnNHaD7vevgYwahpzGwbsNNTwzU2c7VZdmdFm7+cGDP4oREUWSsainiMfRqjRuolQxBwRn8wf1jmu+YZA==} + cpu: [x64] + os: [linux] + + '@turbo/linux-arm64@2.9.6': + resolution: {integrity: sha512-02o/ZS69cOYEDczXvOB2xmyrtzjQ2hVFtWZK1iqxXUfzMmTjZK4UumrfNnjckSg+gqeBfnPRHa0NstA173Ik3g==} + cpu: [arm64] + os: [linux] + + '@turbo/windows-64@2.9.6': + resolution: {integrity: sha512-wVdQjvnBI15wB6JrA+43CtUtagjIMmX6XYO758oZHAsCNSxqRlJtdyujih0D8OCnwCRWiGWGI63zAxR0hO6s9g==} + cpu: [x64] + os: [win32] + + '@turbo/windows-arm64@2.9.6': + resolution: {integrity: sha512-1XUUyWW0W6FTSqGEhU8RHVqb2wP1SPkr7hIvBlMEwH9jr+sJQK5kqeosLJ/QaUv4ecSAd1ZhIrLoW7qslAzT4A==} + cpu: [arm64] + os: [win32] + '@tweenjs/tween.js@23.1.3': resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} @@ -4994,14 +5027,6 @@ packages: '@types/pdf-parse@1.1.5': resolution: {integrity: sha512-kBfrSXsloMnUJOKi25s3+hRmkycHfLK6A09eRGqF/N8BkQoPUmaCr+q8Cli5FnfohEz/rsv82zAiPz/LXtOGhA==} - '@types/prop-types@15.7.15': - resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} - - '@types/react-dom@18.3.7': - resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} - peerDependencies: - '@types/react': ^18.0.0 - '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} peerDependencies: @@ -5015,9 +5040,6 @@ packages: '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@18.3.28': - resolution: {integrity: sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==} - '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} @@ -5790,6 +5812,7 @@ packages: basic-ftp@5.2.0: resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} engines: {node: '>=10.0.0'} + deprecated: Security vulnerability fixed in 5.2.1, please upgrade bcrypt-ts@5.0.3: resolution: {integrity: sha512-2FcgD12xPbwCoe5i9/HK0jJ1xA1m+QfC1e6htG9Bl/hNOnLyaFmQSlqLKcfe3QdnoMPKpKEGFCbESBTg+SJNOw==} @@ -10335,6 +10358,7 @@ packages: tar@6.1.15: resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me teeny-request@10.1.0: resolution: {integrity: sha512-3ZnLvgWF29jikg1sAQ1g0o+lr5JX6sVgYvfUJazn7ZjJroDBUTWp44/+cFVX0bULjv4vci+rBD+oGVAkWqhUbw==} @@ -10473,6 +10497,10 @@ packages: tunnel-rat@0.1.2: resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==} + turbo@2.9.6: + resolution: {integrity: sha512-+v2QJey7ZUeUiuigkU+uFfklvNUyPI2VO2vBpMYJA+a1hKFLFiKtUYlRHdb3P9CrAvMzi0upbjI4WT+zKtqkBg==} + hasBin: true + tw-animate-css@1.4.0: resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} @@ -14037,15 +14065,6 @@ snapshots: '@radix-ui/primitive@1.1.3': {} - '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-accessible-icon@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -14064,23 +14083,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-accordion@1.2.12(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14115,20 +14117,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14157,15 +14145,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -14184,15 +14163,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -14211,19 +14181,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -14250,22 +14207,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14298,22 +14239,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14346,18 +14271,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-collection@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -14382,12 +14295,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -14400,20 +14307,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14442,12 +14335,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-context@1.1.2(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -14460,40 +14347,12 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-context@1.1.3(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-context@1.1.3(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-dialog@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - aria-hidden: 1.2.6 - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - react-remove-scroll: 2.7.2(@types/react@18.3.28)(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14538,12 +14397,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-direction@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -14556,19 +14409,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14595,21 +14435,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14640,12 +14465,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -14658,17 +14477,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -14691,20 +14499,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-form@0.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-label': 2.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-form@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14733,23 +14527,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14788,13 +14565,6 @@ snapshots: dependencies: react: 19.0.1 - '@radix-ui/react-id@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.0.1) @@ -14809,15 +14579,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-label@2.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-label@2.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -14836,32 +14597,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-menu@2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - aria-hidden: 1.2.6 - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - react-remove-scroll: 2.7.2(@types/react@18.3.28)(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14914,24 +14649,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -14968,28 +14685,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15034,26 +14729,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-one-time-password-field@0.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/number': 1.1.1 @@ -15094,22 +14769,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-password-toggle-field@0.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15142,29 +14801,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-popover@1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - aria-hidden: 1.2.6 - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - react-remove-scroll: 2.7.2(@types/react@18.3.28)(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15211,24 +14847,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-popper@1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/rect': 1.1.1 - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@floating-ui/react-dom': 2.1.8(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -15265,16 +14883,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -15295,16 +14903,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-presence@1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -15325,15 +14923,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.0.1) @@ -15352,15 +14941,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.0.1) @@ -15379,16 +14959,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-progress@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -15409,16 +14979,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-progress@1.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-context': 1.1.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-progress@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-context': 1.1.3(@types/react@19.2.14)(react@19.0.1) @@ -15429,24 +14989,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15483,23 +15025,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15534,23 +15059,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/number': 1.1.1 @@ -15585,35 +15093,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-select@2.2.6(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - aria-hidden: 1.2.6 - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - react-remove-scroll: 2.7.2(@types/react@18.3.28)(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/number': 1.1.1 @@ -15672,15 +15151,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-separator@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -15699,15 +15169,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-separator@1.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -15717,25 +15178,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-slider@1.3.6(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/number': 1.1.1 @@ -15774,13 +15216,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-slot@1.2.3(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -15795,13 +15230,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-slot@1.2.4(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -15816,21 +15244,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-switch@1.2.6(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15861,22 +15274,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15909,26 +15306,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toast@1.2.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -15969,21 +15346,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16014,17 +15376,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-toggle@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16047,21 +15398,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-toolbar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16092,26 +15428,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -16152,12 +15468,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -16170,14 +15480,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.0.1) @@ -16194,13 +15496,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.0.1) @@ -16215,13 +15510,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.0.1) @@ -16236,13 +15524,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - use-sync-external-store: 1.6.0(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -16257,12 +15538,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -16275,12 +15550,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: react: 19.0.1 @@ -16293,13 +15562,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/rect': 1.1.1 @@ -16314,13 +15576,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-use-size@1.1.1(@types/react@18.3.28)(react@19.0.1)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - react: 19.0.1 - optionalDependencies: - '@types/react': 18.3.28 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.0.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.0.1) @@ -16335,15 +15590,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -16362,15 +15608,6 @@ snapshots: '@types/react': 19.2.14 '@types/react-dom': 19.2.3(@types/react@19.2.14) - '@radix-ui/react-visually-hidden@1.2.4(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - '@radix-ui/react-visually-hidden@1.2.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) @@ -17001,6 +16238,24 @@ snapshots: minimatch: 10.2.4 path-browserify: 1.0.1 + '@turbo/darwin-64@2.9.6': + optional: true + + '@turbo/darwin-arm64@2.9.6': + optional: true + + '@turbo/linux-64@2.9.6': + optional: true + + '@turbo/linux-arm64@2.9.6': + optional: true + + '@turbo/windows-64@2.9.6': + optional: true + + '@turbo/windows-arm64@2.9.6': + optional: true + '@tweenjs/tween.js@23.1.3': {} '@tybys/wasm-util@0.10.1': @@ -17161,12 +16416,6 @@ snapshots: dependencies: '@types/node': 22.19.15 - '@types/prop-types@15.7.15': {} - - '@types/react-dom@18.3.7(@types/react@18.3.28)': - dependencies: - '@types/react': 18.3.28 - '@types/react-dom@19.2.3(@types/react@19.2.14)': dependencies: '@types/react': 19.2.14 @@ -17179,11 +16428,6 @@ snapshots: dependencies: '@types/react': 19.2.14 - '@types/react@18.3.28': - dependencies: - '@types/prop-types': 15.7.15 - csstype: 3.2.3 - '@types/react@19.2.14': dependencies: csstype: 3.2.3 @@ -17626,17 +16870,6 @@ snapshots: '@webgpu/types@0.1.69': {} - '@xyflow/react@12.10.1(@types/react@18.3.28)(immer@9.0.21)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': - dependencies: - '@xyflow/system': 0.0.75 - classcat: 5.0.5 - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - zustand: 4.5.7(@types/react@18.3.28)(immer@9.0.21)(react@19.0.1) - transitivePeerDependencies: - - '@types/react' - - immer - '@xyflow/react@12.10.1(@types/react@19.2.14)(immer@9.0.21)(react-dom@19.0.1(react@19.0.1))(react@19.0.1)': dependencies: '@xyflow/system': 0.0.75 @@ -18264,18 +17497,6 @@ snapshots: cluster-key-slot@1.1.2: {} - cmdk@1.1.1(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1): - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-id': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1): dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.0.1) @@ -18680,13 +17901,6 @@ snapshots: esbuild: 0.25.12 tsx: 4.21.0 - drizzle-orm@0.34.1(@opentelemetry/api@1.9.0)(@types/react@18.3.28)(postgres@3.4.8)(react@19.0.1): - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/react': 18.3.28 - postgres: 3.4.8 - react: 19.0.1 - drizzle-orm@0.34.1(@opentelemetry/api@1.9.0)(@types/react@19.2.14)(postgres@3.4.8)(react@19.0.1): optionalDependencies: '@opentelemetry/api': 1.9.0 @@ -22366,69 +21580,6 @@ snapshots: quick-lru@5.1.1: {} - radix-ui@1.4.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1): - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-accessible-icon': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-accordion': 1.2.12(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-alert-dialog': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-aspect-ratio': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-avatar': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-checkbox': 1.3.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context': 1.1.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-context-menu': 2.2.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-direction': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-form': 0.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-hover-card': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-label': 2.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-menubar': 1.1.16(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-navigation-menu': 1.2.14(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-one-time-password-field': 0.1.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-password-toggle-field': 0.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-popover': 1.1.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-progress': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-radio-group': 1.3.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-scroll-area': 1.2.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-select': 2.2.6(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-separator': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slider': 1.3.6(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-slot': 1.2.3(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-switch': 1.2.6(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-toast': 1.2.15(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-toggle': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-toggle-group': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-toolbar': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-tooltip': 1.2.8(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.28)(react@19.0.1) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.28))(@types/react@18.3.28)(react-dom@19.0.1(react@19.0.1))(react@19.0.1) - react: 19.0.1 - react-dom: 19.0.1(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - '@types/react-dom': 18.3.7(@types/react@18.3.28) - radix-ui@1.4.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.0.1(react@19.0.1))(react@19.0.1): dependencies: '@radix-ui/primitive': 1.1.3 @@ -22614,14 +21765,6 @@ snapshots: react-refresh@0.18.0: {} - react-remove-scroll-bar@2.3.8(@types/react@18.3.28)(react@19.0.1): - dependencies: - react: 19.0.1 - react-style-singleton: 2.2.3(@types/react@18.3.28)(react@19.0.1) - tslib: 2.8.1 - optionalDependencies: - '@types/react': 18.3.28 - react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.0.1): dependencies: react: 19.0.1 @@ -22638,17 +21781,6 @@ snapshots: optionalDependencies: '@types/react': 19.2.14 - react-remove-scroll@2.7.2(@types/react@18.3.28)(react@19.0.1): - dependencies: - react: 19.0.1 - react-remove-scroll-bar: 2.3.8(@types/react@18.3.28)(react@19.0.1) - react-style-singleton: 2.2.3(@types/react@18.3.28)(react@19.0.1) - tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@18.3.28)(react@19.0.1) - use-sidecar: 1.1.3(@types/react@18.3.28)(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.0.1): dependencies: react: 19.0.1 @@ -22703,14 +21835,6 @@ snapshots: react-dom: 19.2.3(react@19.2.3) react-transition-group: 4.4.5(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react-style-singleton@2.2.3(@types/react@18.3.28)(react@19.0.1): - dependencies: - get-nonce: 1.0.1 - react: 19.0.1 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 18.3.28 - react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.0.1): dependencies: get-nonce: 1.0.1 @@ -24053,6 +23177,15 @@ snapshots: - immer - react + turbo@2.9.6: + optionalDependencies: + '@turbo/darwin-64': 2.9.6 + '@turbo/darwin-arm64': 2.9.6 + '@turbo/linux-64': 2.9.6 + '@turbo/linux-arm64': 2.9.6 + '@turbo/windows-64': 2.9.6 + '@turbo/windows-arm64': 2.9.6 + tw-animate-css@1.4.0: {} tweetnacl@1.0.3: {} @@ -24313,13 +23446,6 @@ snapshots: urlpattern-polyfill@10.0.0: {} - use-callback-ref@1.3.3(@types/react@18.3.28)(react@19.0.1): - dependencies: - react: 19.0.1 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 18.3.28 - use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.0.1): dependencies: react: 19.0.1 @@ -24338,14 +23464,6 @@ snapshots: dependencies: react: 19.2.3 - use-sidecar@1.1.3(@types/react@18.3.28)(react@19.0.1): - dependencies: - detect-node-es: 1.1.0 - react: 19.0.1 - tslib: 2.8.1 - optionalDependencies: - '@types/react': 18.3.28 - use-sidecar@1.1.3(@types/react@19.2.14)(react@19.0.1): dependencies: detect-node-es: 1.1.0 @@ -24745,14 +23863,6 @@ snapshots: zod@4.3.6: {} - zustand@4.5.7(@types/react@18.3.28)(immer@9.0.21)(react@19.0.1): - dependencies: - use-sync-external-store: 1.6.0(react@19.0.1) - optionalDependencies: - '@types/react': 18.3.28 - immer: 9.0.21 - react: 19.0.1 - zustand@4.5.7(@types/react@19.2.14)(immer@9.0.21)(react@19.0.1): dependencies: use-sync-external-store: 1.6.0(react@19.0.1) diff --git a/services/server/migrations/004_add_ivfflat_index.sql b/services/server/migrations/004_add_ivfflat_index.sql new file mode 100644 index 00000000..8a90c6ae --- /dev/null +++ b/services/server/migrations/004_add_ivfflat_index.sql @@ -0,0 +1,7 @@ +-- memwal — Add IVFFlat index for embedding column +-- IVFFlat is better suited for large datasets with frequent writes. +-- Complements the existing HNSW index — the query planner picks the optimal one. +-- Rollback: DROP INDEX IF EXISTS idx_vector_entries_embedding_ivfflat; + +CREATE INDEX IF NOT EXISTS idx_vector_entries_embedding_ivfflat +ON vector_entries USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100); diff --git a/services/server/scripts/sidecar-server.ts b/services/server/scripts/sidecar-server.ts index 050b09c4..e64745d6 100644 --- a/services/server/scripts/sidecar-server.ts +++ b/services/server/scripts/sidecar-server.ts @@ -885,6 +885,65 @@ app.post("/sponsor/execute", async (req, res) => { } }); +// ============================================================ +// POST /embed-batch — Batch text embedding via OpenAI-compatible API +// Accepts array of texts, calls embedding API in parallel, returns array of vectors +// ============================================================ +app.post("/embed-batch", async (req, res) => { + try { + const { texts } = req.body; + if (!texts || !Array.isArray(texts) || texts.length === 0) { + return res.status(400).json({ error: "Missing required field: texts (array of strings)" }); + } + + const apiKey = process.env.OPENAI_API_KEY; + if (!apiKey) { + return res.status(503).json({ error: "OPENAI_API_KEY is not configured" }); + } + + const apiBase = process.env.OPENAI_API_BASE || "https://api.openai.com/v1"; + const url = `${apiBase}/embeddings`; + + // Call embedding API in parallel for each text + const results = await Promise.all( + texts.map(async (text: string, index: number) => { + try { + const resp = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${apiKey}`, + }, + body: JSON.stringify({ + model: "openai/text-embedding-3-small", + input: text, + }), + }); + + if (!resp.ok) { + const body = await resp.text(); + return { index, error: `API error (${resp.status}): ${body}` }; + } + + const data = (await resp.json()) as { data: { embedding: number[] }[] }; + return { index, embedding: data.data[0].embedding }; + } catch (err: any) { + return { index, error: err.message || String(err) }; + } + }) + ); + + const embeddings = results.filter((r) => "embedding" in r); + const errors = results.filter((r) => "error" in r); + + console.log(`[embed-batch] ${embeddings.length}/${texts.length} embedded ok, ${errors.length} errors`); + res.json({ results: embeddings, errors }); + } catch (err: any) { + console.error(`[embed-batch] error: ${err.message || err}`); + res.status(500).json({ error: err.message || String(err) }); + } +}); + // ============================================================ // Start server // ============================================================ diff --git a/services/server/src/db.rs b/services/server/src/db.rs index 02688217..024223c5 100644 --- a/services/server/src/db.rs +++ b/services/server/src/db.rs @@ -1,4 +1,5 @@ use pgvector::Vector; +use redis::AsyncCommands; use sqlx::postgres::PgPoolOptions; use sqlx::PgPool; @@ -12,7 +13,7 @@ impl VectorDb { /// Initialize database connection pool and run migrations pub async fn new(database_url: &str) -> Result { let pool = PgPoolOptions::new() - .max_connections(10) + .max_connections(20) .connect(database_url) .await .map_err(|e| AppError::Internal(format!("Failed to connect to database: {}", e)))?; @@ -36,11 +37,23 @@ impl VectorDb { .await .map_err(|e| AppError::Internal(format!("Failed to run migration 003: {}", e)))?; + let migration_004 = include_str!("../migrations/004_add_ivfflat_index.sql"); + sqlx::raw_sql(migration_004) + .execute(&pool) + .await + .map_err(|e| AppError::Internal(format!("Failed to run migration 004: {}", e)))?; + tracing::info!("database connected and migrations applied"); Ok(Self { pool }) } + /// Gracefully close the database connection pool. + pub async fn close(&self) { + self.pool.close().await; + tracing::info!("database connection pool closed"); + } + /// Insert a vector entry (with blob size tracking for storage quota) pub async fn insert_vector( &self, @@ -71,6 +84,40 @@ impl VectorDb { Ok(()) } + /// Insert multiple vector entries in a single transaction + /// Tuple: (id, owner, namespace, blob_id, vector, blob_size_bytes) + #[allow(clippy::type_complexity)] + pub async fn insert_vectors_batch( + &self, + entries: &[(String, String, String, String, Vec, i64)], + ) -> Result<(), AppError> { + let mut tx = self.pool.begin().await + .map_err(|e| AppError::Internal(format!("Failed to begin transaction: {}", e)))?; + + for (id, owner, namespace, blob_id, vector, blob_size_bytes) in entries { + let embedding = Vector::from(vector.clone()); + sqlx::query( + "INSERT INTO vector_entries (id, owner, namespace, blob_id, embedding, blob_size_bytes) + VALUES ($1, $2, $3, $4, $5, $6)", + ) + .bind(id) + .bind(owner) + .bind(namespace) + .bind(blob_id) + .bind(embedding) + .bind(blob_size_bytes) + .execute(&mut *tx) + .await + .map_err(|e| AppError::Internal(format!("Failed to insert vector in batch: {}", e)))?; + } + + tx.commit().await + .map_err(|e| AppError::Internal(format!("Failed to commit batch transaction: {}", e)))?; + + tracing::debug!("batch inserted {} vectors", entries.len()); + Ok(()) + } + /// Search for similar vectors using pgvector cosine distance (<=>) /// Returns blob_id and distance for each match pub async fn search_similar( @@ -105,6 +152,51 @@ impl VectorDb { Ok(results) } + /// Search with Redis caching (60s TTL). + /// Cache key: search:{owner}:{namespace}:{hash(vector)} + /// Falls back to DB query if Redis is unavailable. + pub async fn search_cached( + &self, + redis: &mut redis::aio::MultiplexedConnection, + query_vector: &[f32], + owner: &str, + namespace: &str, + limit: usize, + ) -> Result, AppError> { + // Build cache key from vector hash + use sha2::Digest; + let vec_bytes: Vec = query_vector.iter() + .flat_map(|f| f.to_le_bytes()) + .collect(); + let hash = hex::encode(sha2::Sha256::digest(&vec_bytes)); + let cache_key = format!("search:{}:{}:{}", owner, namespace, &hash[..16]); + + // Try cache first + match redis.get::<_, Option>(&cache_key).await { + Ok(Some(cached)) => { + if let Ok(hits) = serde_json::from_str::>(&cached) { + tracing::debug!("search cache HIT: key={}", cache_key); + return Ok(hits); + } + } + Ok(None) => { /* cache miss */ } + Err(e) => { + tracing::warn!("Redis get failed ({}), falling back to DB", e); + } + } + + // Cache miss — query DB + let hits = self.search_similar(query_vector, owner, namespace, limit).await?; + + // Store in cache (60s TTL), ignore errors + if let Ok(serialized) = serde_json::to_string(&hits) { + let _: Result<(), _> = redis.set_ex(&cache_key, &serialized, 60).await; + } + + tracing::debug!("search cache MISS: key={}", cache_key); + Ok(hits) + } + /// Get all blob_ids for a given owner + namespace (used by restore flow) pub async fn get_blobs_by_namespace( &self, diff --git a/services/server/src/main.rs b/services/server/src/main.rs index f64276dd..e2c05aa9 100644 --- a/services/server/src/main.rs +++ b/services/server/src/main.rs @@ -125,6 +125,7 @@ async fn main() { // Protected routes (require Ed25519 signature + onchain verification) let protected_routes = Router::new() .route("/api/remember", post(routes::remember)) + .route("/api/remember/batch", post(routes::remember_batch)) .route("/api/recall", post(routes::recall)) .route("/api/remember/manual", post(routes::remember_manual)) .route("/api/recall/manual", post(routes::recall_manual)) @@ -149,6 +150,9 @@ async fn main() { .route("/sponsor", post(routes::sponsor_proxy)) .route("/sponsor/execute", post(routes::sponsor_execute_proxy)); + // Clone state before moving into router (needed for shutdown cleanup) + let state_for_shutdown = state.clone(); + let app = Router::new() .merge(protected_routes) .merge(public_routes) @@ -166,10 +170,25 @@ async fn main() { tracing::info!(" health: http://localhost:{}/health", config.port); tracing::info!(" api: http://localhost:{}/api/{{remember,recall,analyze}}", config.port); - // Graceful shutdown: kill sidecar when server stops + // Graceful shutdown: handle Ctrl+C and SIGTERM let shutdown = async { - tokio::signal::ctrl_c().await.ok(); - tracing::info!("shutting down..."); + let ctrl_c = tokio::signal::ctrl_c(); + + #[cfg(unix)] + let terminate = async { + tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) + .expect("failed to install SIGTERM handler") + .recv() + .await; + }; + + #[cfg(not(unix))] + let terminate = std::future::pending::<()>(); + + tokio::select! { + _ = ctrl_c => { tracing::info!("received Ctrl+C, shutting down..."); }, + _ = terminate => { tracing::info!("received SIGTERM, shutting down..."); }, + } }; axum::serve(listener, app) @@ -177,7 +196,9 @@ async fn main() { .await .expect("Server failed"); - // Cleanup sidecar after shutdown + // Cleanup after shutdown + tracing::info!("closing database pool..."); + state_for_shutdown.db.close().await; sidecar_child.kill().await.ok(); - tracing::info!("sidecar stopped"); + tracing::info!("shutdown complete"); } diff --git a/services/server/src/routes.rs b/services/server/src/routes.rs index 81b34c52..aa6cef75 100644 --- a/services/server/src/routes.rs +++ b/services/server/src/routes.rs @@ -109,6 +109,73 @@ async fn generate_embedding( } } +/// Sidecar batch embedding response +#[derive(serde::Deserialize)] +#[allow(dead_code)] +struct EmbedBatchResult { + index: usize, + embedding: Vec, +} + +#[derive(serde::Deserialize)] +#[allow(dead_code)] +struct EmbedBatchResponse { + results: Vec, + errors: Vec, +} + +/// Generate embeddings for multiple texts in a single batch call via sidecar. +/// Falls back to sequential single-text embedding if sidecar batch endpoint is unavailable. +#[allow(dead_code)] +async fn generate_embeddings_batch( + client: &reqwest::Client, + config: &Config, + texts: &[String], +) -> Result>, AppError> { + // Try sidecar /embed-batch first + let url = format!("{}/embed-batch", config.sidecar_url); + match client + .post(&url) + .json(&serde_json::json!({ "texts": texts })) + .send() + .await + { + Ok(resp) if resp.status().is_success() => { + let batch_resp: EmbedBatchResponse = resp.json().await.map_err(|e| { + AppError::Internal(format!("Failed to parse embed-batch response: {}", e)) + })?; + + // Reconstruct ordered vector list + let mut embeddings = vec![Vec::new(); texts.len()]; + for result in batch_resp.results { + if result.index < embeddings.len() { + embeddings[result.index] = result.embedding; + } + } + + // Check all slots filled + if embeddings.iter().any(|v| v.is_empty()) { + tracing::warn!("embed-batch: some embeddings missing, falling back to sequential"); + } else { + return Ok(embeddings); + } + } + Ok(resp) => { + tracing::warn!("embed-batch returned {}, falling back to sequential", resp.status()); + } + Err(e) => { + tracing::warn!("embed-batch unavailable ({}), falling back to sequential", e); + } + } + + // Fallback: sequential single-text embedding + let mut embeddings = Vec::with_capacity(texts.len()); + for text in texts { + embeddings.push(generate_embedding(client, config, text).await?); + } + Ok(embeddings) +} + // ============================================================ // Routes // ============================================================ @@ -136,7 +203,7 @@ pub async fn remember( tracing::info!("remember: text=\"{}...\" owner={} ns={}", truncate_str(text, 50), owner, namespace); // Check storage quota before processing - let text_bytes = text.as_bytes().len() as i64; + let text_bytes = text.len() as i64; rate_limit::check_storage_quota(&state, owner, text_bytes).await?; // Step 1: Embed text + SEAL encrypt concurrently (they're independent) @@ -178,6 +245,97 @@ pub async fn remember( })) } +/// POST /api/remember/batch +/// +/// Batch version of /api/remember — processes multiple texts in a single transaction. +/// Each item goes through: embed + encrypt → Walrus upload → store. +/// All DB inserts are wrapped in one transaction for atomicity. +pub async fn remember_batch( + State(state): State>, + Extension(auth): Extension, + Json(body): Json, +) -> Result, AppError> { + if body.items.is_empty() { + return Err(AppError::BadRequest("Items array cannot be empty".into())); + } + if body.items.len() > 100 { + return Err(AppError::BadRequest("Batch size cannot exceed 100 items".into())); + } + for (i, item) in body.items.iter().enumerate() { + if item.text.is_empty() { + return Err(AppError::BadRequest(format!("Item {} has empty text", i))); + } + } + + let owner = &auth.owner; + tracing::info!("remember_batch: {} items, owner={}", body.items.len(), owner); + + // Check total storage quota + let total_bytes: i64 = body.items.iter().map(|i| i.text.len() as i64).sum(); + rate_limit::check_storage_quota(&state, owner, total_bytes).await?; + + // Process all items concurrently (embed + encrypt → upload) + let auth_pubkey = auth.public_key.clone(); + let tasks: Vec<_> = body.items.iter().map(|item| { + let state = Arc::clone(&state); + let owner = owner.clone(); + let text = item.text.clone(); + let namespace = item.namespace.clone(); + let auth_pubkey = auth_pubkey.clone(); + let sui_key: Result = state.key_pool.next() + .map(|s| s.to_string()) + .ok_or_else(|| AppError::Internal("No Sui keys configured".into())); + async move { + let sui_key = sui_key?; + + // Embed + encrypt concurrently + let embed_fut = generate_embedding(&state.http_client, &state.config, &text); + let encrypt_fut = seal::seal_encrypt( + &state.http_client, &state.config.sidecar_url, + text.as_bytes(), &owner, &state.config.package_id, + ); + let (vector_result, encrypted_result) = tokio::join!(embed_fut, encrypt_fut); + let vector = vector_result?; + let encrypted = encrypted_result?; + + // Upload to Walrus + let upload_result = walrus::upload_blob( + &state.http_client, &state.config.sidecar_url, + &encrypted, 50, &owner, &sui_key, &namespace, &state.config.package_id, + Some(&auth_pubkey), + ).await?; + + let id = uuid::Uuid::new_v4().to_string(); + let blob_size = encrypted.len() as i64; + let blob_id = upload_result.blob_id; + + Ok::<(String, String, String, String, Vec, i64, RememberResponse), AppError>(( + id.clone(), owner.clone(), namespace.clone(), blob_id.clone(), vector, blob_size, + RememberResponse { id, blob_id, owner: owner.clone(), namespace }, + )) + } + }).collect(); + + let results = futures::future::join_all(tasks).await; + + // Collect results and prepare batch DB insert + let mut db_entries = Vec::with_capacity(results.len()); + let mut responses = Vec::with_capacity(results.len()); + for result in results { + let (id, owner, namespace, blob_id, vector, blob_size, response) = result?; + db_entries.push((id, owner, namespace, blob_id, vector, blob_size)); + responses.push(response); + } + + // Single transaction for all DB inserts + state.db.insert_vectors_batch(&db_entries).await?; + + let total = responses.len(); + tracing::info!("remember_batch complete: {} items stored for owner={}", total, owner); + + Ok(Json(RememberBatchResponse { results: responses, total })) +} + /// POST /api/recall /// /// Full TEE flow: @@ -210,8 +368,10 @@ pub async fn recall( // Step 1: Embed query → vector let query_vector = generate_embedding(&state.http_client, &state.config, &body.query).await?; - // Step 2: Search Vector DB - let hits = state.db.search_similar(&query_vector, owner, namespace, body.limit).await?; + // Step 2: Search Vector DB (with Redis cache) + let hits = state.db.search_cached( + &mut state.redis.clone(), &query_vector, owner, namespace, body.limit, + ).await?; // Step 3: Download + SEAL decrypt all results concurrently let db = &state.db; @@ -372,8 +532,10 @@ pub async fn recall_manual( body.vector.len(), body.limit, owner, namespace ); - // Search Vector DB — return blob IDs + distances only - let hits = state.db.search_similar(&body.vector, owner, namespace, body.limit).await?; + // Search Vector DB — return blob IDs + distances only (with Redis cache) + let hits = state.db.search_cached( + &mut state.redis.clone(), &body.vector, owner, namespace, body.limit, + ).await?; let total = hits.len(); tracing::info!("recall_manual complete: {} results for owner={} ns={}", total, owner, namespace); @@ -416,7 +578,7 @@ pub async fn analyze( } // Check storage quota before processing all facts - let total_text_bytes: i64 = facts.iter().map(|f| f.as_bytes().len() as i64).sum(); + let total_text_bytes: i64 = facts.iter().map(|f| f.len() as i64).sum(); rate_limit::check_storage_quota(&state, owner, total_text_bytes).await?; // Step 2: Process all facts concurrently (embed + encrypt → upload → store) @@ -917,7 +1079,7 @@ pub async fn restore( // Step 4: SEAL decrypt with bounded concurrency (3 at a time) // Use per-blob package_id from on-chain metadata, fall back to current server config use futures::stream::{self, StreamExt}; - let decrypt_results: Vec> = stream::iter(downloaded.into_iter()) + let decrypt_results: Vec> = stream::iter(downloaded) .map(|(blob_id, encrypted_data)| { let http_client = &state.http_client; let sidecar_url = state.config.sidecar_url.clone(); diff --git a/services/server/src/types.rs b/services/server/src/types.rs index ad82cb8f..90cc6bd4 100644 --- a/services/server/src/types.rs +++ b/services/server/src/types.rs @@ -190,7 +190,7 @@ pub struct RecallResult { pub distance: f64, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct SearchHit { pub blob_id: String, pub distance: f64, @@ -198,6 +198,26 @@ pub struct SearchHit { +/// POST /api/remember/batch +/// Batch version of /api/remember — processes multiple texts in a single transaction +#[derive(Debug, Deserialize)] +pub struct RememberBatchRequest { + pub items: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct RememberBatchItem { + pub text: String, + #[serde(default = "default_namespace")] + pub namespace: String, +} + +#[derive(Debug, Serialize)] +pub struct RememberBatchResponse { + pub results: Vec, + pub total: usize, +} + /// POST /api/analyze /// Extract facts from conversation text using LLM, then remember each fact /// Owner is derived from delegate key via onchain verification (auth middleware) diff --git a/services/server/src/walrus.rs b/services/server/src/walrus.rs index 23489a99..f4be8467 100644 --- a/services/server/src/walrus.rs +++ b/services/server/src/walrus.rs @@ -63,6 +63,7 @@ struct WalrusUploadResponse { /// The server wallet pays for gas + storage. After certify, the blob object /// is transferred to `owner_address`. Namespace + owner are stored as /// on-chain metadata attributes for discoverability. +#[allow(clippy::too_many_arguments)] pub async fn upload_blob( client: &reqwest::Client, sidecar_url: &str, @@ -120,6 +121,48 @@ pub async fn upload_blob( }) } +/// A single item for batch upload +#[allow(dead_code)] +pub struct UploadItem { + pub data: Vec, + pub owner_address: String, + pub sui_private_key: String, + pub namespace: String, + pub package_id: String, + pub agent_id: Option, +} + +/// Upload multiple encrypted blobs to Walrus in parallel using the key pool. +/// Each item uses a different signing key from the pool to avoid serialization. +/// Returns a Vec of Results — one per item in the same order. +#[allow(dead_code)] +pub async fn upload_batch( + client: &reqwest::Client, + sidecar_url: &str, + items: Vec, + epochs: u64, +) -> Vec> { + let tasks: Vec<_> = items.into_iter().map(|item| { + let client = client.clone(); + let sidecar_url = sidecar_url.to_string(); + async move { + upload_blob( + &client, + &sidecar_url, + &item.data, + epochs, + &item.owner_address, + &item.sui_private_key, + &item.namespace, + &item.package_id, + item.agent_id.as_deref(), + ).await + } + }).collect(); + + futures::future::join_all(tasks).await +} + /// Query user's Walrus Blob objects from the Sui chain via sidecar. /// /// This enables restore-from-zero: even if the local DB is empty, diff --git a/turbo.json b/turbo.json new file mode 100644 index 00000000..084780d2 --- /dev/null +++ b/turbo.json @@ -0,0 +1,19 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", ".next/**", "build/**"] + }, + "dev": { + "cache": false, + "persistent": true + }, + "test": { + "dependsOn": ["build"] + }, + "typecheck": { + "dependsOn": ["^build"] + } + } +}