From d78ac171257d7a6a811a1af3211b4180a017362d Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 7 May 2026 23:19:20 -0600 Subject: [PATCH 01/51] fix(etl): use raw SQL for completion write to bypass neon-http enum issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Drizzle ORM .set({ status: 'completed' }) with the neon-http driver appears to fail silently (triggering the catch block which then sets status='failed'), even though the identical pattern with 'failed' works. Switch to a raw sql`UPDATE ... SET status = 'completed'::etl_job_status` to match the pattern already used in updateEtlJobProgress, bypassing any Drizzle/neon-http enum serialization difference. Also isolate the completion write in its own try-catch so a transient failure here logs the error and leaves the job 'running' (for Reset Stuck) rather than cascading to 'failed'. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../api/src/services/etl/processCatalogEtl.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 01a8f77f2d..7c3ee865d4 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -160,10 +160,18 @@ export async function processCatalogETL({ const totalRows = rowIndex; - await db - .update(etlJobs) - .set({ status: 'completed', completedAt: new Date() }) - .where(eq(etlJobs.id, jobId)); + // Use raw SQL to avoid neon-http enum serialization issues with Drizzle ORM. + // Isolated try-catch so a transient DB hiccup here doesn't cascade to status='failed'. + try { + await db.execute( + sql`UPDATE etl_jobs SET status = 'completed'::etl_job_status, completed_at = NOW() WHERE id = ${jobId}`, + ); + } catch (completionErr) { + console.error( + `[ETL] Failed to mark job ${jobId} completed — will be reset by stuck-job sweep:`, + completionErr, + ); + } console.log(`🔍 [TRACE] ✅ Done processing ${objectKey} - ${totalRows} rows processed`); } catch (error) { From cb7ef0a4fca42e0145b8850f9273c92347d99623 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 7 May 2026 23:49:39 -0600 Subject: [PATCH 02/51] fix(etl): make catalog_items weight nullable + add ETL integration tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The weight NOT NULL constraint on catalog_items was causing ETL job failures for any item missing weight data (common for clothing/footwear brands). The CatalogItemValidator explicitly marks weight as optional, but the DB would reject the INSERT, causing processValidItemsBatch's fallback to also fail, which propagated to the outer catch and set status='failed'. Migration 0037 drops NOT NULL from weight and weight_unit on catalog_items. Adds full ETL integration test suite confirming: happy path completes, no-weight items don't fail, invalid-only runs still complete, exact/multi-batch row counts work, and the embedding fallback doesn't throw to the outer caller. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../drizzle/0037_nullable_catalog_weight.sql | 5 + packages/api/src/db/schema.ts | 4 +- packages/api/test/etl.test.ts | 243 ++++++++++++++++++ 3 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 packages/api/drizzle/0037_nullable_catalog_weight.sql create mode 100644 packages/api/test/etl.test.ts diff --git a/packages/api/drizzle/0037_nullable_catalog_weight.sql b/packages/api/drizzle/0037_nullable_catalog_weight.sql new file mode 100644 index 0000000000..8cec041368 --- /dev/null +++ b/packages/api/drizzle/0037_nullable_catalog_weight.sql @@ -0,0 +1,5 @@ +-- catalog_items.weight and weight_unit: drop NOT NULL to allow items without weight data. +-- The validator intentionally skips weight (clothing/footwear often omit it), but the +-- NOT NULL constraint was causing upserts to throw, which cascaded to ETL job failures. +ALTER TABLE "catalog_items" ALTER COLUMN "weight" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "catalog_items" ALTER COLUMN "weight_unit" DROP NOT NULL; diff --git a/packages/api/src/db/schema.ts b/packages/api/src/db/schema.ts index a14bf75aac..a16763444a 100644 --- a/packages/api/src/db/schema.ts +++ b/packages/api/src/db/schema.ts @@ -99,8 +99,8 @@ export const catalogItems = pgTable( name: text('name').notNull(), productUrl: text('product_url').notNull(), sku: text('sku').unique().notNull(), - weight: real('weight').notNull(), - weightUnit: text('weight_unit').notNull().$type(), + weight: real('weight'), + weightUnit: text('weight_unit').$type(), description: text('description'), categories: jsonb('categories').$type(), images: jsonb('images').$type(), diff --git a/packages/api/test/etl.test.ts b/packages/api/test/etl.test.ts new file mode 100644 index 0000000000..f70c70e917 --- /dev/null +++ b/packages/api/test/etl.test.ts @@ -0,0 +1,243 @@ +import { createDbClient } from '@packrat/api/db'; +import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/api/db/schema'; +import { processCatalogETL } from '@packrat/api/services/etl/processCatalogEtl'; +import { processValidItemsBatch } from '@packrat/api/services/etl/processValidItemsBatch'; +import { R2BucketService } from '@packrat/api/services/r2-bucket'; +import { count, eq } from 'drizzle-orm'; +import { describe, expect, it, vi } from 'vitest'; + +// ── CSV helpers ─────────────────────────────────────────────────────────────── + +const CSV_HEADER = 'name,sku,productUrl,brand,price,weight,weightUnit\n'; +const CSV_ROW = (i: number) => + `Test Item ${i},SKU-${i},https://example.com/item-${i},TestBrand,49.99,500,g\n`; + +function makeCsv(rows: number): string { + return CSV_HEADER + Array.from({ length: rows }, (_, i) => CSV_ROW(i)).join(''); +} + +function makeReadableStream(text: string): ReadableStream { + const encoder = new TextEncoder(); + const bytes = encoder.encode(text); + return new ReadableStream({ + start(controller) { + controller.enqueue(bytes); + controller.close(); + }, + }); +} + +// ── Mock R2BucketService to return a CSV stream ─────────────────────────────── + +function mockR2WithCsv(csv: string) { + vi.mocked(R2BucketService).mockImplementationOnce( + () => + ({ + get: vi.fn().mockResolvedValue({ body: makeReadableStream(csv) }), + }) as any, + ); +} + +function mockR2WithNull() { + vi.mocked(R2BucketService).mockImplementationOnce( + () => + ({ + get: vi.fn().mockResolvedValue(null), + }) as any, + ); +} + +// ── DB helpers ──────────────────────────────────────────────────────────────── + +async function insertJob(jobId: string) { + const db = createDbClient({} as any); + await db.insert(etlJobs).values({ + id: jobId, + status: 'running', + source: 'test', + filename: 'test.csv', + scraperRevision: 'abc123', + startedAt: new Date(), + }); +} + +async function getJob(jobId: string) { + const db = createDbClient({} as any); + const rows = await db.select().from(etlJobs).where(eq(etlJobs.id, jobId)); + return rows[0]; +} + +// minimal env — createDbClient and R2BucketService are both globally mocked +const TEST_ENV = { + NEON_DATABASE_URL: 'postgres://test_user:test_password@localhost:5432/packrat_test', + OPENAI_API_KEY: 'sk-test', + AI_PROVIDER: 'openai', + CLOUDFLARE_ACCOUNT_ID: 'test-account-id', + CLOUDFLARE_AI_GATEWAY_ID: 'test-gateway-id', +} as unknown as Parameters[0]['env']; + +function makeMessage(jobId: string) { + return { id: jobId, data: { objectKey: 'v2/test/test.csv' } }; +} + +// ───────────────────────────────────────────────────────────────────────────── + +describe('processCatalogETL', () => { + it('marks job as completed after processing a valid CSV', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + mockR2WithCsv(makeCsv(5)); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const job = await getJob(jobId); + expect(job?.status, 'job should be completed, not failed').toBe('completed'); + expect(job?.completedAt).not.toBeNull(); + expect(job?.totalProcessed).toBe(5); + }); + + it('writes catalog items to the DB', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + mockR2WithCsv(makeCsv(3)); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const db = createDbClient({} as any); + const [result] = await db.select({ total: count() }).from(catalogItems); + expect(result?.total).toBeGreaterThanOrEqual(3); + }); + + it('marks job as completed when all rows are invalid (writes invalid logs)', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + // Missing sku and productUrl → all rows invalid + mockR2WithCsv('name,brand\nItem Without SKU,TestBrand\n'); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const job = await getJob(jobId); + expect(job?.status, 'job with only invalid rows should still complete').toBe('completed'); + + const db = createDbClient({} as any); + const [logResult] = await db + .select({ total: count() }) + .from(invalidItemLogs) + .where(eq(invalidItemLogs.jobId, jobId)); + expect(logResult?.total).toBeGreaterThan(0); + }); + + it('marks job as failed and rethrows when R2 object is missing', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + mockR2WithNull(); + + await expect( + processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }), + ).rejects.toThrow(); + + const job = await getJob(jobId); + expect(job?.status).toBe('failed'); + }); + + it('handles exactly BATCH_SIZE rows (no remainder flush — edge case)', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + mockR2WithCsv(makeCsv(100)); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const job = await getJob(jobId); + expect(job?.status, 'exact BATCH_SIZE rows should complete').toBe('completed'); + expect(job?.totalProcessed).toBe(100); + }); + + it('handles rows spanning multiple batches', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + mockR2WithCsv(makeCsv(250)); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const job = await getJob(jobId); + expect(job?.status).toBe('completed'); + expect(job?.totalProcessed).toBe(250); + }); + + it('marks job as completed even when items have no weight', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + // No weight column — valid items that previously caused NOT NULL DB failures + mockR2WithCsv( + 'name,sku,productUrl,brand\nNo Weight Item,SKU-NW,https://example.com/nw,TestBrand\n', + ); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const job = await getJob(jobId); + expect(job?.status, 'items without weight should not cause job failure').toBe('completed'); + }); +}); + +describe('processValidItemsBatch', () => { + it('does not throw when embedding generation fails (falls back gracefully)', async () => { + const jobId = crypto.randomUUID(); + const db = createDbClient({} as any); + await db.insert(etlJobs).values({ + id: jobId, + status: 'running', + source: 'test', + filename: 'test.csv', + scraperRevision: 'abc123', + startedAt: new Date(), + }); + + const { generateManyEmbeddings } = await import('@packrat/api/services/embeddingService'); + vi.mocked(generateManyEmbeddings).mockRejectedValueOnce(new Error('OpenAI rate limit')); + + await expect( + processValidItemsBatch({ + jobId, + items: [ + { + name: 'Test Item', + sku: 'SKU-EMBED-001', + productUrl: 'https://example.com/item', + brand: 'TestBrand', + weight: 500, + weightUnit: 'g', + } as any, + ], + env: TEST_ENV, + }), + ).resolves.not.toThrow(); + }); + + it('does not throw when items have no weight', async () => { + const jobId = crypto.randomUUID(); + const db = createDbClient({} as any); + await db.insert(etlJobs).values({ + id: jobId, + status: 'running', + source: 'test', + filename: 'test.csv', + scraperRevision: 'abc123', + startedAt: new Date(), + }); + + await expect( + processValidItemsBatch({ + jobId, + items: [ + { + name: 'No Weight Item', + sku: 'SKU-NW-002', + productUrl: 'https://example.com/nw', + brand: 'TestBrand', + } as any, + ], + env: TEST_ENV, + }), + ).resolves.not.toThrow(); + }); +}); From 60d5be8be2d66627ff177812233a5248f3089348 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Fri, 8 May 2026 19:50:36 -0600 Subject: [PATCH 03/51] fix(db): regenerate migration with drizzle-kit (0037_rich_electro) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the handwritten 0037_nullable_catalog_weight.sql with the drizzle-kit generated equivalent — same ALTER TABLE statements but now tracked in the drizzle journal with the proper snapshot. Social feed table CREATE statements were stripped from the generated output because 0033_social_feed_tables.sql was applied manually and not tracked in the journal, causing drizzle-kit to emit duplicate DDL. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/drizzle/0037_rich_electro.sql | 2 + packages/api/drizzle/meta/0037_snapshot.json | 2070 ++++++++++++++++++ packages/api/drizzle/meta/_journal.json | 7 + 3 files changed, 2079 insertions(+) create mode 100644 packages/api/drizzle/0037_rich_electro.sql create mode 100644 packages/api/drizzle/meta/0037_snapshot.json diff --git a/packages/api/drizzle/0037_rich_electro.sql b/packages/api/drizzle/0037_rich_electro.sql new file mode 100644 index 0000000000..de1571ce17 --- /dev/null +++ b/packages/api/drizzle/0037_rich_electro.sql @@ -0,0 +1,2 @@ +ALTER TABLE "catalog_items" ALTER COLUMN "weight" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "catalog_items" ALTER COLUMN "weight_unit" DROP NOT NULL; diff --git a/packages/api/drizzle/meta/0037_snapshot.json b/packages/api/drizzle/meta/0037_snapshot.json new file mode 100644 index 0000000000..cb2590a262 --- /dev/null +++ b/packages/api/drizzle/meta/0037_snapshot.json @@ -0,0 +1,2070 @@ +{ + "id": "a4b61d9f-bdf1-486a-8331-bcde8ae3c8a0", + "prevId": "fa3d18d1-67a7-488a-aba5-5b18295e80f2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.auth_providers": { + "name": "auth_providers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "auth_providers_user_id_users_id_fk": { + "name": "auth_providers_user_id_users_id_fk", + "tableFrom": "auth_providers", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.catalog_item_etl_jobs": { + "name": "catalog_item_etl_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "etl_job_id": { + "name": "etl_job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "catalog_item_etl_jobs_catalog_item_id_catalog_items_id_fk": { + "name": "catalog_item_etl_jobs_catalog_item_id_catalog_items_id_fk", + "tableFrom": "catalog_item_etl_jobs", + "tableTo": "catalog_items", + "columnsFrom": ["catalog_item_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "catalog_item_etl_jobs_etl_job_id_etl_jobs_id_fk": { + "name": "catalog_item_etl_jobs_etl_job_id_etl_jobs_id_fk", + "tableFrom": "catalog_item_etl_jobs", + "tableTo": "etl_jobs", + "columnsFrom": ["etl_job_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.catalog_items": { + "name": "catalog_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_url": { + "name": "product_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "categories": { + "name": "categories", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "images": { + "name": "images", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rating_value": { + "name": "rating_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "availability": { + "name": "availability", + "type": "availability", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "seller": { + "name": "seller", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_sku": { + "name": "product_sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "material": { + "name": "material", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "condition": { + "name": "condition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "review_count": { + "name": "review_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "variants": { + "name": "variants", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "techs": { + "name": "techs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "reviews": { + "name": "reviews", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "qas": { + "name": "qas", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "faqs": { + "name": "faqs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "embedding_idx": { + "name": "embedding_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "catalog_items_sku_unique": { + "name": "catalog_items_sku_unique", + "nullsNotDistinct": false, + "columns": ["sku"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.comment_likes": { + "name": "comment_likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "comment_id": { + "name": "comment_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "comment_likes_comment_id_post_comments_id_fk": { + "name": "comment_likes_comment_id_post_comments_id_fk", + "tableFrom": "comment_likes", + "tableTo": "post_comments", + "columnsFrom": ["comment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "comment_likes_user_id_users_id_fk": { + "name": "comment_likes_user_id_users_id_fk", + "tableFrom": "comment_likes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "comment_likes_comment_id_user_id_unique": { + "name": "comment_likes_comment_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["comment_id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.etl_jobs": { + "name": "etl_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "status": { + "name": "status", + "type": "etl_job_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_processed": { + "name": "total_processed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_valid": { + "name": "total_valid", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_invalid": { + "name": "total_invalid", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scraper_revision": { + "name": "scraper_revision", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "etl_jobs_scraper_revision_idx": { + "name": "etl_jobs_scraper_revision_idx", + "columns": [ + { + "expression": "scraper_revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invalid_item_logs": { + "name": "invalid_item_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errors": { + "name": "errors", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "raw_data": { + "name": "raw_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "row_index": { + "name": "row_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "invalid_item_logs_job_id_etl_jobs_id_fk": { + "name": "invalid_item_logs_job_id_etl_jobs_id_fk", + "tableFrom": "invalid_item_logs", + "tableTo": "etl_jobs", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.one_time_passwords": { + "name": "one_time_passwords", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar(6)", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "one_time_passwords_user_id_users_id_fk": { + "name": "one_time_passwords_user_id_users_id_fk", + "tableFrom": "one_time_passwords", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pack_items": { + "name": "pack_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consumable": { + "name": "consumable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "worn": { + "name": "worn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_ai_generated": { + "name": "is_ai_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "template_item_id": { + "name": "template_item_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pack_items_embedding_idx": { + "name": "pack_items_embedding_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "pack_items_pack_id_packs_id_fk": { + "name": "pack_items_pack_id_packs_id_fk", + "tableFrom": "pack_items", + "tableTo": "packs", + "columnsFrom": ["pack_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pack_items_catalog_item_id_catalog_items_id_fk": { + "name": "pack_items_catalog_item_id_catalog_items_id_fk", + "tableFrom": "pack_items", + "tableTo": "catalog_items", + "columnsFrom": ["catalog_item_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pack_items_user_id_users_id_fk": { + "name": "pack_items_user_id_users_id_fk", + "tableFrom": "pack_items", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pack_items_template_item_id_pack_template_items_id_fk": { + "name": "pack_items_template_item_id_pack_template_items_id_fk", + "tableFrom": "pack_items", + "tableTo": "pack_template_items", + "columnsFrom": ["template_item_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pack_template_items": { + "name": "pack_template_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consumable": { + "name": "consumable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "worn": { + "name": "worn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pack_template_id": { + "name": "pack_template_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pack_template_items_pack_template_id_pack_templates_id_fk": { + "name": "pack_template_items_pack_template_id_pack_templates_id_fk", + "tableFrom": "pack_template_items", + "tableTo": "pack_templates", + "columnsFrom": ["pack_template_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pack_template_items_catalog_item_id_catalog_items_id_fk": { + "name": "pack_template_items_catalog_item_id_catalog_items_id_fk", + "tableFrom": "pack_template_items", + "tableTo": "catalog_items", + "columnsFrom": ["catalog_item_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pack_template_items_user_id_users_id_fk": { + "name": "pack_template_items_user_id_users_id_fk", + "tableFrom": "pack_template_items", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pack_templates": { + "name": "pack_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_app_template": { + "name": "is_app_template", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_id": { + "name": "content_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pack_templates_user_id_users_id_fk": { + "name": "pack_templates_user_id_users_id_fk", + "tableFrom": "pack_templates", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.weight_history": { + "name": "weight_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "weight_history_user_id_users_id_fk": { + "name": "weight_history_user_id_users_id_fk", + "tableFrom": "weight_history", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "weight_history_pack_id_packs_id_fk": { + "name": "weight_history_pack_id_packs_id_fk", + "tableFrom": "weight_history", + "tableTo": "packs", + "columnsFrom": ["pack_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.packs": { + "name": "packs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_ai_generated": { + "name": "is_ai_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "packs_user_id_users_id_fk": { + "name": "packs_user_id_users_id_fk", + "tableFrom": "packs", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "packs_template_id_pack_templates_id_fk": { + "name": "packs_template_id_pack_templates_id_fk", + "tableFrom": "packs", + "tableTo": "pack_templates", + "columnsFrom": ["template_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.post_comments": { + "name": "post_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_comment_id": { + "name": "parent_comment_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "post_comments_post_id_posts_id_fk": { + "name": "post_comments_post_id_posts_id_fk", + "tableFrom": "post_comments", + "tableTo": "posts", + "columnsFrom": ["post_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "post_comments_user_id_users_id_fk": { + "name": "post_comments_user_id_users_id_fk", + "tableFrom": "post_comments", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "post_comments_parent_comment_id_post_comments_id_fk": { + "name": "post_comments_parent_comment_id_post_comments_id_fk", + "tableFrom": "post_comments", + "tableTo": "post_comments", + "columnsFrom": ["parent_comment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.post_likes": { + "name": "post_likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "post_likes_post_id_posts_id_fk": { + "name": "post_likes_post_id_posts_id_fk", + "tableFrom": "post_likes", + "tableTo": "posts", + "columnsFrom": ["post_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "post_likes_user_id_users_id_fk": { + "name": "post_likes_user_id_users_id_fk", + "tableFrom": "post_likes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "post_likes_post_id_user_id_unique": { + "name": "post_likes_post_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["post_id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.posts": { + "name": "posts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "images": { + "name": "images", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "posts_user_id_users_id_fk": { + "name": "posts_user_id_users_id_fk", + "tableFrom": "posts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.refresh_tokens": { + "name": "refresh_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "replaced_by_token": { + "name": "replaced_by_token", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "refresh_tokens_user_id_users_id_fk": { + "name": "refresh_tokens_user_id_users_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "refresh_tokens_token_unique": { + "name": "refresh_tokens_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reported_content": { + "name": "reported_content", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_query": { + "name": "user_query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_response": { + "name": "ai_response", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_comment": { + "name": "user_comment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "reviewed": { + "name": "reviewed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "reviewed_by": { + "name": "reviewed_by", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "reported_content_user_id_users_id_fk": { + "name": "reported_content_user_id_users_id_fk", + "tableFrom": "reported_content", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reported_content_reviewed_by_users_id_fk": { + "name": "reported_content_reviewed_by_users_id_fk", + "tableFrom": "reported_content", + "tableTo": "users", + "columnsFrom": ["reviewed_by"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trail_condition_reports": { + "name": "trail_condition_reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "trail_name": { + "name": "trail_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trail_region": { + "name": "trail_region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "overall_condition": { + "name": "overall_condition", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hazards": { + "name": "hazards", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "water_crossings": { + "name": "water_crossings", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "water_crossing_difficulty": { + "name": "water_crossing_difficulty", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "photos": { + "name": "photos", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "trip_id": { + "name": "trip_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "trail_condition_reports_user_id_idx": { + "name": "trail_condition_reports_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "trail_condition_reports_active_created_idx": { + "name": "trail_condition_reports_active_created_idx", + "columns": [ + { + "expression": "deleted", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "trail_condition_reports_trail_name_idx": { + "name": "trail_condition_reports_trail_name_idx", + "columns": [ + { + "expression": "trail_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "trail_condition_reports_trip_id_idx": { + "name": "trail_condition_reports_trip_id_idx", + "columns": [ + { + "expression": "trip_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"trail_condition_reports\".\"trip_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "trail_condition_reports_user_id_users_id_fk": { + "name": "trail_condition_reports_user_id_users_id_fk", + "tableFrom": "trail_condition_reports", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "trail_condition_reports_trip_id_trips_id_fk": { + "name": "trail_condition_reports_trip_id_trips_id_fk", + "tableFrom": "trail_condition_reports", + "tableTo": "trips", + "columnsFrom": ["trip_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trips": { + "name": "trips", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_date": { + "name": "start_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "trips_user_id_users_id_fk": { + "name": "trips_user_id_users_id_fk", + "tableFrom": "trips", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "trips_pack_id_packs_id_fk": { + "name": "trips_pack_id_packs_id_fk", + "tableFrom": "trips", + "tableTo": "packs", + "columnsFrom": ["pack_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'USER'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/api/drizzle/meta/_journal.json b/packages/api/drizzle/meta/_journal.json index 70f2f73413..b514164bd9 100644 --- a/packages/api/drizzle/meta/_journal.json +++ b/packages/api/drizzle/meta/_journal.json @@ -267,6 +267,13 @@ "when": 1775883868581, "tag": "0036_typical_zuras", "breakpoints": true + }, + { + "idx": 37, + "version": "7", + "when": 1778291376040, + "tag": "0037_rich_electro", + "breakpoints": true } ] } From f694d960fadcb2c4a9d07d088b4cf647c61d8a7c Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 08:00:42 -0600 Subject: [PATCH 04/51] fix(etl): handle nullable weight/weightUnit when building pack items from catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Making catalog_items.weight/weight_unit nullable caused a TypeScript error in packService.ts — pack items require non-null weight. Fall back to 0/'g' so the AI-generated pack flow still compiles; user can edit after generation. --- packages/api/src/services/packService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/api/src/services/packService.ts b/packages/api/src/services/packService.ts index 44ff2ed716..8aa7bff679 100644 --- a/packages/api/src/services/packService.ts +++ b/packages/api/src/services/packService.ts @@ -150,8 +150,8 @@ export class PackService { catalogItemId: catalogItem.id, name: catalogItem.name, description: catalogItem.description, - weight: catalogItem.weight, - weightUnit: catalogItem.weightUnit, + weight: catalogItem.weight ?? 0, + weightUnit: catalogItem.weightUnit ?? 'g', image: catalogItem.images?.[0], }; }) From 6f84a52e205981552dc1a656f671b2ab319ee07f Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 08:07:43 -0600 Subject: [PATCH 05/51] fix(admin,etl): fix CORS Access-Control-Allow-Origin missing on preflight + regenerate weight migration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CORS: admin scoped cors was silently dropping Access-Control-Allow-Origin on preflight (two stacked cors plugins conflicted — root sets credentials:false/*, admin sets credentials:true/specific-origin, header got dropped). Switch to origin function so Elysia reflects the exact origin back; bypass auth guard for OPTIONS preflights. Fixes admin app CORS errors from https://admin.packratai.com. Migration: regenerate weight/weight_unit nullable migration as 0047 — main merged 0037–0046 after this branch was cut. --- .../api/drizzle/0047_cute_bloodscream.sql | 2 + .../drizzle/meta/0047_cute_bloodscream.json | 2257 +++++++++++++++++ packages/api/drizzle/meta/_journal.json | 7 + packages/api/src/routes/admin/index.ts | 12 +- 4 files changed, 2277 insertions(+), 1 deletion(-) create mode 100644 packages/api/drizzle/0047_cute_bloodscream.sql create mode 100644 packages/api/drizzle/meta/0047_cute_bloodscream.json diff --git a/packages/api/drizzle/0047_cute_bloodscream.sql b/packages/api/drizzle/0047_cute_bloodscream.sql new file mode 100644 index 0000000000..18c813ecf1 --- /dev/null +++ b/packages/api/drizzle/0047_cute_bloodscream.sql @@ -0,0 +1,2 @@ +ALTER TABLE "catalog_items" ALTER COLUMN "weight" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "catalog_items" ALTER COLUMN "weight_unit" DROP NOT NULL; \ No newline at end of file diff --git a/packages/api/drizzle/meta/0047_cute_bloodscream.json b/packages/api/drizzle/meta/0047_cute_bloodscream.json new file mode 100644 index 0000000000..cce3b7f09e --- /dev/null +++ b/packages/api/drizzle/meta/0047_cute_bloodscream.json @@ -0,0 +1,2257 @@ +{ + "id": "1f086d6d-055d-4b37-a5d6-32b1141d2043", + "prevId": "548299d6-dc62-4a37-893b-932e6b7451a1", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_users_id_fk": { + "name": "account_user_id_users_id_fk", + "tableFrom": "account", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "account_provider_account_idx": { + "name": "account_provider_account_idx", + "nullsNotDistinct": false, + "columns": ["provider_id", "account_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.catalog_item_etl_jobs": { + "name": "catalog_item_etl_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "etl_job_id": { + "name": "etl_job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "catalog_item_etl_jobs_catalog_item_id_catalog_items_id_fk": { + "name": "catalog_item_etl_jobs_catalog_item_id_catalog_items_id_fk", + "tableFrom": "catalog_item_etl_jobs", + "tableTo": "catalog_items", + "columnsFrom": ["catalog_item_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "catalog_item_etl_jobs_etl_job_id_etl_jobs_id_fk": { + "name": "catalog_item_etl_jobs_etl_job_id_etl_jobs_id_fk", + "tableFrom": "catalog_item_etl_jobs", + "tableTo": "etl_jobs", + "columnsFrom": ["etl_job_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.catalog_items": { + "name": "catalog_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "product_url": { + "name": "product_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "categories": { + "name": "categories", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "images": { + "name": "images", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "brand": { + "name": "brand", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "model": { + "name": "model", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "rating_value": { + "name": "rating_value", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "color": { + "name": "color", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "price": { + "name": "price", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "availability": { + "name": "availability", + "type": "availability", + "typeSchema": "public", + "primaryKey": false, + "notNull": false + }, + "seller": { + "name": "seller", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "product_sku": { + "name": "product_sku", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "material": { + "name": "material", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "currency": { + "name": "currency", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "condition": { + "name": "condition", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "review_count": { + "name": "review_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "variants": { + "name": "variants", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "techs": { + "name": "techs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "links": { + "name": "links", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "reviews": { + "name": "reviews", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "qas": { + "name": "qas", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "faqs": { + "name": "faqs", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "embedding_idx": { + "name": "embedding_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "catalog_items_sku_unique": { + "name": "catalog_items_sku_unique", + "nullsNotDistinct": false, + "columns": ["sku"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.comment_likes": { + "name": "comment_likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "comment_id": { + "name": "comment_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "comment_likes_comment_id_post_comments_id_fk": { + "name": "comment_likes_comment_id_post_comments_id_fk", + "tableFrom": "comment_likes", + "tableTo": "post_comments", + "columnsFrom": ["comment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "comment_likes_user_id_users_id_fk": { + "name": "comment_likes_user_id_users_id_fk", + "tableFrom": "comment_likes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "comment_likes_comment_id_user_id_unique": { + "name": "comment_likes_comment_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["comment_id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.etl_jobs": { + "name": "etl_jobs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "status": { + "name": "status", + "type": "etl_job_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filename": { + "name": "filename", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "started_at": { + "name": "started_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "total_processed": { + "name": "total_processed", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_valid": { + "name": "total_valid", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "total_invalid": { + "name": "total_invalid", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "scraper_revision": { + "name": "scraper_revision", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "etl_jobs_scraper_revision_idx": { + "name": "etl_jobs_scraper_revision_idx", + "columns": [ + { + "expression": "scraper_revision", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invalid_item_logs": { + "name": "invalid_item_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errors": { + "name": "errors", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "raw_data": { + "name": "raw_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "row_index": { + "name": "row_index", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "invalid_item_logs_job_id_etl_jobs_id_fk": { + "name": "invalid_item_logs_job_id_etl_jobs_id_fk", + "tableFrom": "invalid_item_logs", + "tableTo": "etl_jobs", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.jwks": { + "name": "jwks", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pack_items": { + "name": "pack_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consumable": { + "name": "consumable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "worn": { + "name": "worn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_ai_generated": { + "name": "is_ai_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "template_item_id": { + "name": "template_item_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "embedding": { + "name": "embedding", + "type": "vector(1536)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pack_items_embedding_idx": { + "name": "pack_items_embedding_idx", + "columns": [ + { + "expression": "embedding", + "isExpression": false, + "asc": true, + "nulls": "last", + "opclass": "vector_cosine_ops" + } + ], + "isUnique": false, + "concurrently": false, + "method": "hnsw", + "with": {} + } + }, + "foreignKeys": { + "pack_items_pack_id_packs_id_fk": { + "name": "pack_items_pack_id_packs_id_fk", + "tableFrom": "pack_items", + "tableTo": "packs", + "columnsFrom": ["pack_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pack_items_catalog_item_id_catalog_items_id_fk": { + "name": "pack_items_catalog_item_id_catalog_items_id_fk", + "tableFrom": "pack_items", + "tableTo": "catalog_items", + "columnsFrom": ["catalog_item_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pack_items_user_id_users_id_fk": { + "name": "pack_items_user_id_users_id_fk", + "tableFrom": "pack_items", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pack_items_template_item_id_pack_template_items_id_fk": { + "name": "pack_items_template_item_id_pack_template_items_id_fk", + "tableFrom": "pack_items", + "tableTo": "pack_template_items", + "columnsFrom": ["template_item_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pack_template_items": { + "name": "pack_template_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "weight_unit": { + "name": "weight_unit", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "consumable": { + "name": "consumable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "worn": { + "name": "worn", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "pack_template_id": { + "name": "pack_template_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "catalog_item_id": { + "name": "catalog_item_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pack_template_items_pack_template_id_pack_templates_id_fk": { + "name": "pack_template_items_pack_template_id_pack_templates_id_fk", + "tableFrom": "pack_template_items", + "tableTo": "pack_templates", + "columnsFrom": ["pack_template_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pack_template_items_catalog_item_id_catalog_items_id_fk": { + "name": "pack_template_items_catalog_item_id_catalog_items_id_fk", + "tableFrom": "pack_template_items", + "tableTo": "catalog_items", + "columnsFrom": ["catalog_item_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "pack_template_items_user_id_users_id_fk": { + "name": "pack_template_items_user_id_users_id_fk", + "tableFrom": "pack_template_items", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pack_templates": { + "name": "pack_templates", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "is_app_template": { + "name": "is_app_template", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_id": { + "name": "content_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "pack_templates_user_id_users_id_fk": { + "name": "pack_templates_user_id_users_id_fk", + "tableFrom": "pack_templates", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.weight_history": { + "name": "weight_history", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": true + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "weight_history_user_id_users_id_fk": { + "name": "weight_history_user_id_users_id_fk", + "tableFrom": "weight_history", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "weight_history_pack_id_packs_id_fk": { + "name": "weight_history_pack_id_packs_id_fk", + "tableFrom": "weight_history", + "tableTo": "packs", + "columnsFrom": ["pack_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.packs": { + "name": "packs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_public": { + "name": "is_public", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tags": { + "name": "tags", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_ai_generated": { + "name": "is_ai_generated", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "packs_user_id_users_id_fk": { + "name": "packs_user_id_users_id_fk", + "tableFrom": "packs", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "packs_template_id_pack_templates_id_fk": { + "name": "packs_template_id_pack_templates_id_fk", + "tableFrom": "packs", + "tableTo": "pack_templates", + "columnsFrom": ["template_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.post_comments": { + "name": "post_comments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_comment_id": { + "name": "parent_comment_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "post_comments_post_id_posts_id_fk": { + "name": "post_comments_post_id_posts_id_fk", + "tableFrom": "post_comments", + "tableTo": "posts", + "columnsFrom": ["post_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "post_comments_user_id_users_id_fk": { + "name": "post_comments_user_id_users_id_fk", + "tableFrom": "post_comments", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "post_comments_parent_comment_id_post_comments_id_fk": { + "name": "post_comments_parent_comment_id_post_comments_id_fk", + "tableFrom": "post_comments", + "tableTo": "post_comments", + "columnsFrom": ["parent_comment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.post_likes": { + "name": "post_likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "post_id": { + "name": "post_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "post_likes_post_id_posts_id_fk": { + "name": "post_likes_post_id_posts_id_fk", + "tableFrom": "post_likes", + "tableTo": "posts", + "columnsFrom": ["post_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "post_likes_user_id_users_id_fk": { + "name": "post_likes_user_id_users_id_fk", + "tableFrom": "post_likes", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "post_likes_post_id_user_id_unique": { + "name": "post_likes_post_id_user_id_unique", + "nullsNotDistinct": false, + "columns": ["post_id", "user_id"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.posts": { + "name": "posts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "caption": { + "name": "caption", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "images": { + "name": "images", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "posts_user_id_users_id_fk": { + "name": "posts_user_id_users_id_fk", + "tableFrom": "posts", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reported_content": { + "name": "reported_content", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_query": { + "name": "user_query", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "ai_response": { + "name": "ai_response", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_comment": { + "name": "user_comment", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "reviewed": { + "name": "reviewed", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "reviewed_by": { + "name": "reviewed_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reviewed_at": { + "name": "reviewed_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "reported_content_user_id_users_id_fk": { + "name": "reported_content_user_id_users_id_fk", + "tableFrom": "reported_content", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "reported_content_reviewed_by_users_id_fk": { + "name": "reported_content_reviewed_by_users_id_fk", + "tableFrom": "reported_content", + "tableTo": "users", + "columnsFrom": ["reviewed_by"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_users_id_fk": { + "name": "session_user_id_users_id_fk", + "tableFrom": "session", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trail_condition_reports": { + "name": "trail_condition_reports", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "trail_name": { + "name": "trail_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trail_region": { + "name": "trail_region", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "surface": { + "name": "surface", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "overall_condition": { + "name": "overall_condition", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "hazards": { + "name": "hazards", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "water_crossings": { + "name": "water_crossings", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "water_crossing_difficulty": { + "name": "water_crossing_difficulty", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "photos": { + "name": "photos", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'::jsonb" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "trip_id": { + "name": "trip_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "trail_condition_reports_user_id_idx": { + "name": "trail_condition_reports_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "trail_condition_reports_active_created_idx": { + "name": "trail_condition_reports_active_created_idx", + "columns": [ + { + "expression": "deleted", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": false, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "trail_condition_reports_trail_name_idx": { + "name": "trail_condition_reports_trail_name_idx", + "columns": [ + { + "expression": "trail_name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "trail_condition_reports_trip_id_idx": { + "name": "trail_condition_reports_trip_id_idx", + "columns": [ + { + "expression": "trip_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "where": "\"trail_condition_reports\".\"trip_id\" IS NOT NULL", + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "trail_condition_reports_user_id_users_id_fk": { + "name": "trail_condition_reports_user_id_users_id_fk", + "tableFrom": "trail_condition_reports", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "trail_condition_reports_trip_id_trips_id_fk": { + "name": "trail_condition_reports_trip_id_trips_id_fk", + "tableFrom": "trail_condition_reports", + "tableTo": "trips", + "columnsFrom": ["trip_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trips": { + "name": "trips", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start_date": { + "name": "start_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "end_date": { + "name": "end_date", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "location": { + "name": "location", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "notes": { + "name": "notes", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "trail_osm_id": { + "name": "trail_osm_id", + "type": "bigint", + "primaryKey": false, + "notNull": false + }, + "local_created_at": { + "name": "local_created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "local_updated_at": { + "name": "local_updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "deleted": { + "name": "deleted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "trips_user_id_users_id_fk": { + "name": "trips_user_id_users_id_fk", + "tableFrom": "trips", + "tableTo": "users", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "trips_pack_id_packs_id_fk": { + "name": "trips_pack_id_packs_id_fk", + "tableFrom": "trips", + "tableTo": "packs", + "columnsFrom": ["pack_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'USER'" + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "users_email_unique": { + "name": "users_email_unique", + "nullsNotDistinct": false, + "columns": ["email"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/api/drizzle/meta/_journal.json b/packages/api/drizzle/meta/_journal.json index af35b27774..ca463a5058 100644 --- a/packages/api/drizzle/meta/_journal.json +++ b/packages/api/drizzle/meta/_journal.json @@ -330,6 +330,13 @@ "when": 1777803600000, "tag": "0046_social_feed_tables_uuid", "breakpoints": true + }, + { + "idx": 46, + "version": "7", + "when": 1778594728740, + "tag": "0047_cute_bloodscream", + "breakpoints": true } ] } diff --git a/packages/api/src/routes/admin/index.ts b/packages/api/src/routes/admin/index.ts index d40d9db318..0ab63aeac9 100644 --- a/packages/api/src/routes/admin/index.ts +++ b/packages/api/src/routes/admin/index.ts @@ -117,7 +117,16 @@ export const adminRoutes = new Elysia({ prefix: '/admin' }) // is rejected by browsers per the CORS spec). .use( cors({ - origin: 'https://admin.packratai.com', + // With credentials:true the browser requires a specific origin (not *). + // Reflect origin back when it's in our allowlist. + origin: (request) => { + const origin = request.headers.get('origin'); + if (!origin) return false; + if (origin === 'https://admin.packratai.com') return true; + if (origin.endsWith('.workers.dev')) return true; + if (/^https?:\/\/localhost(:\d+)?$/.test(origin)) return true; + return false; + }, credentials: true, allowedHeaders: ['Authorization', 'Content-Type'], }), @@ -172,6 +181,7 @@ export const adminRoutes = new Elysia({ prefix: '/admin' }) ) .onBeforeHandle(async ({ request, path }) => { if (path === '/api/admin/token') return; + if (request.method === 'OPTIONS') return; const ok = await adminAuthGuard(request); if (!ok) return status(401, { error: 'Unauthorized' }); }) From a28c16c42a12f9f34f1e824b6739b8e3059cb481 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 08:57:06 -0600 Subject: [PATCH 06/51] fix(types): resolve TypeScript errors blocking CI checks job - admin api.ts: double-cast Eden Treaty responses to PaginatedResponse (treaty infers wide union types that don't overlap with the interface) - packages/app user queries: remove deleted auth hooks (useLoginMutation, useRegisterMutation) that called Better Auth routes not in Elysia; redirect useCurrentUser to client.user.profile.get() - apps/web auth page: implement login/register locally via Better Auth REST endpoints instead of importing from @packrat/app - apps/trails useAuth: replace (apiClient as any).auth.* calls with typed trailsAuthClient (createAuthClient from better-auth/react); add auth-client.ts - apps/trails UserInfoSchema: id is UUID string not number (Better Auth) - apps/web types: Post.userId and PostAuthor.id are UUID strings not numbers - apps/web data.ts: update mock post IDs to match string type Co-Authored-By: Claude --- apps/admin/lib/api.ts | 12 ++- apps/admin/lib/queryKeys.ts | 7 ++ apps/trails/components/AuthGate.tsx | 11 ++- apps/trails/lib/auth-client.ts | 8 ++ apps/trails/lib/auth.ts | 2 +- apps/trails/lib/useAuth.tsx | 89 ++++++++++++----------- apps/web/app/auth/page.tsx | 47 ++++++++++-- apps/web/lib/data.ts | 20 ++--- apps/web/lib/types.ts | 4 +- packages/app/src/entities/user/index.ts | 2 - packages/app/src/entities/user/queries.ts | 29 +------- 11 files changed, 135 insertions(+), 96 deletions(-) create mode 100644 apps/trails/lib/auth-client.ts diff --git a/apps/admin/lib/api.ts b/apps/admin/lib/api.ts index e86364752f..19fd3ce416 100644 --- a/apps/admin/lib/api.ts +++ b/apps/admin/lib/api.ts @@ -103,7 +103,7 @@ export async function getUsers({ query: { limit, offset, q, includeDeleted: includeDeleted ? 'true' : undefined }, }); if (error) throwOnError(error); - return unwrap(data, 'users'); + return unwrap(data, 'users') as unknown as PaginatedResponse; } export async function deleteUser(id: number): Promise<{ success: boolean }> { @@ -158,7 +158,7 @@ export async function getPacks({ query: { limit, offset, q, includeDeleted: includeDeleted ? 'true' : undefined }, }); if (error) throwOnError(error); - return unwrap(data, 'packs'); + return unwrap(data, 'packs') as unknown as PaginatedResponse; } export async function deletePack(id: string): Promise<{ success: boolean }> { @@ -219,7 +219,7 @@ export async function getCatalogItems({ query: { limit, offset, q }, }); if (error) throwOnError(error); - return unwrap(data, 'catalog'); + return unwrap(data, 'catalog') as unknown as PaginatedResponse; } export async function deleteCatalogItem(id: number): Promise<{ success: boolean }> { @@ -377,6 +377,12 @@ export async function deleteTrailCondition(reportId: string): Promise<{ success: return unwrap(data, 'deleteTrailCondition'); } +async function adminFetch(path: string, init?: RequestInit): Promise { + const res = await adminFetcher(`${API_BASE}/api/admin${path}`, init); + if (!res.ok) throw new Error(`Admin API error: ${res.status}`); + return res.json() as Promise; +} + export function resetStuckEtlJobs(): Promise<{ reset: number; ids: string[] }> { return adminFetch('/analytics/catalog/etl/reset-stuck', { method: 'POST' }); } diff --git a/apps/admin/lib/queryKeys.ts b/apps/admin/lib/queryKeys.ts index 54d83d9626..ef1038b37d 100644 --- a/apps/admin/lib/queryKeys.ts +++ b/apps/admin/lib/queryKeys.ts @@ -37,6 +37,13 @@ export const queryKeys = { breakdown: () => [...queryKeys.platform.all(), 'breakdown'] as const, }, + osm: { + all: () => ['osm'] as const, + search: (q?: string, sport?: string) => [...queryKeys.osm.all(), 'search', q, sport] as const, + trail: (osmId: string) => [...queryKeys.osm.all(), 'trail', osmId] as const, + conditions: (q?: string) => [...queryKeys.osm.all(), 'conditions', q] as const, + }, + catalogAnalytics: { all: () => ['catalogAnalytics'] as const, overview: () => [...queryKeys.catalogAnalytics.all(), 'overview'] as const, diff --git a/apps/trails/components/AuthGate.tsx b/apps/trails/components/AuthGate.tsx index 0967e46c7f..be1718de6d 100644 --- a/apps/trails/components/AuthGate.tsx +++ b/apps/trails/components/AuthGate.tsx @@ -16,7 +16,7 @@ import { Loader2 } from 'lucide-react'; import { useState } from 'react'; import { toast } from 'sonner'; import { VerifyEmail } from 'trails-app/components/VerifyEmail'; -import { apiClient } from 'trails-app/lib/apiClient'; +import { trailsAuthClient } from 'trails-app/lib/auth-client'; import { useAuth } from 'trails-app/lib/useAuth'; const TABS = ['register', 'login', 'forgot'] as const; @@ -77,7 +77,14 @@ export function AuthGate() { e.preventDefault(); setLoading(true); try { - await apiClient.auth['forgot-password'].post({ email: forgotEmail }); + const { error } = await trailsAuthClient.requestPasswordReset({ + email: forgotEmail, + redirectTo: + typeof window !== 'undefined' + ? `${window.location.origin}/reset-password` + : '/reset-password', + }); + if (error) throw new Error(error.message); setForgotSent(true); } catch { toast.error('Could not send reset email. Try again.'); diff --git a/apps/trails/lib/auth-client.ts b/apps/trails/lib/auth-client.ts new file mode 100644 index 0000000000..a12b75ffae --- /dev/null +++ b/apps/trails/lib/auth-client.ts @@ -0,0 +1,8 @@ +'use client'; + +import { createAuthClient } from 'better-auth/react'; +import { trailsEnv } from 'trails-app/lib/env'; + +export const trailsAuthClient = createAuthClient({ + baseURL: trailsEnv.NEXT_PUBLIC_API_URL, +}); diff --git a/apps/trails/lib/auth.ts b/apps/trails/lib/auth.ts index 11198e198a..88cc129beb 100644 --- a/apps/trails/lib/auth.ts +++ b/apps/trails/lib/auth.ts @@ -39,7 +39,7 @@ export function clearTokens(): void { } export const UserInfoSchema = z.object({ - id: z.number(), + id: z.string(), email: z.string(), firstName: z.string().nullish(), lastName: z.string().nullish(), diff --git a/apps/trails/lib/useAuth.tsx b/apps/trails/lib/useAuth.tsx index 3b7bf4abef..ef4c8b473f 100644 --- a/apps/trails/lib/useAuth.tsx +++ b/apps/trails/lib/useAuth.tsx @@ -1,30 +1,28 @@ 'use client'; -import { asStringRecord, fromZod } from '@packrat/guards'; +import { fromZod } from '@packrat/guards'; import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import { apiClient } from 'trails-app/lib/apiClient'; import { clearTokens, clearUser, getAccessToken, - getRefreshToken, getUser, setTokens, setUser, type UserInfo, UserInfoSchema, } from 'trails-app/lib/auth'; +import { trailsAuthClient } from 'trails-app/lib/auth-client'; interface AuthState { isAuthed: boolean; user: UserInfo | null; - // Pending verification: user registered but hasn't verified email yet pendingEmail: string | null; } interface AuthActions { register(email: string, opts: { password: string; firstName?: string }): Promise; - verifyEmail(otp: string): Promise; + verifyEmail(token: string): Promise; resendVerification(): Promise; login(email: string, password: string): Promise; logout(): Promise; @@ -35,10 +33,19 @@ interface AuthActions { const AuthContext = createContext<(AuthState & AuthActions) | null>(null); -function apiError(error: unknown, fallback: string): Error { - const rec = asStringRecord(error); - const msg = rec.error ?? rec.message; - return new Error(msg ?? fallback); +function parseAuthUser(user: { + id: string; + email: string; + [key: string]: unknown; +}): UserInfo | null { + return ( + fromZod(UserInfoSchema)({ + id: user.id, + email: user.email, + firstName: (user.firstName as string | null | undefined) ?? null, + lastName: (user.lastName as string | null | undefined) ?? null, + }) ?? null + ); } export function AuthProvider({ children }: { children: React.ReactNode }) { @@ -60,32 +67,40 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const register = useCallback( async (email: string, opts: { password: string; firstName?: string }) => { - const { error, status } = await apiClient.auth.register.post({ + const name = opts.firstName ?? email; + const { data, error } = await trailsAuthClient.signUp.email({ email, password: opts.password, - firstName: opts.firstName, + name, }); - if (error) throw apiError(error.value, `Registration failed: ${status}`); - setState((s) => ({ ...s, pendingEmail: email })); + if (error) throw new Error(error.message ?? 'Registration failed'); + if (data?.token) { + // autoSignIn: true succeeded — token is the Bearer session token + const parsedUser = parseAuthUser(data.user as Parameters[0]); + if (!parsedUser) throw new Error('Registration failed: unexpected user shape'); + setTokens(data.token, ''); + setUser(parsedUser); + setState({ isAuthed: true, user: parsedUser, pendingEmail: null }); + setAuthGateOpen(false); + } else { + setState((s) => ({ ...s, pendingEmail: email })); + } }, [], ); const verifyEmail = useCallback( - async (otp: string) => { + async (token: string) => { if (!state.pendingEmail) throw new Error('No pending email verification'); - const { data, error, status } = await apiClient.auth['verify-email'].post({ - email: state.pendingEmail, - code: otp, - }); - if (error || !data) throw apiError(error?.value, `Verification failed: ${status}`); - const { accessToken, refreshToken, user } = data; - if (!accessToken || !refreshToken || !user) { - throw new Error('Verification failed: missing token data'); + const { error } = await trailsAuthClient.verifyEmail({ query: { token } }); + if (error) throw new Error(error.message ?? 'Verification failed'); + const sessionRes = await trailsAuthClient.getSession(); + if (!sessionRes.data?.session || !sessionRes.data.user) { + throw new Error('Verification failed: could not get session'); } - const parsedUser = fromZod(UserInfoSchema)(user); + const parsedUser = parseAuthUser(sessionRes.data.user as Parameters[0]); if (!parsedUser) throw new Error('Verification failed: unexpected user shape'); - setTokens(accessToken, refreshToken); + setTokens(sessionRes.data.session.token, ''); setUser(parsedUser); setState({ isAuthed: true, user: parsedUser, pendingEmail: null }); setAuthGateOpen(false); @@ -95,36 +110,26 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { const resendVerification = useCallback(async () => { if (!state.pendingEmail) throw new Error('No pending email'); - const { error, status } = await apiClient.auth['resend-verification'].post({ + const { error } = await trailsAuthClient.sendVerificationEmail({ email: state.pendingEmail, + callbackURL: typeof window !== 'undefined' ? window.location.origin : '', }); - if (error) throw apiError(error.value, `Resend failed: ${status}`); + if (error) throw new Error(error.message ?? 'Resend failed'); }, [state.pendingEmail]); const login = useCallback(async (email: string, password: string) => { - const { data, error, status } = await apiClient.auth.login.post({ email, password }); - if (error || !data) throw apiError(error?.value, `Login failed: ${status}`); - const { accessToken, refreshToken, user } = data; - if (!accessToken || !refreshToken || !user) { - throw new Error('Login failed: missing token data'); - } - const parsedUser = fromZod(UserInfoSchema)(user); + const { data, error } = await trailsAuthClient.signIn.email({ email, password }); + if (error || !data) throw new Error(error?.message ?? 'Login failed'); + const parsedUser = parseAuthUser(data.user as Parameters[0]); if (!parsedUser) throw new Error('Login failed: unexpected user shape'); - setTokens(accessToken, refreshToken); + setTokens(data.token, ''); setUser(parsedUser); setState({ isAuthed: true, user: parsedUser, pendingEmail: null }); setAuthGateOpen(false); }, []); const logout = useCallback(async () => { - const refreshToken = getRefreshToken(); - if (refreshToken) { - try { - await apiClient.auth.logout.post({ refreshToken }); - } catch { - // ignore — clear tokens regardless - } - } + await trailsAuthClient.signOut(); clearTokens(); clearUser(); setState({ isAuthed: false, user: null, pendingEmail: null }); diff --git a/apps/web/app/auth/page.tsx b/apps/web/app/auth/page.tsx index 400794c569..737937334b 100644 --- a/apps/web/app/auth/page.tsx +++ b/apps/web/app/auth/page.tsx @@ -1,10 +1,47 @@ 'use client'; -import { useLoginMutation, useRegisterMutation } from '@packrat/app'; +import { webEnv } from '@packrat/env/web'; +import { useMutation } from '@tanstack/react-query'; import { useRouter } from 'next/navigation'; import type React from 'react'; import { useState } from 'react'; import { setTokens } from 'web-app/lib/auth'; +const API_BASE = webEnv.NEXT_PUBLIC_API_URL ?? 'http://localhost:8787'; + +function useLoginMutation() { + return useMutation({ + mutationFn: async (body: { email: string; password: string }) => { + const res = await fetch(`${API_BASE}/api/auth/sign-in/email`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + if (!res.ok) throw new Error('Login failed'); + return res.json() as Promise<{ token?: string; user?: unknown }>; + }, + }); +} + +function useRegisterMutation() { + return useMutation({ + mutationFn: async (body: { + email: string; + password: string; + firstName?: string; + lastName?: string; + }) => { + const name = [body.firstName, body.lastName].filter(Boolean).join(' ') || body.email; + const res = await fetch(`${API_BASE}/api/auth/sign-up/email`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: body.email, password: body.password, name }), + }); + if (!res.ok) throw new Error('Registration failed'); + return res.json() as Promise; + }, + }); +} + export default function AuthPage() { const [tab, setTab] = useState<'login' | 'register'>('login'); const [email, setEmail] = useState(''); @@ -23,11 +60,9 @@ export default function AuthPage() { { email, password }, { onSuccess: (data) => { - const response = data as { accessToken?: string; refreshToken?: string } | null; - const accessToken = response?.accessToken ?? ''; - const refreshToken = response?.refreshToken ?? ''; - if (!accessToken) return; - setTokens(accessToken, refreshToken); + const token = (data as { token?: string }).token ?? ''; + if (!token) return; + setTokens(token, ''); router.push('/'); }, }, diff --git a/apps/web/lib/data.ts b/apps/web/lib/data.ts index a6f66b6df7..9948e024bf 100644 --- a/apps/web/lib/data.ts +++ b/apps/web/lib/data.ts @@ -1031,65 +1031,65 @@ export const mockCatalogItems: CatalogItem[] = [ export const mockPosts: Post[] = [ { id: 1, - userId: 2, + userId: '2', caption: 'Finally dialed in my SoCal desert PCT setup. 6.5lb base weight! The key was switching to a tarp and going stoveless for the desert section.', images: [], createdAt: '2024-07-28T14:30:00Z', updatedAt: '2024-07-28T14:30:00Z', - author: { id: 2, firstName: 'Sarah', lastName: 'Chen' }, + author: { id: '2', firstName: 'Sarah', lastName: 'Chen' }, likeCount: 142, commentCount: 23, likedByMe: false, }, { id: 2, - userId: 3, + userId: '3', caption: 'Wind River High Route gear list. This is what 4000+ miles of thru-hiking has taught me. Every gram has been earned.', images: [], createdAt: '2024-07-25T09:15:00Z', updatedAt: '2024-07-25T09:15:00Z', - author: { id: 3, firstName: 'Jake', lastName: 'Morrison' }, + author: { id: '3', firstName: 'Jake', lastName: 'Morrison' }, likeCount: 89, commentCount: 11, likedByMe: true, }, { id: 3, - userId: 4, + userId: '4', caption: 'AT weekend basecamp setup. Not the lightest but extremely comfortable for section hiking with my kids.', images: [], createdAt: '2024-07-22T16:45:00Z', updatedAt: '2024-07-22T16:45:00Z', - author: { id: 4, firstName: 'Maria', lastName: 'Santos' }, + author: { id: '4', firstName: 'Maria', lastName: 'Santos' }, likeCount: 54, commentCount: 7, likedByMe: false, }, { id: 4, - userId: 5, + userId: '5', caption: 'Wonderland Trail FKT attempt kit. Shaved 2 lbs from last year. Going for sub-24 hours this time.', images: [], createdAt: '2024-07-20T11:00:00Z', updatedAt: '2024-07-20T11:00:00Z', - author: { id: 5, firstName: 'Alex', lastName: 'Park' }, + author: { id: '5', firstName: 'Alex', lastName: 'Park' }, likeCount: 211, commentCount: 38, likedByMe: true, }, { id: 5, - userId: 6, + userId: '6', caption: "Zion Narrows overnighter loadout. Waterproof everything! Learned from experience that the river doesn't care about your gear.", images: [], createdAt: '2024-07-18T08:30:00Z', updatedAt: '2024-07-18T08:30:00Z', - author: { id: 6, firstName: 'Jordan', lastName: 'Kim' }, + author: { id: '6', firstName: 'Jordan', lastName: 'Kim' }, likeCount: 77, commentCount: 14, likedByMe: false, diff --git a/apps/web/lib/types.ts b/apps/web/lib/types.ts index 45aa9ae1b4..cc008a5872 100644 --- a/apps/web/lib/types.ts +++ b/apps/web/lib/types.ts @@ -147,14 +147,14 @@ export type CatalogListResponse = { // ── Feed ───────────────────────────────────────────────────────────────────── export type PostAuthor = { - id: number; + id: string; firstName: string | null; lastName: string | null; }; export type Post = { id: number; - userId: number; + userId: string; caption: string | null; images: string[]; // array of image URLs createdAt: string; diff --git a/packages/app/src/entities/user/index.ts b/packages/app/src/entities/user/index.ts index 0495808d5b..5ab0d89883 100644 --- a/packages/app/src/entities/user/index.ts +++ b/packages/app/src/entities/user/index.ts @@ -1,7 +1,5 @@ export { useCurrentUser, - useLoginMutation, - useRegisterMutation, useUpdateProfileMutation, useUserProfile, } from './queries'; diff --git a/packages/app/src/entities/user/queries.ts b/packages/app/src/entities/user/queries.ts index 66d773a5e4..d621048b60 100644 --- a/packages/app/src/entities/user/queries.ts +++ b/packages/app/src/entities/user/queries.ts @@ -6,7 +6,7 @@ export function useCurrentUser() { return useQuery({ queryKey: queryKeys.user, queryFn: async () => { - const { data, error } = await client.auth.me.get(); + const { data, error } = await client.user.profile.get(); if (error) throw new Error('Failed to fetch current user'); return data; }, @@ -25,33 +25,6 @@ export function useUserProfile() { }); } -export function useLoginMutation() { - const client = useApiClient(); - return useMutation({ - mutationFn: async (body: { email: string; password: string }) => { - const { data, error } = await client.auth.login.post(body); - if (error) throw new Error('Login failed'); - return data; - }, - }); -} - -export function useRegisterMutation() { - const client = useApiClient(); - return useMutation({ - mutationFn: async (body: { - email: string; - password: string; - firstName?: string; - lastName?: string; - }) => { - const { data, error } = await client.auth.register.post(body); - if (error) throw new Error('Registration failed'); - return data; - }, - }); -} - interface UpdateProfileInput { firstName?: string; lastName?: string; From fdbc495fcafae839d60aa212f32a8b171924da3a Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 08:59:42 -0600 Subject: [PATCH 07/51] fix(types): annotate safe-casts and remove redundant Promise casts - admin api.ts: add safe-cast annotations on Eden Treaty PaginatedResponse double casts; res.json() returns Promise so no explicit cast needed - web auth page: drop superfluous `as Promise` on res.json() Co-Authored-By: Claude --- apps/admin/lib/api.ts | 8 ++++---- apps/web/app/auth/page.tsx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/admin/lib/api.ts b/apps/admin/lib/api.ts index 19fd3ce416..040075d7dd 100644 --- a/apps/admin/lib/api.ts +++ b/apps/admin/lib/api.ts @@ -103,7 +103,7 @@ export async function getUsers({ query: { limit, offset, q, includeDeleted: includeDeleted ? 'true' : undefined }, }); if (error) throwOnError(error); - return unwrap(data, 'users') as unknown as PaginatedResponse; + return unwrap(data, 'users') as unknown as PaginatedResponse; // safe-cast: Eden Treaty infers wide union; runtime shape matches PaginatedResponse } export async function deleteUser(id: number): Promise<{ success: boolean }> { @@ -158,7 +158,7 @@ export async function getPacks({ query: { limit, offset, q, includeDeleted: includeDeleted ? 'true' : undefined }, }); if (error) throwOnError(error); - return unwrap(data, 'packs') as unknown as PaginatedResponse; + return unwrap(data, 'packs') as unknown as PaginatedResponse; // safe-cast: Eden Treaty infers wide union; runtime shape matches PaginatedResponse } export async function deletePack(id: string): Promise<{ success: boolean }> { @@ -219,7 +219,7 @@ export async function getCatalogItems({ query: { limit, offset, q }, }); if (error) throwOnError(error); - return unwrap(data, 'catalog') as unknown as PaginatedResponse; + return unwrap(data, 'catalog') as unknown as PaginatedResponse; // safe-cast: Eden Treaty infers wide union; runtime shape matches PaginatedResponse } export async function deleteCatalogItem(id: number): Promise<{ success: boolean }> { @@ -380,7 +380,7 @@ export async function deleteTrailCondition(reportId: string): Promise<{ success: async function adminFetch(path: string, init?: RequestInit): Promise { const res = await adminFetcher(`${API_BASE}/api/admin${path}`, init); if (!res.ok) throw new Error(`Admin API error: ${res.status}`); - return res.json() as Promise; + return res.json(); } export function resetStuckEtlJobs(): Promise<{ reset: number; ids: string[] }> { diff --git a/apps/web/app/auth/page.tsx b/apps/web/app/auth/page.tsx index 737937334b..d47266cf9d 100644 --- a/apps/web/app/auth/page.tsx +++ b/apps/web/app/auth/page.tsx @@ -37,7 +37,7 @@ function useRegisterMutation() { body: JSON.stringify({ email: body.email, password: body.password, name }), }); if (!res.ok) throw new Error('Registration failed'); - return res.json() as Promise; + return res.json(); }, }); } From 90b4999b9ad4fbda32d8aae850a4491575c69097 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 09:31:49 -0600 Subject: [PATCH 08/51] fix(expo/auth): fix TS errors blocking CI checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - UserSchema.id: z.number() → z.string() (Better Auth uses UUID strings; aligns with Drizzle schema's text('id') PK) - mapToUser/applySessionUser: add missing emailVerified/createdAt/updatedAt fields; replace `as` casts with asString/asBoolean guards from @packrat/guards - useAuthActions signInWithGoogle: replace dead apiClient/setToken/UserSchema references with authClient.signIn.social - ItemReviews: guard nullable review.title/text/date per CatalogItemSchema --- .../features/auth/hooks/useAuthActions.ts | 31 +++++++++---------- apps/expo/features/auth/hooks/useAuthInit.ts | 21 +++++++------ .../catalog/components/ItemReviews.tsx | 18 +++++------ packages/api/src/schemas/users.ts | 2 +- 4 files changed, 36 insertions(+), 36 deletions(-) diff --git a/apps/expo/features/auth/hooks/useAuthActions.ts b/apps/expo/features/auth/hooks/useAuthActions.ts index 6cfefe8819..95ed336fae 100644 --- a/apps/expo/features/auth/hooks/useAuthActions.ts +++ b/apps/expo/features/auth/hooks/useAuthActions.ts @@ -1,3 +1,4 @@ +import { asBoolean, asString } from '@packrat/guards'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { GoogleSignin, @@ -32,15 +33,18 @@ function redirect(route: string) { } function mapToUser(raw: Record): User { - const name = String(raw.name ?? ''); + const name = asString(raw.name) ?? ''; const spaceIdx = name.indexOf(' '); return { - id: String(raw.id ?? ''), - email: String(raw.email ?? ''), + id: asString(raw.id) ?? '', + email: asString(raw.email) ?? '', firstName: spaceIdx >= 0 ? name.slice(0, spaceIdx) : name, lastName: spaceIdx >= 0 ? name.slice(spaceIdx + 1) : '', - role: (raw.role as 'USER' | 'ADMIN') ?? 'USER', - avatarUrl: (raw.image as string | null) ?? null, + role: asString(raw.role) ?? 'USER', + emailVerified: asBoolean(raw.emailVerified) ?? null, + avatarUrl: asString(raw.image) ?? null, + createdAt: asString(raw.createdAt) ?? null, + updatedAt: asString(raw.updatedAt) ?? null, preferredWeightUnit: (raw.preferredWeightUnit as User['preferredWeightUnit']) ?? 'g', }; } @@ -88,17 +92,12 @@ export function useAuthActions() { if (!idToken) throw new Error(t('auth.noIdTokenFromGoogle')); - const { data, error } = await apiClient.auth.google.post({ idToken }); - if (error || !data) { - throw new Error(extractAuthError(error?.value, t('auth.failedToSignInWithGoogle'))); - } - - await setToken(data.accessToken); - await setRefreshToken(data.refreshToken); - userStore.set(UserSchema.parse(data.user)); - - setNeedsReauth(false); - redirect(redirectTo); + const { data, error } = await authClient.signIn.social({ + provider: 'google', + idToken: { token: idToken }, + }); + if (error) throw new Error(error.message ?? t('auth.failedToSignInWithGoogle')); + if (data && 'user' in data && data.user) applySession(data.user as Record); // safe-cast: Better Auth user type omits additionalFields; role/preferredWeightUnit present at runtime } catch (error) { setIsLoading(false); diff --git a/apps/expo/features/auth/hooks/useAuthInit.ts b/apps/expo/features/auth/hooks/useAuthInit.ts index 0aa8653852..b8e9714bc1 100644 --- a/apps/expo/features/auth/hooks/useAuthInit.ts +++ b/apps/expo/features/auth/hooks/useAuthInit.ts @@ -1,5 +1,6 @@ import { when } from '@legendapp/state'; import { clientEnvs } from '@packrat/env/expo-client'; +import { asBoolean, asString } from '@packrat/guards'; import AsyncStorage from '@react-native-async-storage/async-storage'; import { GoogleSignin } from '@react-native-google-signin/google-signin'; import { userStore, userSyncState } from 'expo-app/features/auth/store'; @@ -23,17 +24,17 @@ async function runVersionGateMigration() { } function applySessionUser(sessionUser: Record) { + const name = asString(sessionUser.name) ?? ''; userStore.set({ - id: String(sessionUser.id ?? ''), - email: String(sessionUser.email ?? ''), - firstName: String(sessionUser.name ?? '').split(' ')[0] ?? '', - lastName: - String(sessionUser.name ?? '') - .split(' ') - .slice(1) - .join(' ') ?? '', - role: (sessionUser.role as 'USER' | 'ADMIN') ?? 'USER', // safe-cast: Better Auth client type omits additionalFields; role is present at runtime - avatarUrl: (sessionUser.image as string | null) ?? null, + id: asString(sessionUser.id) ?? '', + email: asString(sessionUser.email) ?? '', + firstName: name.split(' ')[0] ?? '', + lastName: name.split(' ').slice(1).join(' ') ?? '', + role: asString(sessionUser.role) ?? 'USER', + emailVerified: asBoolean(sessionUser.emailVerified) ?? null, + avatarUrl: asString(sessionUser.image) ?? null, + createdAt: asString(sessionUser.createdAt) ?? null, + updatedAt: asString(sessionUser.updatedAt) ?? null, preferredWeightUnit: 'g', }); } diff --git a/apps/expo/features/catalog/components/ItemReviews.tsx b/apps/expo/features/catalog/components/ItemReviews.tsx index ca3b205968..382f88ef49 100644 --- a/apps/expo/features/catalog/components/ItemReviews.tsx +++ b/apps/expo/features/catalog/components/ItemReviews.tsx @@ -44,12 +44,13 @@ export function ItemReviews({ reviews }: ItemReviewsProps) { - {reviews.map((review) => { - const isExpanded = expandedReviews[review.title] || false; - const shouldTruncate = review.text.length > 150; + {reviews.map((review, idx) => { + const reviewKey = review.title ?? String(idx); + const isExpanded = expandedReviews[reviewKey] || false; + const shouldTruncate = (review.text?.length ?? 0) > 150; return ( - + {review.user_avatar ? ( @@ -63,7 +64,9 @@ export function ItemReviews({ reviews }: ItemReviewsProps) { {review.user_name || t('catalog.anonymous')} - {formatDate(review.date)} + {review.date && ( + {formatDate(review.date)} + )} @@ -97,10 +100,7 @@ export function ItemReviews({ reviews }: ItemReviewsProps) { {shouldTruncate && ( - toggleReviewExpansion(review.title)} - > + toggleReviewExpansion(reviewKey)}> {isExpanded ? t('catalog.showLess') : t('catalog.readMore')} diff --git a/packages/api/src/schemas/users.ts b/packages/api/src/schemas/users.ts index 479d4c0f42..a1c1f15bba 100644 --- a/packages/api/src/schemas/users.ts +++ b/packages/api/src/schemas/users.ts @@ -2,7 +2,7 @@ import { z } from 'zod'; // Base user schema export const UserSchema = z.object({ - id: z.number().int().positive(), + id: z.string(), email: z.string().email(), firstName: z.string().nullable(), lastName: z.string().nullable(), From 5676ae53c04463ab6aa5b83b67d2f8ff13f0f956 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 10:04:10 -0600 Subject: [PATCH 09/51] chore(deps): pin @packrat-ai/nativewindui to 2.0.3-2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes 3 TypeScript errors in nativewindui source: - Icon/types: SymbolViewPropsWithStringName satisfies IconMapper constraint - AdaptiveSearchHeader, LargeTitleHeader: map 'systemDefault' autoCapitalize to 'none' Also widens expo-router peer dep to >=6.0.23 (was ~6.0.23) so bun does not install expo-router@6.0.23 into apps/expo/node_modules, which was causing TypeScript to resolve to the old API and produce 1184+ false positives. 2.0.3-2 is an exact pin; the root override prevents range resolution. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- bun.lock | 28 +++------------------------- package.json | 2 +- packages/ui/package.json | 2 +- 3 files changed, 5 insertions(+), 27 deletions(-) diff --git a/bun.lock b/bun.lock index 67ed5ceefb..cfb54dacbe 100644 --- a/bun.lock +++ b/bun.lock @@ -638,7 +638,7 @@ "name": "@packrat/ui", "version": "2.0.25", "dependencies": { - "@packrat-ai/nativewindui": "^2.0.5", + "@packrat-ai/nativewindui": "2.0.3-2", }, }, "packages/units": { @@ -718,7 +718,7 @@ "@sentry/cli", ], "overrides": { - "@packrat-ai/nativewindui": "2.0.3", + "@packrat-ai/nativewindui": "2.0.3-2", "@sinclair/typebox": "^0.34.15", "elysia": "^1.4.0", "expo-sqlite": "~55.0.15", @@ -1475,7 +1475,7 @@ "@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="], - "@packrat-ai/nativewindui": ["@packrat-ai/nativewindui@2.0.3", "https://npm.pkg.github.com/download/@packrat-ai/nativewindui/2.0.3/6d1d9364d32c4a145cc9fc49a73744b553db5e14", { "peerDependencies": { "@expo/vector-icons": ">=15.0.0", "@gorhom/bottom-sheet": "^5.1.2", "@react-native-community/datetimepicker": "^8.4.0", "@react-native-community/slider": "^5.0.0", "@react-native-picker/picker": "^2.11.0", "@react-native-segmented-control/segmented-control": "^2.5.0", "@react-navigation/drawer": "^7.1.1", "@react-navigation/elements": "^2.3.1", "@react-navigation/native": "^7.0.14", "@rn-primitives/alert-dialog": "^1.1.0", "@rn-primitives/avatar": "^1.0.4", "@rn-primitives/checkbox": "^1.1.0", "@rn-primitives/context-menu": "^1.1.0", "@rn-primitives/dropdown-menu": "^1.1.0", "@rn-primitives/hooks": "^1.1.0", "@rn-primitives/portal": "^1.1.0", "@rn-primitives/slot": "^1.1.0", "@shopify/flash-list": "^2.0.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "expo-blur": "~15.0.8", "expo-device": "~8.0.0", "expo-glass-effect": "*", "expo-haptics": "~15.0.8", "expo-image": "~3.0.11", "expo-linear-gradient": "~15.0.8", "expo-navigation-bar": "~5.0.10", "expo-router": "~6.0.23", "expo-symbols": "~1.0.8", "nativewind": "^4.2.3", "react": ">=19.0.0", "react-native": ">=0.79.0", "react-native-keyboard-controller": "^1.16.7", "react-native-reanimated": ">=3.17.0", "react-native-safe-area-context": ">=5.4.0", "react-native-screens": ">=4.11.0", "react-native-uitextview": "^1.1.4", "rn-icon-mapper": "^0.0.1", "tailwind-merge": "^2.2.1" } }, "sha512-C0PzEmKNqc7KS6DcxRN6hOtyIpkK6xGLO0I3x6eKBtunGSPseQWz/JEdsQW2i42xRVZN3r83loYb3v7dVUDTFg=="], + "@packrat-ai/nativewindui": ["@packrat-ai/nativewindui@2.0.3-2", "https://npm.pkg.github.com/download/@packrat-ai/nativewindui/2.0.3-2/cae289eeb09d41ea394e5273bc4694596e3facc3", { "peerDependencies": { "@expo/vector-icons": ">=15.0.0", "@gorhom/bottom-sheet": "^5.1.2", "@react-native-community/datetimepicker": "^8.4.0", "@react-native-community/slider": "^5.0.0", "@react-native-picker/picker": "^2.11.0", "@react-native-segmented-control/segmented-control": "^2.5.0", "@react-navigation/drawer": "^7.1.1", "@react-navigation/elements": "^2.3.1", "@react-navigation/native": "^7.0.14", "@rn-primitives/alert-dialog": "^1.1.0", "@rn-primitives/avatar": "^1.0.4", "@rn-primitives/checkbox": "^1.1.0", "@rn-primitives/context-menu": "^1.1.0", "@rn-primitives/dropdown-menu": "^1.1.0", "@rn-primitives/hooks": "^1.1.0", "@rn-primitives/portal": "^1.1.0", "@rn-primitives/slot": "^1.1.0", "@shopify/flash-list": "^2.0.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "expo-blur": "~15.0.8", "expo-device": "~8.0.0", "expo-glass-effect": "*", "expo-haptics": "~15.0.8", "expo-image": "~3.0.11", "expo-linear-gradient": "~15.0.8", "expo-navigation-bar": "~5.0.10", "expo-router": ">=6.0.23", "expo-symbols": "~1.0.8", "nativewind": "^4.2.3", "react": ">=19.0.0", "react-native": ">=0.79.0", "react-native-keyboard-controller": "^1.16.7", "react-native-reanimated": ">=3.17.0", "react-native-safe-area-context": ">=5.4.0", "react-native-screens": ">=4.11.0", "react-native-uitextview": "^1.1.4", "rn-icon-mapper": "^0.0.1", "tailwind-merge": "^2.2.1" } }, "sha512-gimhxLYi3IiAqV4h0s1pzPb4mxy07XOcgRkhN8nOVRi7t6Ucb5dOGv2ArWY3WfQlSrN52dPRxzDtdsnIn33FRg=="], "@packrat/analytics": ["@packrat/analytics@workspace:packages/analytics"], @@ -4657,14 +4657,6 @@ "@modelcontextprotocol/sdk/jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], - "@packrat-ai/nativewindui/expo-device": ["expo-device@55.0.15", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-vXy4U/IeYI+zHGG45Ap6J7EuyQmkstyo8I+/5YGr5q2zmqLBo6SWE62wii8i9hLHheHn6AtF9UPrSWAREJrE8A=="], - - "@packrat-ai/nativewindui/expo-image": ["expo-image@55.0.9", "", { "dependencies": { "sf-symbols-typescript": "^2.2.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-+NVgWv+tr7a6EpBEaIIVVp+XfruRA2JL5xOxvd6ajvFGdH0rOhagwX1m1piAII6w7sh6uAnBr8X+fDZsav7B2w=="], - - "@packrat-ai/nativewindui/expo-router": ["expo-router@55.0.13", "", { "dependencies": { "@expo/metro-runtime": "^55.0.10", "@expo/schema-utils": "^55.0.3", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-navigation/bottom-tabs": "^7.15.5", "@react-navigation/native": "^7.1.33", "@react-navigation/native-stack": "^7.14.5", "client-only": "^0.0.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "expo-glass-effect": "^55.0.10", "expo-image": "^55.0.9", "expo-server": "^55.0.8", "expo-symbols": "^55.0.7", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.8", "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", "react-native-is-edge-to-edge": "^1.2.1", "semver": "~7.6.3", "server-only": "^0.0.1", "sf-symbols-typescript": "^2.1.0", "shallowequal": "^1.1.0", "use-latest-callback": "^0.2.1", "vaul": "^1.1.2" }, "peerDependencies": { "@expo/log-box": "55.0.11", "@react-navigation/drawer": "^7.9.4", "@testing-library/react-native": ">= 13.2.0", "expo": "*", "expo-constants": "^55.0.15", "expo-linking": "^55.0.14", "react": "*", "react-dom": "*", "react-native": "*", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": ">= 5.4.0", "react-native-screens": "*", "react-native-web": "*", "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" }, "optionalPeers": ["@react-navigation/drawer", "@testing-library/react-native", "react-dom", "react-native-gesture-handler", "react-native-reanimated", "react-native-web", "react-server-dom-webpack"] }, "sha512-cIBR5RmQtbr+b535mlbMhmm7lweVZXFtjzJOgJTutoxIApRztl816kFRFNesnVyqQ0LZrEU0a6vqa3i0wdlRQw=="], - - "@packrat-ai/nativewindui/expo-symbols": ["expo-symbols@55.0.7", "", { "dependencies": { "@expo-google-fonts/material-symbols": "^0.4.1", "sf-symbols-typescript": "^2.0.0" }, "peerDependencies": { "expo": "*", "expo-font": "*", "react": "*", "react-native": "*" } }, "sha512-y4ALLbncSGQzhFLw1PaIBbO39xzaw3ie249HmK6zK/WLJYfw4Z/9UU4iPKO3KCE4FyCKIzd+yRsvzvlri23YrQ=="], - "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], "@poppinss/colors/kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], @@ -5341,18 +5333,6 @@ "@manypkg/tools/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "@packrat-ai/nativewindui/expo-router/@expo/log-box": ["@expo/log-box@55.0.11", "", { "dependencies": { "@expo/dom-webview": "^55.0.5", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-JQHFLWkskIbJi6cxYMjErx8lQqfFJilDQLKmdTO3m3YkdmN9GE/CrzjOfVlCG0DGEGZJ90br0pGKvGPdXNsHKw=="], - - "@packrat-ai/nativewindui/expo-router/@expo/schema-utils": ["@expo/schema-utils@55.0.3", "", {}, "sha512-l9KHVjTo6MvoeyvwNr6AjckGJm8NIcqZ3QSAh51cWozXW9v2AUjyCyqYtFtyntLWRZ0x/ByYJishpQo4ZQq45Q=="], - - "@packrat-ai/nativewindui/expo-router/expo-glass-effect": ["expo-glass-effect@55.0.10", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-5kL/jATvgJWdrqPdxixrECJqD2l8cfQ4ALr1DK7qi9XkyI97ejXvUjB2VsfEePNy3Fg+/VwzA3n3L7Nv3tAPkw=="], - - "@packrat-ai/nativewindui/expo-router/expo-server": ["expo-server@55.0.8", "", {}, "sha512-AoV5TKuO4biSzrhe/OVLyInfTT0pV9/OOc/g/oVq5vmCjL8SaSYTkES8PLt+67Tm7VqX+Dn0+kSx1nQcjEKaPw=="], - - "@packrat-ai/nativewindui/expo-router/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "@packrat-ai/nativewindui/expo-router/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], - "@react-native-ai/apple/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@react-native-ai/llama/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], @@ -5815,8 +5795,6 @@ "@lhci/cli/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], - "@packrat-ai/nativewindui/expo-router/@expo/log-box/@expo/dom-webview": ["@expo/dom-webview@55.0.5", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-lt3uxYOCk3wmWvtOOvsC35CKGbDAOx5C2EaY8SH1JVSfBzqmF8Cs0Xp1MPxncDPMyxpMiWx5SvvV/iLF1rJU4A=="], - "@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], "@react-native/community-cli-plugin/metro/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], diff --git a/package.json b/package.json index 537658fd03..77d64f21a6 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "web": "bun run --cwd apps/web dev" }, "overrides": { - "@packrat-ai/nativewindui": "2.0.3", + "@packrat-ai/nativewindui": "2.0.3-2", "@sinclair/typebox": "^0.34.15", "elysia": "^1.4.0", "expo-sqlite": "~55.0.15", diff --git a/packages/ui/package.json b/packages/ui/package.json index 6447cfc226..16332aef81 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -3,6 +3,6 @@ "version": "2.0.25", "private": true, "dependencies": { - "@packrat-ai/nativewindui": "^2.0.5" + "@packrat-ai/nativewindui": "2.0.3-2" } } From b5a461ca2c0c50ecbaedcd4f9a840c3c68e305b5 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 11:25:24 -0600 Subject: [PATCH 10/51] feat(admin,etl): align TypeBox schemas with route columns + add ETL admin ops - Expand AdminCatalogItemSchema to all 24 columns the route returns - Fix AdminUserItemSchema/AdminPackItemSchema to match SELECT outputs - Derive AdminUser/AdminPack/AdminCatalogItem via Static<> (drop hand-written interfaces) - Remove 3 `as unknown as PaginatedResponse` casts in admin API client - Fix edit-catalog-dialog.tsx for nullable weightUnit ripple ETL admin routes (catalog analytics): - GET /etl/failure-summary: jsonb unnest aggregation of top validation errors - GET /etl/:jobId/failures: per-job error breakdown + raw samples - POST /etl/reset-stuck: marks running jobs stuck >30min as failed - POST /etl/:jobId/retry: clones job record and re-queues file to Cloudflare Queue --- apps/admin/components/edit-catalog-dialog.tsx | 4 +- apps/admin/lib/api.ts | 62 +----- .../api/src/routes/admin/analytics/catalog.ts | 191 +++++++++++++++++- packages/api/src/schemas/admin.ts | 30 ++- 4 files changed, 224 insertions(+), 63 deletions(-) diff --git a/apps/admin/components/edit-catalog-dialog.tsx b/apps/admin/components/edit-catalog-dialog.tsx index e03ff86c5f..7019e07d03 100644 --- a/apps/admin/components/edit-catalog-dialog.tsx +++ b/apps/admin/components/edit-catalog-dialog.tsx @@ -49,7 +49,7 @@ export function EditCatalogDialog({ item }: EditCatalogDialogProps) { : null; const weightRaw = fd.get('weight')?.toString().trim(); const weight = weightRaw ? Number(weightRaw) : undefined; - const weightUnit = fd.get('weightUnit')?.toString().trim() || item.weightUnit; + const weightUnit = fd.get('weightUnit')?.toString().trim() || item.weightUnit || undefined; const priceRaw = fd.get('price')?.toString().trim(); const price = priceRaw ? Number(priceRaw) : null; @@ -108,7 +108,7 @@ export function EditCatalogDialog({ item }: EditCatalogDialogProps) { diff --git a/apps/admin/lib/api.ts b/apps/admin/lib/api.ts index 040075d7dd..ed81f6879c 100644 --- a/apps/admin/lib/api.ts +++ b/apps/admin/lib/api.ts @@ -3,6 +3,9 @@ import type { App } from '@packrat/api'; import type { ActiveUsersSchema, ActivityPointSchema, + AdminCatalogItemSchema, + AdminPackItemSchema, + AdminUserItemSchema, BrandRowSchema, BreakdownItemSchema, CatalogOverviewSchema, @@ -69,17 +72,7 @@ export async function getStats(): Promise { // ─── Users ──────────────────────────────────────────────────────────────────── -export interface AdminUser { - id: number; - email: string; - firstName: string | null; - lastName: string | null; - role: string | null; - emailVerified: boolean | null; - avatarUrl: string | null; - createdAt: string | null; - updatedAt: string | null; -} +export type AdminUser = Static; export interface PaginatedResponse { data: T[]; @@ -103,7 +96,7 @@ export async function getUsers({ query: { limit, offset, q, includeDeleted: includeDeleted ? 'true' : undefined }, }); if (error) throwOnError(error); - return unwrap(data, 'users') as unknown as PaginatedResponse; // safe-cast: Eden Treaty infers wide union; runtime shape matches PaginatedResponse + return unwrap(data, 'users'); } export async function deleteUser(id: number): Promise<{ success: boolean }> { @@ -129,19 +122,7 @@ export async function restoreUser(id: number): Promise<{ success: boolean }> { // ─── Packs ──────────────────────────────────────────────────────────────────── -export interface AdminPack { - id: string; - name: string; - description: string | null; - category: string; - isPublic: boolean | null; - isAIGenerated: boolean; - tags: string[] | null; - image: string | null; - createdAt: string | null; - updatedAt: string | null; - userEmail: string | null; -} +export type AdminPack = Static; export async function getPacks({ limit = 100, @@ -158,7 +139,7 @@ export async function getPacks({ query: { limit, offset, q, includeDeleted: includeDeleted ? 'true' : undefined }, }); if (error) throwOnError(error); - return unwrap(data, 'packs') as unknown as PaginatedResponse; // safe-cast: Eden Treaty infers wide union; runtime shape matches PaginatedResponse + return unwrap(data, 'packs'); } export async function deletePack(id: string): Promise<{ success: boolean }> { @@ -169,32 +150,7 @@ export async function deletePack(id: string): Promise<{ success: boolean }> { // ─── Catalog Items ──────────────────────────────────────────────────────────── -export interface AdminCatalogItem { - id: number; - name: string; - description: string | null; - categories: string[] | null; - brand: string | null; - model: string | null; - sku: string | null; - price: number | null; - currency: string | null; - weight: number | null; - weightUnit: string; - availability: string | null; - ratingValue: number | null; - reviewCount: number | null; - color: string | null; - size: string | null; - material: string | null; - seller: string | null; - productUrl: string | null; - images: string[] | null; - variants: Array<{ attribute: string; values: string[] }> | null; - techs: Record | null; - links: Array<{ title: string; url: string }> | null; - createdAt: string | null; -} +export type AdminCatalogItem = Static; export interface UpdateCatalogItemInput { name?: string; @@ -219,7 +175,7 @@ export async function getCatalogItems({ query: { limit, offset, q }, }); if (error) throwOnError(error); - return unwrap(data, 'catalog') as unknown as PaginatedResponse; // safe-cast: Eden Treaty infers wide union; runtime shape matches PaginatedResponse + return unwrap(data, 'catalog'); } export async function deleteCatalogItem(id: number): Promise<{ success: boolean }> { diff --git a/packages/api/src/routes/admin/analytics/catalog.ts b/packages/api/src/routes/admin/analytics/catalog.ts index 44f9bf71d5..936f5a3eae 100644 --- a/packages/api/src/routes/admin/analytics/catalog.ts +++ b/packages/api/src/routes/admin/analytics/catalog.ts @@ -1,5 +1,5 @@ import { createDb } from '@packrat/api/db'; -import { catalogItems, etlJobs } from '@packrat/api/db/schema'; +import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/api/db/schema'; import { AdminErrorResponses, BrandRowSchema, @@ -7,7 +7,9 @@ import { EtlResponseSchema, PriceBucketSchema, } from '@packrat/api/schemas/admin'; -import { and, avg, count, desc, gt, isNotNull, max, min, sql } from 'drizzle-orm'; +import { queueCatalogETL } from '@packrat/api/services/etl/queue'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { and, avg, count, desc, eq, gt, isNotNull, lt, max, min, sql } from 'drizzle-orm'; import { Elysia, status, t } from 'elysia'; import { z } from 'zod'; @@ -257,4 +259,189 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) } }, { detail: { tags: ['Admin'], summary: 'Embedding coverage' } }, + ) + + // ─── ETL failure summary ────────────────────────────────────────────────────── + + .get( + '/etl/failure-summary', + async ({ query }) => { + const db = createDb(); + const { limit = 20 } = query; + + try { + const rows = await db.execute<{ field: string; reason: string; count: number }>( + sql` + SELECT + err->>'field' AS field, + err->>'reason' AS reason, + COUNT(*)::int AS count + FROM ${invalidItemLogs}, + jsonb_array_elements(${invalidItemLogs.errors}) AS err + GROUP BY err->>'field', err->>'reason' + ORDER BY count DESC + LIMIT ${limit} + `, + ); + + const [total] = await db.select({ n: count() }).from(invalidItemLogs); + + return { + topErrors: rows.rows.map((r) => ({ + field: r.field, + reason: r.reason, + count: r.count, + })), + totalInvalidItems: total?.n ?? 0, + }; + } catch (error) { + console.error('ETL failure summary error:', error); + return status(500, { + error: 'Failed to fetch failure summary', + code: 'ETL_FAILURE_SUMMARY_ERROR', + }); + } + }, + { + query: z.object({ + limit: z.coerce.number().int().min(1).max(100).optional().default(20), + }), + detail: { tags: ['Admin'], summary: 'Top ETL validation failure patterns' }, + }, + ) + + // ─── Per-job failure drill-down ─────────────────────────────────────────────── + + .get( + '/etl/:jobId/failures', + async ({ params, query }) => { + const db = createDb(); + const { limit = 50 } = query; + + try { + const samples = await db + .select() + .from(invalidItemLogs) + .where(eq(invalidItemLogs.jobId, params.jobId)) + .orderBy(invalidItemLogs.rowIndex) + .limit(limit); + + const breakdown = await db.execute<{ field: string; reason: string; count: number }>( + sql` + SELECT + err->>'field' AS field, + err->>'reason' AS reason, + COUNT(*)::int AS count + FROM ${invalidItemLogs}, + jsonb_array_elements(${invalidItemLogs.errors}) AS err + WHERE ${invalidItemLogs.jobId} = ${params.jobId} + GROUP BY err->>'field', err->>'reason' + ORDER BY count DESC + `, + ); + + return { + jobId: params.jobId, + errorBreakdown: breakdown.rows.map((r) => ({ + field: r.field, + reason: r.reason, + count: r.count, + })), + samples: samples.map((s) => ({ + rowIndex: s.rowIndex, + errors: s.errors, + rawData: s.rawData, + })), + totalShown: samples.length, + }; + } catch (error) { + console.error('ETL job failures error:', error); + return status(500, { + error: 'Failed to fetch job failures', + code: 'ETL_JOB_FAILURES_ERROR', + }); + } + }, + { + params: z.object({ jobId: z.string() }), + query: z.object({ + limit: z.coerce.number().int().min(1).max(200).optional().default(50), + }), + detail: { tags: ['Admin'], summary: 'Validation failures for a specific ETL job' }, + }, + ) + + // ─── Reset stuck jobs ───────────────────────────────────────────────────────── + + .post( + '/etl/reset-stuck', + async () => { + const db = createDb(); + + try { + // Jobs stuck in 'running' for more than 30 minutes are considered stalled + const stuckCutoff = new Date(Date.now() - 30 * 60 * 1000); + + const reset = await db + .update(etlJobs) + .set({ status: 'failed', completedAt: new Date() }) + .where(and(eq(etlJobs.status, 'running'), lt(etlJobs.startedAt, stuckCutoff))) + .returning(); + + return { reset: reset.length, ids: reset.map((r) => r.id) }; + } catch (error) { + console.error('ETL reset stuck error:', error); + return status(500, { error: 'Failed to reset stuck jobs', code: 'ETL_RESET_STUCK_ERROR' }); + } + }, + { detail: { tags: ['Admin'], summary: 'Mark stuck running ETL jobs as failed' } }, + ) + + // ─── Retry a failed job ─────────────────────────────────────────────────────── + + .post( + '/etl/:jobId/retry', + async ({ params }) => { + const db = createDb(); + + try { + const [original] = await db + .select() + .from(etlJobs) + .where(eq(etlJobs.id, params.jobId)) + .limit(1); + + if (!original) return status(404, { error: 'ETL job not found' }); + if (original.status === 'running') + return status(409, { + error: 'Job is still running — wait for it to complete or reset stuck jobs first', + }); + + const newJobId = crypto.randomUUID(); + const objectKey = `v2/${original.source}/${original.filename}`; + const env = getEnv(); + + if (!env.ETL_QUEUE) return status(400, { error: 'ETL_QUEUE is not configured' }); + + await db.insert(etlJobs).values({ + id: newJobId, + status: 'running', + source: original.source, + filename: original.filename, + scraperRevision: original.scraperRevision, + startedAt: new Date(), + }); + + await queueCatalogETL({ queue: env.ETL_QUEUE, objectKeys: [objectKey], jobId: newJobId }); + + return { success: true, newJobId, objectKey }; + } catch (error) { + console.error('ETL retry error:', error); + return status(500, { error: 'Failed to retry ETL job', code: 'ETL_RETRY_ERROR' }); + } + }, + { + params: z.object({ jobId: z.string() }), + detail: { tags: ['Admin'], summary: 'Retry a failed ETL job' }, + }, ); diff --git a/packages/api/src/schemas/admin.ts b/packages/api/src/schemas/admin.ts index 2107ba50d3..bdd1b61fd0 100644 --- a/packages/api/src/schemas/admin.ts +++ b/packages/api/src/schemas/admin.ts @@ -33,9 +33,9 @@ export const AdminUserItemSchema = t.Object({ lastName: t.Nullable(t.String()), role: t.Nullable(t.String()), emailVerified: t.Nullable(t.Boolean()), + avatarUrl: t.Nullable(t.String()), createdAt: t.Nullable(t.String()), - lastActiveAt: t.Nullable(t.String()), - deletedAt: t.Nullable(t.String()), + updatedAt: t.Nullable(t.String()), }); // ─── Packs ──────────────────────────────────────────────────────────────────── @@ -46,9 +46,11 @@ export const AdminPackItemSchema = t.Object({ description: t.Nullable(t.String()), category: t.String(), isPublic: t.Nullable(t.Boolean()), - deleted: t.Boolean(), - deletedAt: t.Nullable(t.String()), + isAIGenerated: t.Boolean(), + tags: t.Nullable(t.Array(t.String())), + image: t.Nullable(t.String()), createdAt: t.Nullable(t.String()), + updatedAt: t.Nullable(t.String()), userEmail: t.Nullable(t.String()), }); @@ -57,11 +59,27 @@ export const AdminPackItemSchema = t.Object({ export const AdminCatalogItemSchema = t.Object({ id: t.Number(), name: t.String(), + description: t.Nullable(t.String()), categories: t.Nullable(t.Array(t.String())), brand: t.Nullable(t.String()), + model: t.Nullable(t.String()), + sku: t.String(), price: t.Nullable(t.Number()), - weight: t.Number(), - weightUnit: t.String(), + currency: t.Nullable(t.String()), + weight: t.Nullable(t.Number()), + weightUnit: t.Nullable(t.String()), + availability: t.Nullable(t.String()), + ratingValue: t.Nullable(t.Number()), + reviewCount: t.Nullable(t.Number()), + color: t.Nullable(t.String()), + size: t.Nullable(t.String()), + material: t.Nullable(t.String()), + seller: t.Nullable(t.String()), + productUrl: t.String(), + images: t.Nullable(t.Array(t.String())), + variants: t.Nullable(t.Array(t.Object({ attribute: t.String(), values: t.Array(t.String()) }))), + techs: t.Nullable(t.Record(t.String(), t.String())), + links: t.Nullable(t.Array(t.Object({ title: t.String(), url: t.String() }))), createdAt: t.Nullable(t.String()), }); From 868aa4c683a10290636d164535c3cb521c7467e4 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 11:39:22 -0600 Subject: [PATCH 11/51] fix(admin,etl): address CodeRabbit/Copilot review comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix users.id schema: t.Number() → t.String() (users.id is text PK, not serial) - Update deleteUser/hardDeleteUser/restoreUser signatures: id: number → id: string - Add TypeBox response schemas to all 4 new ETL routes (failure-summary, job-failures, reset-stuck, retry) — Eden Treaty consumers were getting unknown payloads - Derive EtlFailureSummary/EtlJobFailures types via Static<> instead of hand-written interfaces - Parallelize independent DB queries in failure-summary and job-failures via Promise.all - Restrict retry to failed jobs only (was accepting completed, risking duplicate ingest) - Clean up orphaned running row on enqueue failure (mark new job failed in catch) - Validate jobId as UUID on failures and retry endpoints --- apps/admin/lib/api.ts | 33 ++---- .../api/src/routes/admin/analytics/catalog.ts | 107 +++++++++++------- packages/api/src/schemas/admin.ts | 36 +++++- 3 files changed, 110 insertions(+), 66 deletions(-) diff --git a/apps/admin/lib/api.ts b/apps/admin/lib/api.ts index ed81f6879c..1df1af5636 100644 --- a/apps/admin/lib/api.ts +++ b/apps/admin/lib/api.ts @@ -10,6 +10,8 @@ import type { BreakdownItemSchema, CatalogOverviewSchema, EmbeddingStatsSchema, + EtlFailureSummarySchema, + EtlJobFailuresSchema, EtlJobSchema, EtlResponseSchema, GrowthPointSchema, @@ -99,23 +101,23 @@ export async function getUsers({ return unwrap(data, 'users'); } -export async function deleteUser(id: number): Promise<{ success: boolean }> { - const { data, error } = await adminClient.users({ id: String(id) }).delete(); +export async function deleteUser(id: string): Promise<{ success: boolean }> { + const { data, error } = await adminClient.users({ id }).delete(); if (error) throwOnError(error); return unwrap(data, 'deleteUser'); } export async function hardDeleteUser( - id: number, + id: string, reason: string, ): Promise<{ success: boolean; purged: boolean }> { - const { data, error } = await adminClient.users({ id: String(id) }).hard.delete({ reason }); + const { data, error } = await adminClient.users({ id }).hard.delete({ reason }); if (error) throwOnError(error); return unwrap(data, 'hardDeleteUser'); } -export async function restoreUser(id: number): Promise<{ success: boolean }> { - const { data, error } = await adminClient.users({ id: String(id) }).restore.post(); +export async function restoreUser(id: string): Promise<{ success: boolean }> { + const { data, error } = await adminClient.users({ id }).restore.post(); if (error) throwOnError(error); return unwrap(data, 'restoreUser'); } @@ -343,23 +345,8 @@ export function resetStuckEtlJobs(): Promise<{ reset: number; ids: string[] }> { return adminFetch('/analytics/catalog/etl/reset-stuck', { method: 'POST' }); } -export type EtlErrorRow = { field: string; reason: string; count: number }; - -export type EtlFailureSummary = { - topErrors: EtlErrorRow[]; - totalInvalidItems: number; -}; - -export type EtlJobFailures = { - jobId: string; - errorBreakdown: EtlErrorRow[]; - samples: Array<{ - rowIndex: number; - errors: Array<{ field: string; reason: string; value?: unknown }>; - rawData: unknown; - }>; - totalShown: number; -}; +export type EtlFailureSummary = Static; +export type EtlJobFailures = Static; export function getEtlFailureSummary(limit = 20): Promise { return adminFetch(`/analytics/catalog/etl/failure-summary?limit=${limit}`); diff --git a/packages/api/src/routes/admin/analytics/catalog.ts b/packages/api/src/routes/admin/analytics/catalog.ts index 936f5a3eae..301036c115 100644 --- a/packages/api/src/routes/admin/analytics/catalog.ts +++ b/packages/api/src/routes/admin/analytics/catalog.ts @@ -4,7 +4,11 @@ import { AdminErrorResponses, BrandRowSchema, CatalogOverviewSchema, + EtlFailureSummarySchema, + EtlJobFailuresSchema, + EtlResetStuckSchema, EtlResponseSchema, + EtlRetrySchema, PriceBucketSchema, } from '@packrat/api/schemas/admin'; import { queueCatalogETL } from '@packrat/api/services/etl/queue'; @@ -270,21 +274,22 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) const { limit = 20 } = query; try { - const rows = await db.execute<{ field: string; reason: string; count: number }>( - sql` - SELECT - err->>'field' AS field, - err->>'reason' AS reason, - COUNT(*)::int AS count - FROM ${invalidItemLogs}, - jsonb_array_elements(${invalidItemLogs.errors}) AS err - GROUP BY err->>'field', err->>'reason' - ORDER BY count DESC - LIMIT ${limit} - `, - ); - - const [total] = await db.select({ n: count() }).from(invalidItemLogs); + const [rows, [total]] = await Promise.all([ + db.execute<{ field: string; reason: string; count: number }>( + sql` + SELECT + err->>'field' AS field, + err->>'reason' AS reason, + COUNT(*)::int AS count + FROM ${invalidItemLogs}, + jsonb_array_elements(${invalidItemLogs.errors}) AS err + GROUP BY err->>'field', err->>'reason' + ORDER BY count DESC + LIMIT ${limit} + `, + ), + db.select({ n: count() }).from(invalidItemLogs), + ]); return { topErrors: rows.rows.map((r) => ({ @@ -306,6 +311,7 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) query: z.object({ limit: z.coerce.number().int().min(1).max(100).optional().default(20), }), + response: { 200: EtlFailureSummarySchema, ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Top ETL validation failure patterns' }, }, ) @@ -319,26 +325,27 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) const { limit = 50 } = query; try { - const samples = await db - .select() - .from(invalidItemLogs) - .where(eq(invalidItemLogs.jobId, params.jobId)) - .orderBy(invalidItemLogs.rowIndex) - .limit(limit); - - const breakdown = await db.execute<{ field: string; reason: string; count: number }>( - sql` - SELECT - err->>'field' AS field, - err->>'reason' AS reason, - COUNT(*)::int AS count - FROM ${invalidItemLogs}, - jsonb_array_elements(${invalidItemLogs.errors}) AS err - WHERE ${invalidItemLogs.jobId} = ${params.jobId} - GROUP BY err->>'field', err->>'reason' - ORDER BY count DESC - `, - ); + const [samples, breakdown] = await Promise.all([ + db + .select() + .from(invalidItemLogs) + .where(eq(invalidItemLogs.jobId, params.jobId)) + .orderBy(invalidItemLogs.rowIndex) + .limit(limit), + db.execute<{ field: string; reason: string; count: number }>( + sql` + SELECT + err->>'field' AS field, + err->>'reason' AS reason, + COUNT(*)::int AS count + FROM ${invalidItemLogs}, + jsonb_array_elements(${invalidItemLogs.errors}) AS err + WHERE ${invalidItemLogs.jobId} = ${params.jobId} + GROUP BY err->>'field', err->>'reason' + ORDER BY count DESC + `, + ), + ]); return { jobId: params.jobId, @@ -363,10 +370,11 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) } }, { - params: z.object({ jobId: z.string() }), + params: z.object({ jobId: z.string().uuid() }), query: z.object({ limit: z.coerce.number().int().min(1).max(200).optional().default(50), }), + response: { 200: EtlJobFailuresSchema, ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Validation failures for a specific ETL job' }, }, ) @@ -394,7 +402,10 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) return status(500, { error: 'Failed to reset stuck jobs', code: 'ETL_RESET_STUCK_ERROR' }); } }, - { detail: { tags: ['Admin'], summary: 'Mark stuck running ETL jobs as failed' } }, + { + response: { 200: EtlResetStuckSchema, ...AdminErrorResponses }, + detail: { tags: ['Admin'], summary: 'Mark stuck running ETL jobs as failed' }, + }, ) // ─── Retry a failed job ─────────────────────────────────────────────────────── @@ -412,9 +423,12 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) .limit(1); if (!original) return status(404, { error: 'ETL job not found' }); - if (original.status === 'running') + if (original.status !== 'failed') return status(409, { - error: 'Job is still running — wait for it to complete or reset stuck jobs first', + error: + original.status === 'running' + ? 'Job is still running — wait for it to complete or reset stuck jobs first' + : 'Only failed jobs can be retried', }); const newJobId = crypto.randomUUID(); @@ -432,16 +446,25 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) startedAt: new Date(), }); - await queueCatalogETL({ queue: env.ETL_QUEUE, objectKeys: [objectKey], jobId: newJobId }); + try { + await queueCatalogETL({ queue: env.ETL_QUEUE, objectKeys: [objectKey], jobId: newJobId }); + } catch (enqueueErr) { + await db + .update(etlJobs) + .set({ status: 'failed', completedAt: new Date() }) + .where(eq(etlJobs.id, newJobId)); + throw enqueueErr; + } - return { success: true, newJobId, objectKey }; + return { success: true as const, newJobId, objectKey }; } catch (error) { console.error('ETL retry error:', error); return status(500, { error: 'Failed to retry ETL job', code: 'ETL_RETRY_ERROR' }); } }, { - params: z.object({ jobId: z.string() }), + params: z.object({ jobId: z.string().uuid() }), + response: { 200: EtlRetrySchema, ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Retry a failed ETL job' }, }, ); diff --git a/packages/api/src/schemas/admin.ts b/packages/api/src/schemas/admin.ts index bdd1b61fd0..e64ad8655a 100644 --- a/packages/api/src/schemas/admin.ts +++ b/packages/api/src/schemas/admin.ts @@ -27,7 +27,7 @@ export const AdminStatsSchema = t.Object({ // ─── Users ──────────────────────────────────────────────────────────────────── export const AdminUserItemSchema = t.Object({ - id: t.Number(), + id: t.String(), email: t.String(), firstName: t.Nullable(t.String()), lastName: t.Nullable(t.String()), @@ -176,6 +176,40 @@ export const EmbeddingStatsSchema = t.Object({ coveragePct: t.Number(), }); +const EtlErrorRowSchema = t.Object({ field: t.String(), reason: t.String(), count: t.Number() }); + +export const EtlFailureSummarySchema = t.Object({ + topErrors: t.Array(EtlErrorRowSchema), + totalInvalidItems: t.Number(), +}); + +export const EtlJobFailuresSchema = t.Object({ + jobId: t.String(), + errorBreakdown: t.Array(EtlErrorRowSchema), + samples: t.Array( + t.Object({ + rowIndex: t.Number(), + errors: t.Array( + t.Object({ + field: t.String(), + reason: t.String(), + value: t.Optional(t.Unknown()), + }), + ), + rawData: t.Unknown(), + }), + ), + totalShown: t.Number(), +}); + +export const EtlResetStuckSchema = t.Object({ reset: t.Number(), ids: t.Array(t.String()) }); + +export const EtlRetrySchema = t.Object({ + success: t.Literal(true), + newJobId: t.String(), + objectKey: t.String(), +}); + // ─── Trails ─────────────────────────────────────────────────────────────────── export const TrailSearchItemSchema = t.Object({ From fca7d17a0756939beed4992f4ce2a3b80b2bff7d Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 11:56:55 -0600 Subject: [PATCH 12/51] fix(cors): add admin.packratai.com + *.workers.dev to root cors allowlist Root cors runs first and short-circuits OPTIONS preflights before the admin-scoped cors plugin can set Access-Control-Allow-Origin. Admin origin was only in the admin-scoped plugin, so preflights from admin.packratai.com got no origin header. Non-preflight requests worked because the full middleware chain ran and the admin cors added the header to the response. Fix: add admin.packratai.com and *.workers.dev to root cors allowlist so OPTIONS preflights get the origin header reflected back correctly. --- packages/api/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index 0ff3fa9edd..baeb3fd4b6 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -33,6 +33,8 @@ export const app = new Elysia({ adapter: CloudflareAdapter }) const allowed = [ /^https:\/\/(www\.)?packrat\.world$/, /^https:\/\/[\w-]+\.packrat\.world$/, + /^https:\/\/[\w-]+\.packratai\.com$/, + /^https?:\/\/[\w-]+\.workers\.dev$/, /^http:\/\/localhost:\d+$/, /^exp:\/\//, ]; From 460d15199bf2489d3b5aef36c5a50e1991c0ba62 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 12:07:34 -0600 Subject: [PATCH 13/51] fix(admin/analytics): coerce Neon int8 strings to Number() in overview + ETL summary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neon returns PostgreSQL BIGINT (int8) as JavaScript strings to avoid precision loss. raw sql is a TS-only hint — COUNT/SUM results from raw SQL come back as strings at runtime. Drizzle's count() adds ::int cast internally, but sql`count(...)` and sql`sum(...)` do not. TypeBox t.Number() rejects string values so response validation failed. Coerce totalBrands, withEmbedding, completed, failed, totalItemsIngested. --- packages/api/src/routes/admin/analytics/catalog.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/api/src/routes/admin/analytics/catalog.ts b/packages/api/src/routes/admin/analytics/catalog.ts index 301036c115..8c3081feb7 100644 --- a/packages/api/src/routes/admin/analytics/catalog.ts +++ b/packages/api/src/routes/admin/analytics/catalog.ts @@ -65,14 +65,14 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) return { totalItems: t.totalItems, - totalBrands: t.totalBrands, + totalBrands: Number(t.totalBrands), avgPrice: t.avgPrice != null ? Math.round(Number(t.avgPrice) * 100) / 100 : null, minPrice: t.minPrice != null ? Number(t.minPrice) : null, maxPrice: t.maxPrice != null ? Number(t.maxPrice) : null, embeddingCoverage: { total: e.total, - withEmbedding: e.withEmbedding, - pct: e.total > 0 ? Math.round((e.withEmbedding / e.total) * 1000) / 10 : 0, + withEmbedding: Number(e.withEmbedding), + pct: e.total > 0 ? Math.round((Number(e.withEmbedding) / e.total) * 1000) / 10 : 0, }, availability: availabilityStats.map((r) => ({ status: r.status ?? null, @@ -215,9 +215,9 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) })), summary: { totalRuns: s?.totalRuns ?? 0, - completed: s?.completed ?? 0, - failed: s?.failed ?? 0, - totalItemsIngested: s?.totalItemsIngested ?? 0, + completed: Number(s?.completed ?? 0), + failed: Number(s?.failed ?? 0), + totalItemsIngested: Number(s?.totalItemsIngested ?? 0), }, }; } catch (error) { From c45b9e7d80f536317d45ec3a9e444beb7f242240 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 23:53:48 -0600 Subject: [PATCH 14/51] fix(etl): flush remaining items before updating totalProcessed + use ORM for completion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs caused phantom failures and inflated totalProcessed counts: 1. Remaining-items ordering: totalProcessed was incremented *before* the final valid/invalid flushes. If processValidItemsBatch or processLogsBatch threw, totalProcessed showed an inflated count while totalValid/totalInvalid stayed null — making job records misleading and harder to diagnose on retry. Fixed by flushing remaining items first, then updating totalProcessed. 2. Silent completion failure: the final status='completed' update used raw SQL with an ::etl_job_status cast that silently failed in some Neon HTTP driver versions. The inner try-catch swallowed the error, leaving the job in 'running' state so the reset-stuck sweep would mark it 'failed' even though all items were successfully ingested. Fixed by using the same db.update().set({ status: 'completed' }) pattern that already works reliably for the 'failed' path in the outer catch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../api/src/services/etl/processCatalogEtl.ts | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 7c3ee865d4..e9a29529cb 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -130,9 +130,22 @@ export async function processCatalogETL({ console.log(`🔍 [TRACE] Streaming complete - processing remaining batches`); - // Flush remaining items after the stream ends - const remainingItems = validItemsBatch.length + invalidItemsBatch.length; + // Flush remaining items BEFORE updating totalProcessed so that if a flush throws, + // totalProcessed isn't inflated while valid/invalid counts stay null. + const remainingValid = validItemsBatch.length; + const remainingInvalid = invalidItemsBatch.length; + + if (remainingValid > 0) { + console.log(`🔍 [TRACE] Processing valid items batch - size: ${remainingValid}`); + await processValidItemsBatch({ jobId, items: validItemsBatch, env }); + } + + if (remainingInvalid > 0) { + console.log(`🔍 [TRACE] Processing invalid items batch - size: ${remainingInvalid}`); + await processLogsBatch({ jobId, logs: invalidItemsBatch, env }); + } + const remainingItems = remainingValid + remainingInvalid; if (remainingItems > 0) { await db .update(etlJobs) @@ -140,32 +153,16 @@ export async function processCatalogETL({ .where(eq(etlJobs.id, jobId)); } - if (validItemsBatch.length > 0) { - console.log(`🔍 [TRACE] Processing valid items batch - size: ${validItemsBatch.length}`); - await processValidItemsBatch({ - jobId, - items: validItemsBatch, - env, - }); - } - - if (invalidItemsBatch.length > 0) { - console.log(`🔍 [TRACE] Processing invalid items batch - size: ${invalidItemsBatch.length}`); - await processLogsBatch({ - jobId, - logs: invalidItemsBatch, - env, - }); - } - const totalRows = rowIndex; - // Use raw SQL to avoid neon-http enum serialization issues with Drizzle ORM. + // Mark completed using Drizzle ORM (same as the failed path below) — avoids the + // silent failure that ::etl_job_status raw SQL casts produced in some Neon HTTP driver versions. // Isolated try-catch so a transient DB hiccup here doesn't cascade to status='failed'. try { - await db.execute( - sql`UPDATE etl_jobs SET status = 'completed'::etl_job_status, completed_at = NOW() WHERE id = ${jobId}`, - ); + await db + .update(etlJobs) + .set({ status: 'completed', completedAt: new Date() }) + .where(eq(etlJobs.id, jobId)); } catch (completionErr) { console.error( `[ETL] Failed to mark job ${jobId} completed — will be reset by stuck-job sweep:`, From 72ebd9ca8bd73379ce91a79f189a67ad8be17adb Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 00:23:00 -0600 Subject: [PATCH 15/51] fix(etl): yield to event loop every 100 rows instead of every row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per-row 1ms yield created a hard wall-clock floor of N ms for N rows. backcountry (142k rows) = 142s minimum; geartrade (210k) = 210s minimum — both guaranteed to exceed the CF Worker time limit before any processing. Yielding every 100 rows reduces overhead to ~1.4s for the largest files while still giving the GC breathing room on memory-heavy batches. --- packages/api/src/services/etl/processCatalogEtl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index e9a29529cb..2a9ceedb0d 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -73,7 +73,7 @@ export async function processCatalogETL({ })(); for await (const record of parser) { - await new Promise((resolve) => setTimeout(resolve, 1)); // Yield to event loop for GC Opportunities to prevent memory bloat + if (rowIndex % 100 === 0) await new Promise((resolve) => setTimeout(resolve, 1)); // Yield every 100 rows for GC; per-row yield hits the CF Worker wall-clock limit on large files const row = record as string[]; if (!isHeaderProcessed) { fieldMap = row.reduce>((acc, header, idx) => { From c7eba1a320c34b56e2b78bd7417e93f5f81f83cd Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 06:54:04 -0600 Subject: [PATCH 16/51] fix(og): generate static PNG OG images for landing and guides static exports (#2417) --- .gitignore | 5 + apps/guides/__tests__/og-image.test.ts | 113 ++++++++++ .../app/guide/[slug]/opengraph-image.tsx | 105 +-------- apps/guides/app/guide/[slug]/page.tsx | 2 + apps/guides/app/layout.tsx | 43 +--- apps/guides/app/opengraph-image.tsx | 95 +------- apps/guides/app/twitter-image.tsx | 72 +----- apps/guides/lib/metadata.ts | 44 ++++ apps/guides/lib/og-image.tsx | 210 ++++++++++++++++++ apps/guides/package.json | 7 +- apps/guides/scripts/generate-og-images.ts | 67 ++++++ apps/guides/vitest.config.ts | 18 ++ apps/landing/__tests__/og-image.test.ts | 62 ++++++ apps/landing/app/layout.tsx | 35 +-- apps/landing/app/opengraph-image.tsx | 92 +------- apps/landing/app/twitter-image.tsx | 72 +----- apps/landing/lib/metadata.ts | 36 +++ apps/landing/lib/og-image.tsx | 96 ++++++++ apps/landing/package.json | 9 +- apps/landing/scripts/generate-og-images.ts | 44 ++++ apps/landing/vitest.config.ts | 16 ++ bun.lock | 6 + package.json | 2 + 23 files changed, 780 insertions(+), 471 deletions(-) create mode 100644 apps/guides/__tests__/og-image.test.ts create mode 100644 apps/guides/lib/metadata.ts create mode 100644 apps/guides/lib/og-image.tsx create mode 100644 apps/guides/scripts/generate-og-images.ts create mode 100644 apps/guides/vitest.config.ts create mode 100644 apps/landing/__tests__/og-image.test.ts create mode 100644 apps/landing/lib/metadata.ts create mode 100644 apps/landing/lib/og-image.tsx create mode 100644 apps/landing/scripts/generate-og-images.ts create mode 100644 apps/landing/vitest.config.ts diff --git a/.gitignore b/.gitignore index b63f08dd25..7c94d60f1a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,11 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json .claude/scheduled_tasks.lock .dev.vars +# Generated OG images (produced at build time by scripts/generate-og-images.ts) +apps/landing/public/og-image.png +apps/guides/public/og-image.png +apps/guides/public/og/ + # Git worktrees .worktrees/ .worktrees diff --git a/apps/guides/__tests__/og-image.test.ts b/apps/guides/__tests__/og-image.test.ts new file mode 100644 index 0000000000..c910e48988 --- /dev/null +++ b/apps/guides/__tests__/og-image.test.ts @@ -0,0 +1,113 @@ +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { getAllPosts } from '../lib/mdx-static'; +import { guidesMetadata } from '../lib/metadata'; + +const APP_DIR = path.resolve(__dirname, '..'); +const PUBLIC_DIR = path.join(APP_DIR, 'public'); +const OG_DIR = path.join(PUBLIC_DIR, 'og'); +const ROOT_OG_PATH = path.join(PUBLIC_DIR, 'og-image.png'); + +const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); + +/** Read a uint32 big-endian from a buffer at offset. */ +function readUint32BE(buf: Buffer, offset: number): number { + return buf.readUInt32BE(offset); +} + +function assertValidPng(filePath: string): void { + const buf = fs.readFileSync(filePath); + expect(buf.subarray(0, 8), `${path.basename(filePath)} PNG signature`).toEqual(PNG_SIGNATURE); + const width = readUint32BE(buf, 16); + const height = readUint32BE(buf, 20); + expect(width, `${path.basename(filePath)} width`).toBe(1200); + expect(height, `${path.basename(filePath)} height`).toBe(630); + expect(buf.length, `${path.basename(filePath)} size`).toBeGreaterThan(1024); +} + +describe('guides OG image generation', () => { + beforeAll(() => { + execSync('bun run scripts/generate-og-images.ts', { + cwd: APP_DIR, + stdio: 'inherit', + }); + }); + + it('generates public/og-image.png', () => { + expect(fs.existsSync(ROOT_OG_PATH)).toBe(true); + }); + + it('root og-image.png is a valid 1200×630 PNG', () => { + assertValidPng(ROOT_OG_PATH); + }); + + it('generates public/og/ directory', () => { + expect(fs.existsSync(OG_DIR)).toBe(true); + }); + + it('generates a per-post PNG for every post', () => { + const posts = getAllPosts(); + expect(posts.length).toBeGreaterThan(0); + + for (const post of posts) { + const filePath = path.join(OG_DIR, `${post.slug}.png`); + expect(fs.existsSync(filePath), `missing: og/${post.slug}.png`).toBe(true); + } + }); + + it('every per-post PNG is a valid 1200×630 PNG', () => { + const posts = getAllPosts(); + for (const post of posts) { + assertValidPng(path.join(OG_DIR, `${post.slug}.png`)); + } + }); +}); + +describe('guides layout metadata', () => { + it('includes openGraph.images pointing to /og-image.png', () => { + const images = (guidesMetadata.openGraph as { images?: unknown })?.images; + expect(images).toBeDefined(); + const first = Array.isArray(images) ? images[0] : images; + const url = typeof first === 'string' ? first : (first as { url: string })?.url; + expect(url).toBe('/og-image.png'); + }); + + it('includes twitter.images pointing to /og-image.png', () => { + const images = (guidesMetadata.twitter as { images?: unknown })?.images; + expect(images).toBeDefined(); + const first = Array.isArray(images) ? images[0] : images; + expect(first).toBe('/og-image.png'); + }); +}); + +describe('guides per-slug page metadata', () => { + it('generateMetadata sets openGraph.images to /og/[slug].png', async () => { + // Dynamically import to avoid top-level JSX issues in test runner + const { generateMetadata } = await import('../app/guide/[slug]/page'); + const posts = getAllPosts(); + const post = posts[0]; + if (!post) throw new Error('No posts found'); + + const meta = await generateMetadata({ params: Promise.resolve({ slug: post.slug }) }); + const images = (meta.openGraph as { images?: unknown })?.images; + expect(images).toBeDefined(); + const first = Array.isArray(images) ? images[0] : images; + const url = typeof first === 'string' ? first : (first as { url: string })?.url; + expect(url).toBe(`/og/${post.slug}.png`); + }); + + it('generateMetadata sets twitter.images to /og/[slug].png', async () => { + const { generateMetadata } = await import('../app/guide/[slug]/page'); + const posts = getAllPosts(); + const post = posts[0]; + if (!post) throw new Error('No posts found'); + + const meta = await generateMetadata({ params: Promise.resolve({ slug: post.slug }) }); + const images = (meta.twitter as { images?: unknown })?.images; + expect(images).toBeDefined(); + const first = Array.isArray(images) ? images[0] : images; + expect(first).toBe(`/og/${post.slug}.png`); + }); +}); diff --git a/apps/guides/app/guide/[slug]/opengraph-image.tsx b/apps/guides/app/guide/[slug]/opengraph-image.tsx index 8c19a53c2b..08c0e696de 100644 --- a/apps/guides/app/guide/[slug]/opengraph-image.tsx +++ b/apps/guides/app/guide/[slug]/opengraph-image.tsx @@ -1,9 +1,14 @@ import { getAllPosts, getPostBySlug } from 'guides-app/lib/mdx-static'; +import { + getPostOgImageElement, + OG_IMAGE_CONTENT_TYPE, + OG_IMAGE_SIZE, +} from 'guides-app/lib/og-image'; import { ImageResponse } from 'next/og'; export const dynamic = 'force-static'; -export const size = { width: 1200, height: 630 }; -export const contentType = 'image/png'; +export const size = OG_IMAGE_SIZE; +export const contentType = OG_IMAGE_CONTENT_TYPE; export async function generateStaticParams() { return getAllPosts().map((post) => ({ slug: post.slug })); @@ -13,98 +18,12 @@ export default async function Image({ params }: { params: Promise<{ slug: string const { slug } = await params; const post = getPostBySlug(slug); - const title = post?.title ?? 'PackRat Guides'; - const description = post?.description ?? 'Expert hiking and outdoor guides'; - const categories = post?.categories ?? []; - return new ImageResponse( -
-
-
🏔️
-
- PackRat Guides -
-
- -
- {categories.length > 0 && ( -
- {categories.slice(0, 3).map((cat) => ( -
- {cat} -
- ))} -
- )} -
50 ? '44px' : '56px', - fontWeight: 700, - color: 'white', - lineHeight: 1.15, - letterSpacing: '-1px', - maxWidth: '900px', - }} - > - {title} -
-
- {description.length > 120 ? `${description.slice(0, 117)}...` : description} -
-
- -
- guides.packratai.com -
-
, + getPostOgImageElement({ + title: post?.title ?? 'PackRat Guides', + description: post?.description ?? 'Expert hiking and outdoor guides', + categories: post?.categories ?? [], + }), { ...size }, ); } diff --git a/apps/guides/app/guide/[slug]/page.tsx b/apps/guides/app/guide/[slug]/page.tsx index e84fd05de4..1e672ecf0d 100644 --- a/apps/guides/app/guide/[slug]/page.tsx +++ b/apps/guides/app/guide/[slug]/page.tsx @@ -39,12 +39,14 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str siteName: 'PackRat Guides', publishedTime: post.date, tags: post.categories, + images: [{ url: `/og/${slug}.png`, width: 1200, height: 630, alt: post.title }], }, twitter: { card: 'summary_large_image', title: post.title, description: post.description, creator: '@packratai', + images: [`/og/${slug}.png`], }, }; } diff --git a/apps/guides/app/layout.tsx b/apps/guides/app/layout.tsx index eb159f716b..03111135e8 100644 --- a/apps/guides/app/layout.tsx +++ b/apps/guides/app/layout.tsx @@ -3,8 +3,7 @@ import Footer from 'guides-app/components/footer'; import Header from 'guides-app/components/header'; import { QueryProvider } from 'guides-app/components/providers/query-provider'; import { ThemeProvider } from 'guides-app/components/theme-provider'; -import { siteConfig } from 'guides-app/lib/config'; -import type { Metadata } from 'next'; +import { guidesMetadata } from 'guides-app/lib/metadata'; import { Mona_Sans as FontSans } from 'next/font/google'; import type React from 'react'; import './globals.css'; @@ -15,45 +14,7 @@ const fontSans = FontSans({ weight: ['400', '500', '600', '700'], }); -export const metadata: Metadata = { - title: { - default: 'PackRat Guides | Hiking & Outdoor Adventures', - template: '%s | PackRat Guides', - }, - description: 'Expert hiking and outdoor guides to help you prepare for your next adventure', - keywords: [ - 'hiking guides', - 'outdoor adventures', - 'trail guides', - 'camping', - 'backpacking', - 'gear reviews', - 'wilderness skills', - 'outdoor planning', - ], - authors: [{ name: 'PackRat Team', url: 'https://packrat.world' }], - creator: 'PackRat Team', - metadataBase: new URL(siteConfig.url), - openGraph: { - type: 'website', - locale: 'en_US', - url: siteConfig.url, - siteName: 'PackRat Guides', - title: 'PackRat Guides | Hiking & Outdoor Adventures', - description: 'Expert hiking and outdoor guides to help you prepare for your next adventure', - }, - twitter: { - card: 'summary_large_image', - title: 'PackRat Guides | Hiking & Outdoor Adventures', - description: 'Expert hiking and outdoor guides to help you prepare for your next adventure', - creator: '@packratai', - }, - icons: { - icon: [{ url: '/PackRatGuides.ico', type: 'image/x-icon' }], - shortcut: '/favicon-16x16.png', - apple: '/apple-touch-icon.png', - }, -}; +export const metadata = guidesMetadata; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( diff --git a/apps/guides/app/opengraph-image.tsx b/apps/guides/app/opengraph-image.tsx index ade5c5fcc9..0dbd5a11ae 100644 --- a/apps/guides/app/opengraph-image.tsx +++ b/apps/guides/app/opengraph-image.tsx @@ -1,93 +1,14 @@ +import { + getGuidesOgImageElement, + OG_IMAGE_CONTENT_TYPE, + OG_IMAGE_SIZE, +} from 'guides-app/lib/og-image'; import { ImageResponse } from 'next/og'; export const dynamic = 'force-static'; -export const size = { width: 1200, height: 630 }; -export const contentType = 'image/png'; +export const size = OG_IMAGE_SIZE; +export const contentType = OG_IMAGE_CONTENT_TYPE; export default function Image() { - return new ImageResponse( -
-
-
- 🏔️ -
-
- PackRat Guides -
-
-
- Expert hiking and outdoor guides for your next adventure -
-
- {['Trail Guides', 'Gear Reviews', 'Survival Skills'].map((tag) => ( -
- {tag} -
- ))} -
-
, - { ...size }, - ); + return new ImageResponse(getGuidesOgImageElement(), { ...size }); } diff --git a/apps/guides/app/twitter-image.tsx b/apps/guides/app/twitter-image.tsx index 61da1f7581..0dbd5a11ae 100644 --- a/apps/guides/app/twitter-image.tsx +++ b/apps/guides/app/twitter-image.tsx @@ -1,70 +1,14 @@ +import { + getGuidesOgImageElement, + OG_IMAGE_CONTENT_TYPE, + OG_IMAGE_SIZE, +} from 'guides-app/lib/og-image'; import { ImageResponse } from 'next/og'; export const dynamic = 'force-static'; -export const size = { width: 1200, height: 630 }; -export const contentType = 'image/png'; +export const size = OG_IMAGE_SIZE; +export const contentType = OG_IMAGE_CONTENT_TYPE; export default function Image() { - return new ImageResponse( -
-
-
- 🏔️ -
-
- PackRat Guides -
-
-
- Expert hiking and outdoor guides for your next adventure -
-
, - { ...size }, - ); + return new ImageResponse(getGuidesOgImageElement(), { ...size }); } diff --git a/apps/guides/lib/metadata.ts b/apps/guides/lib/metadata.ts new file mode 100644 index 0000000000..f0e826959d --- /dev/null +++ b/apps/guides/lib/metadata.ts @@ -0,0 +1,44 @@ +import { siteConfig } from 'guides-app/lib/config'; +import type { Metadata } from 'next'; + +export const guidesMetadata: Metadata = { + title: { + default: 'PackRat Guides | Hiking & Outdoor Adventures', + template: '%s | PackRat Guides', + }, + description: 'Expert hiking and outdoor guides to help you prepare for your next adventure', + keywords: [ + 'hiking guides', + 'outdoor adventures', + 'trail guides', + 'camping', + 'backpacking', + 'gear reviews', + 'wilderness skills', + 'outdoor planning', + ], + authors: [{ name: 'PackRat Team', url: 'https://packrat.world' }], + creator: 'PackRat Team', + metadataBase: new URL(siteConfig.url), + openGraph: { + type: 'website', + locale: 'en_US', + url: siteConfig.url, + siteName: 'PackRat Guides', + title: 'PackRat Guides | Hiking & Outdoor Adventures', + description: 'Expert hiking and outdoor guides to help you prepare for your next adventure', + images: [{ url: '/og-image.png', width: 1200, height: 630, alt: 'PackRat Guides' }], + }, + twitter: { + card: 'summary_large_image', + title: 'PackRat Guides | Hiking & Outdoor Adventures', + description: 'Expert hiking and outdoor guides to help you prepare for your next adventure', + creator: '@packratai', + images: ['/og-image.png'], + }, + icons: { + icon: [{ url: '/PackRatGuides.ico', type: 'image/x-icon' }], + shortcut: '/favicon-16x16.png', + apple: '/apple-touch-icon.png', + }, +}; diff --git a/apps/guides/lib/og-image.tsx b/apps/guides/lib/og-image.tsx new file mode 100644 index 0000000000..e20432ca81 --- /dev/null +++ b/apps/guides/lib/og-image.tsx @@ -0,0 +1,210 @@ +import type { ReactElement } from 'react'; + +export const OG_IMAGE_SIZE = { width: 1200, height: 630 } as const; +export const OG_IMAGE_CONTENT_TYPE = 'image/png' as const; + +/** Returns the JSX element for the root Guides Open Graph / Twitter card image. */ +export function getGuidesOgImageElement(): ReactElement { + return ( +
+
+
+
+
+
+ PackRat Guides +
+
+
+ Expert hiking and outdoor guides for your next adventure +
+
+ {['Trail Guides', 'Gear Reviews', 'Survival Skills'].map((tag) => ( +
+ {tag} +
+ ))} +
+
+ ); +} + +export interface PostOgImageProps { + title: string; + description: string; + categories?: string[]; +} + +/** Returns the JSX element for a per-guide-post Open Graph image. */ +export function getPostOgImageElement({ + title, + description, + categories = [], +}: PostOgImageProps): ReactElement { + return ( +
+
+
+
+ PackRat Guides +
+
+ +
+ {categories.length > 0 && ( +
+ {categories.slice(0, 3).map((cat) => ( +
+ {cat} +
+ ))} +
+ )} +
50 ? '44px' : '56px', + fontWeight: 700, + color: 'white', + lineHeight: 1.15, + letterSpacing: '-1px', + maxWidth: '900px', + }} + > + {title} +
+
+ {description.length > 120 ? `${description.slice(0, 117)}...` : description} +
+
+ +
+ guides.packratai.com +
+
+ ); +} diff --git a/apps/guides/package.json b/apps/guides/package.json index b0331cc335..7c3ae05e6d 100644 --- a/apps/guides/package.json +++ b/apps/guides/package.json @@ -3,17 +3,19 @@ "version": "2.0.25", "private": true, "scripts": { - "build": "bun run build-content && next build", + "build": "bun run generate-og-images && bun run build-content && next build", "build-content": "bun run scripts/build-content.ts", "clean": "bunx rimraf .next node_modules out", "demo-enhancement": "bun run scripts/demo-enhancement.ts", "dev": "next dev", "doctor:react": "bunx react-doctor", "enhance-content": "bun run scripts/enhance-content.ts", + "generate-og-images": "bun run scripts/generate-og-images.ts", "lighthouse": "bun run build && bunx lhci autorun", "lint": "next lint", "start": "next start", "sync-to-r2": "bun run scripts/sync-to-r2.ts", + "test": "vitest run --config vitest.config.ts", "test-enhancement": "bun run scripts/test-enhancement.ts", "update-authors": "bun run scripts/update-authors.ts" }, @@ -96,6 +98,7 @@ "postcss": "^8.5.6", "postcss-import": "^16.1.1", "tailwindcss": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "~3.1.4" } } diff --git a/apps/guides/scripts/generate-og-images.ts b/apps/guides/scripts/generate-og-images.ts new file mode 100644 index 0000000000..104bd96ed6 --- /dev/null +++ b/apps/guides/scripts/generate-og-images.ts @@ -0,0 +1,67 @@ +/** + * Pre-build script: generates static Open Graph image PNGs for the guides site. + * + * Static exports (`output: 'export'`) cannot serve Next.js metadata-route images + * (opengraph-image.tsx) correctly from a CDN — the generated .body/.meta files + * are a Next.js-internal format, not plain PNG files. + * + * This script renders the same JSX used in opengraph-image.tsx via ImageResponse + * and writes real .png files to public/ so Cloudflare Workers can serve them with + * the correct Content-Type automatically. + * + * Outputs: + * public/og-image.png — root / site-level OG image + * public/og/[slug].png — per-post OG images + * + * Run: `bun run scripts/generate-og-images.ts` + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { ImageResponse } from 'next/og'; +import { createElement } from 'react'; +import { getAllPosts } from '../lib/mdx-static'; +import { getGuidesOgImageElement, getPostOgImageElement, OG_IMAGE_SIZE } from '../lib/og-image'; + +const PUBLIC_DIR = path.join(import.meta.dir, '..', 'public'); +const OG_DIR = path.join(PUBLIC_DIR, 'og'); + +async function renderToPng(element: ReturnType): Promise { + const response = new ImageResponse( + createElement(() => element), + OG_IMAGE_SIZE, + ); + return Buffer.from(await response.arrayBuffer()); +} + +async function generateOgImages(): Promise { + fs.mkdirSync(OG_DIR, { recursive: true }); + + // Root site image + const rootBuffer = await renderToPng(getGuidesOgImageElement()); + const rootPath = path.join(PUBLIC_DIR, 'og-image.png'); + fs.writeFileSync(rootPath, rootBuffer); + console.log(`✓ Generated ${path.relative(process.cwd(), rootPath)} (${rootBuffer.length} bytes)`); + + // Per-post images + const posts = getAllPosts(); + for (const post of posts) { + const buffer = await renderToPng( + getPostOgImageElement({ + title: post.title, + description: post.description ?? '', + categories: post.categories ?? [], + }), + ); + const outPath = path.join(OG_DIR, `${post.slug}.png`); + fs.writeFileSync(outPath, buffer); + console.log(`✓ Generated ${path.relative(process.cwd(), outPath)} (${buffer.length} bytes)`); + } + + console.log(`\nDone — generated 1 root + ${posts.length} post OG images.`); +} + +generateOgImages().catch((err) => { + console.error('Failed to generate OG images:', err); + process.exit(1); +}); diff --git a/apps/guides/vitest.config.ts b/apps/guides/vitest.config.ts new file mode 100644 index 0000000000..7eb8b7863d --- /dev/null +++ b/apps/guides/vitest.config.ts @@ -0,0 +1,18 @@ +import { resolve } from 'node:path'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + resolve: { + alias: { + 'guides-app': resolve(__dirname, '.'), + }, + }, + test: { + name: 'guides-og', + environment: 'node', + globals: true, + include: [resolve(__dirname, '__tests__/**/*.test.ts')], + hookTimeout: 60_000, + testTimeout: 15_000, + }, +}); diff --git a/apps/landing/__tests__/og-image.test.ts b/apps/landing/__tests__/og-image.test.ts new file mode 100644 index 0000000000..1d595a1552 --- /dev/null +++ b/apps/landing/__tests__/og-image.test.ts @@ -0,0 +1,62 @@ +import { execSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { beforeAll, describe, expect, it } from 'vitest'; +import { landingMetadata } from '../lib/metadata'; + +const OG_IMAGE_PATH = path.resolve(__dirname, '../public/og-image.png'); +const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); + +/** Read a uint32 big-endian from a buffer at offset. */ +function readUint32BE(buf: Buffer, offset: number): number { + return buf.readUInt32BE(offset); +} + +describe('landing OG image generation', () => { + beforeAll(() => { + execSync('bun run scripts/generate-og-images.ts', { + cwd: path.resolve(__dirname, '..'), + stdio: 'inherit', + }); + }); + + it('generates public/og-image.png', () => { + expect(fs.existsSync(OG_IMAGE_PATH)).toBe(true); + }); + + it('output is a valid PNG file', () => { + const buf = fs.readFileSync(OG_IMAGE_PATH); + expect(buf.subarray(0, 8)).toEqual(PNG_SIGNATURE); + }); + + it('PNG has correct dimensions (1200 × 630)', () => { + const buf = fs.readFileSync(OG_IMAGE_PATH); + // IHDR chunk starts at byte 16; first 4 bytes = width, next 4 = height + const width = readUint32BE(buf, 16); + const height = readUint32BE(buf, 20); + expect(width).toBe(1200); + expect(height).toBe(630); + }); + + it('PNG is non-trivially sized (> 1 KB)', () => { + const { size } = fs.statSync(OG_IMAGE_PATH); + expect(size).toBeGreaterThan(1024); + }); +}); + +describe('landing metadata', () => { + it('includes openGraph.images pointing to /og-image.png', () => { + const images = (landingMetadata.openGraph as { images?: unknown })?.images; + expect(images).toBeDefined(); + const first = Array.isArray(images) ? images[0] : images; + const url = typeof first === 'string' ? first : (first as { url: string })?.url; + expect(url).toBe('/og-image.png'); + }); + + it('includes twitter.images pointing to /og-image.png', () => { + const images = (landingMetadata.twitter as { images?: unknown })?.images; + expect(images).toBeDefined(); + const first = Array.isArray(images) ? images[0] : images; + expect(first).toBe('/og-image.png'); + }); +}); diff --git a/apps/landing/app/layout.tsx b/apps/landing/app/layout.tsx index beb509206a..2324e29eb7 100644 --- a/apps/landing/app/layout.tsx +++ b/apps/landing/app/layout.tsx @@ -2,8 +2,7 @@ import { cn } from '@packrat/web-ui/lib/utils'; import MainNav from 'landing-app/components/main-nav'; import SiteFooter from 'landing-app/components/site-footer'; import { ThemeProvider } from 'landing-app/components/theme-provider'; -import { siteConfig } from 'landing-app/config/site'; -import type { Metadata } from 'next'; +import { landingMetadata } from 'landing-app/lib/metadata'; import { Mona_Sans as FontSans } from 'next/font/google'; import type React from 'react'; import './globals.css'; @@ -14,37 +13,7 @@ const fontSans = FontSans({ weight: ['400', '500', '600', '700'], }); -export const metadata: Metadata = { - title: { - default: siteConfig.name, - template: `%s | ${siteConfig.name}`, - }, - description: siteConfig.description, - keywords: siteConfig.keywords, - authors: [{ name: siteConfig.author, url: siteConfig.url }], - creator: siteConfig.author, - metadataBase: new URL(siteConfig.url), - openGraph: { - type: 'website', - locale: 'en_US', - url: siteConfig.url, - title: siteConfig.name, - description: siteConfig.description, - siteName: siteConfig.name, - }, - twitter: { - card: 'summary_large_image', - title: siteConfig.name, - description: siteConfig.description, - creator: siteConfig.twitterHandle, - }, - icons: { - icon: '/PackRat.ico', - shortcut: '/favicon-16x16.png', - apple: '/apple-touch-icon.png', - }, - manifest: `${siteConfig.url}/site.webmanifest`, -}; +export const metadata = landingMetadata; export default function RootLayout({ children, diff --git a/apps/landing/app/opengraph-image.tsx b/apps/landing/app/opengraph-image.tsx index f4ed493119..e79b53bca3 100644 --- a/apps/landing/app/opengraph-image.tsx +++ b/apps/landing/app/opengraph-image.tsx @@ -1,90 +1,14 @@ +import { + getLandingOgImageElement, + OG_IMAGE_CONTENT_TYPE, + OG_IMAGE_SIZE, +} from 'landing-app/lib/og-image'; import { ImageResponse } from 'next/og'; export const dynamic = 'force-static'; -export const size = { width: 1200, height: 630 }; -export const contentType = 'image/png'; +export const size = OG_IMAGE_SIZE; +export const contentType = OG_IMAGE_CONTENT_TYPE; export default function Image() { - return new ImageResponse( -
-
-
- 🎒 -
-
- PackRat -
-
-
- Stop overpacking. Start adventuring. -
-
- {['10K+ Users', '4.8★ Rating', '100% Free'].map((stat) => ( -
- {stat} -
- ))} -
-
, - { ...size }, - ); + return new ImageResponse(getLandingOgImageElement(), { ...size }); } diff --git a/apps/landing/app/twitter-image.tsx b/apps/landing/app/twitter-image.tsx index c105da691f..e79b53bca3 100644 --- a/apps/landing/app/twitter-image.tsx +++ b/apps/landing/app/twitter-image.tsx @@ -1,70 +1,14 @@ +import { + getLandingOgImageElement, + OG_IMAGE_CONTENT_TYPE, + OG_IMAGE_SIZE, +} from 'landing-app/lib/og-image'; import { ImageResponse } from 'next/og'; export const dynamic = 'force-static'; -export const size = { width: 1200, height: 630 }; -export const contentType = 'image/png'; +export const size = OG_IMAGE_SIZE; +export const contentType = OG_IMAGE_CONTENT_TYPE; export default function Image() { - return new ImageResponse( -
-
-
- 🎒 -
-
- PackRat -
-
-
- Your AI-powered outdoor adventure companion. Free forever. -
-
, - { ...size }, - ); + return new ImageResponse(getLandingOgImageElement(), { ...size }); } diff --git a/apps/landing/lib/metadata.ts b/apps/landing/lib/metadata.ts new file mode 100644 index 0000000000..7dce150a79 --- /dev/null +++ b/apps/landing/lib/metadata.ts @@ -0,0 +1,36 @@ +import { siteConfig } from 'landing-app/config/site'; +import type { Metadata } from 'next'; + +export const landingMetadata: Metadata = { + title: { + default: siteConfig.name, + template: `%s | ${siteConfig.name}`, + }, + description: siteConfig.description, + keywords: siteConfig.keywords, + authors: [{ name: siteConfig.author, url: siteConfig.url }], + creator: siteConfig.author, + metadataBase: new URL(siteConfig.url), + openGraph: { + type: 'website', + locale: 'en_US', + url: siteConfig.url, + title: siteConfig.name, + description: siteConfig.description, + siteName: siteConfig.name, + images: [{ url: '/og-image.png', width: 1200, height: 630, alt: siteConfig.name }], + }, + twitter: { + card: 'summary_large_image', + title: siteConfig.name, + description: siteConfig.description, + creator: siteConfig.twitterHandle, + images: ['/og-image.png'], + }, + icons: { + icon: '/PackRat.ico', + shortcut: '/favicon-16x16.png', + apple: '/apple-touch-icon.png', + }, + manifest: `${siteConfig.url}/site.webmanifest`, +}; diff --git a/apps/landing/lib/og-image.tsx b/apps/landing/lib/og-image.tsx new file mode 100644 index 0000000000..f60dab071b --- /dev/null +++ b/apps/landing/lib/og-image.tsx @@ -0,0 +1,96 @@ +import type { ReactElement } from 'react'; + +export const OG_IMAGE_SIZE = { width: 1200, height: 630 } as const; +export const OG_IMAGE_CONTENT_TYPE = 'image/png' as const; + +/** Returns the JSX element for the landing Open Graph / Twitter card image. */ +export function getLandingOgImageElement(): ReactElement { + return ( +
+
+
+
+
+
+ PackRat +
+
+
+ Stop overpacking. Start adventuring. +
+
+ {['10K+ Users', '4.8★ Rating', '100% Free'].map((stat) => ( +
+ {stat} +
+ ))} +
+
+ ); +} diff --git a/apps/landing/package.json b/apps/landing/package.json index e2cbe350c5..50886e76d3 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -3,13 +3,15 @@ "version": "2.0.25", "private": true, "scripts": { - "build": "next build", + "build": "bun run generate-og-images && next build", "clean": "bunx rimraf node_modules .next out", "dev": "next dev", "doctor:react": "bunx react-doctor", + "generate-og-images": "bun run scripts/generate-og-images.ts", "lighthouse": "bun run build && bunx lhci autorun", "lint": "next lint", - "start": "next start" + "start": "next start", + "test": "vitest run --config vitest.config.ts" }, "dependencies": { "@emotion/is-prop-valid": "^1.3.1", @@ -72,6 +74,7 @@ "postcss": "^8.5.6", "postcss-import": "^16.1.1", "tailwindcss": "catalog:", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "~3.1.4" } } diff --git a/apps/landing/scripts/generate-og-images.ts b/apps/landing/scripts/generate-og-images.ts new file mode 100644 index 0000000000..8af8ba5671 --- /dev/null +++ b/apps/landing/scripts/generate-og-images.ts @@ -0,0 +1,44 @@ +/** + * Pre-build script: generates static Open Graph image PNGs for the landing site. + * + * Static exports (`output: 'export'`) cannot serve Next.js metadata-route images + * (opengraph-image.tsx) correctly from a CDN — the generated .body/.meta files + * are a Next.js-internal format, not plain PNG files. + * + * This script renders the same JSX used in opengraph-image.tsx via ImageResponse + * and writes a real .png file to public/ so Cloudflare Workers can serve it with + * the correct Content-Type automatically. + * + * Run: `bun run scripts/generate-og-images.ts` + * Output: apps/landing/public/og-image.png + */ + +import fs from 'node:fs'; +import path from 'node:path'; +import { ImageResponse } from 'next/og'; +import { createElement } from 'react'; +import { getLandingOgImageElement, OG_IMAGE_SIZE } from '../lib/og-image'; + +const PUBLIC_DIR = path.join(import.meta.dir, '..', 'public'); + +async function generateOgImages(): Promise { + if (!fs.existsSync(PUBLIC_DIR)) { + fs.mkdirSync(PUBLIC_DIR, { recursive: true }); + } + + const response = new ImageResponse( + createElement(() => getLandingOgImageElement()), + OG_IMAGE_SIZE, + ); + + const buffer = Buffer.from(await response.arrayBuffer()); + const outputPath = path.join(PUBLIC_DIR, 'og-image.png'); + fs.writeFileSync(outputPath, buffer); + + console.log(`✓ Generated ${path.relative(process.cwd(), outputPath)} (${buffer.length} bytes)`); +} + +generateOgImages().catch((err) => { + console.error('Failed to generate OG images:', err); + process.exit(1); +}); diff --git a/apps/landing/vitest.config.ts b/apps/landing/vitest.config.ts new file mode 100644 index 0000000000..859d8c078b --- /dev/null +++ b/apps/landing/vitest.config.ts @@ -0,0 +1,16 @@ +import { resolve } from 'node:path'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + resolve: { + alias: { + 'landing-app': resolve(__dirname, '.'), + }, + }, + test: { + name: 'landing-og', + environment: 'node', + globals: true, + include: [resolve(__dirname, '__tests__/**/*.test.ts')], + }, +}); diff --git a/bun.lock b/bun.lock index cfb54dacbe..aaa5027be7 100644 --- a/bun.lock +++ b/bun.lock @@ -279,6 +279,7 @@ "postcss-import": "^16.1.1", "tailwindcss": "catalog:", "typescript": "catalog:", + "vitest": "~3.1.4", }, }, "apps/landing": { @@ -346,6 +347,7 @@ "postcss-import": "^16.1.1", "tailwindcss": "catalog:", "typescript": "catalog:", + "vitest": "~3.1.4", }, }, "apps/trails": { @@ -4657,6 +4659,8 @@ "@modelcontextprotocol/sdk/jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + "@packrat/api/@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], "@poppinss/colors/kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], @@ -5333,6 +5337,8 @@ "@manypkg/tools/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "@packrat/api/@types/bun/bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], + "@react-native-ai/apple/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@react-native-ai/llama/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], diff --git a/package.json b/package.json index 77d64f21a6..9bb4facf0f 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,8 @@ "test:e2e:ios": "bash .github/scripts/e2e.sh ios", "test:expo": "vitest run --config apps/expo/vitest.config.ts", "test:expo:rpc-types": "vitest run --config apps/expo/vitest.types.config.ts", + "test:guides": "vitest run --config apps/guides/vitest.config.ts", + "test:landing": "vitest run --config apps/landing/vitest.config.ts", "test:mcp": "bun run --cwd packages/mcp test", "trails": "bun run --cwd apps/trails dev", "web": "bun run --cwd apps/web dev" From 765412324cf93eb31e4d5223ff29e187066b429e Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 12:38:21 -0600 Subject: [PATCH 17/51] fix(etl): respect stream backpressure to prevent Worker OOM on large files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without drain-wait, R2 delivers the entire file into the csv-parse buffer before the main processing loop can drain any rows. For files >50 MB this fills the 128 MB Worker memory limit → Worker killed externally → outer catch never runs → job stays stuck in 'running'. Fix: check the return value of parser.write() and await 'drain' before pushing more chunks, exactly as the Node.js streams backpressure contract requires. --- packages/api/src/services/etl/processCatalogEtl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 2a9ceedb0d..7637e2ef03 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -67,7 +67,11 @@ export async function processCatalogETL({ (async () => { for await (const chunk of streamToText(r2Object.body)) { - parser.write(chunk); + // Respect backpressure: if the parser buffer is full, wait for drain before + // pushing more data. Without this, R2 fills the parser buffer for the entire + // file (up to 600 MB) before the main loop processes any rows → Worker OOM. + const ok = parser.write(chunk); + if (!ok) await new Promise((resolve) => parser.once('drain', resolve)); } parser.end(); })(); From df9d5274dbb40eb0b7e430587e9c64c2804543c2 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 13:19:09 -0600 Subject: [PATCH 18/51] fix(etl): raise cpu_ms limit to 400k (CF max) for large-file queue processing 50k-row files exhaust the 300k CPU budget (~6ms CPU/row for 42-field CSV). This extends the per-invocation limit to ~66k rows. Chunking in ScrapyD is the long-term fix for files beyond that threshold. --- packages/api/wrangler.jsonc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/wrangler.jsonc b/packages/api/wrangler.jsonc index 6ca2ec58df..9297ec0c96 100644 --- a/packages/api/wrangler.jsonc +++ b/packages/api/wrangler.jsonc @@ -16,7 +16,7 @@ "head_sampling_rate": 1 }, "limits": { - "cpu_ms": 300000 // 300,000 milliseconds = 5 minutes + "cpu_ms": 400000 // 400,000 milliseconds = max allowed by Cloudflare }, // Environment variables are managed via: // - Production: Cloudflare dashboard From 0715bd9e285be6c8f87ea495f66dcac55f8be69e Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 13:46:57 -0600 Subject: [PATCH 19/51] feat(etl): split large R2 files into 20 MB byte-range chunks at queue time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of streaming the full file (up to 600 MB) in one Worker invocation, the notify endpoint now HEADs each object and splits files >20 MB into sequential byte-range messages. Each Worker invocation fetches only its slice via a Range request (~30k rows) — well within the 400k CPU budget. Non-first chunks fetch the header row via a separate 4 KB range request and skip the partial row at the chunk boundary. No ScrapyD changes needed. --- .../api/src/routes/admin/analytics/catalog.ts | 2 +- packages/api/src/routes/catalog/index.ts | 25 +++++++++- .../api/src/services/etl/processCatalogEtl.ts | 50 +++++++++++++++++-- packages/api/src/services/etl/queue.ts | 8 +-- packages/api/src/services/etl/types.ts | 8 +-- 5 files changed, 78 insertions(+), 15 deletions(-) diff --git a/packages/api/src/routes/admin/analytics/catalog.ts b/packages/api/src/routes/admin/analytics/catalog.ts index 936f5a3eae..9a3490395e 100644 --- a/packages/api/src/routes/admin/analytics/catalog.ts +++ b/packages/api/src/routes/admin/analytics/catalog.ts @@ -432,7 +432,7 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) startedAt: new Date(), }); - await queueCatalogETL({ queue: env.ETL_QUEUE, objectKeys: [objectKey], jobId: newJobId }); + await queueCatalogETL({ queue: env.ETL_QUEUE, chunks: [{ objectKey }], jobId: newJobId }); return { success: true, newJobId, objectKey }; } catch (error) { diff --git a/packages/api/src/routes/catalog/index.ts b/packages/api/src/routes/catalog/index.ts index adb9f7f32f..8e6f31b814 100644 --- a/packages/api/src/routes/catalog/index.ts +++ b/packages/api/src/routes/catalog/index.ts @@ -10,6 +10,7 @@ import { import { CatalogService } from '@packrat/api/services'; import { generateEmbedding } from '@packrat/api/services/embeddingService'; import { queueCatalogETL } from '@packrat/api/services/etl/queue'; +import { R2BucketService } from '@packrat/api/services/r2-bucket'; import { getEmbeddingText } from '@packrat/api/utils/embeddingHelper'; import { getEnv } from '@packrat/api/utils/env-validation'; import { isString } from '@packrat/guards'; @@ -176,9 +177,31 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) startedAt: new Date(), }); + // Split large files into 20 MB byte-range chunks so each Worker + // invocation stays within the CPU time budget (~30k rows / chunk). + const CHUNK_BYTES = 20 * 1024 * 1024; + const r2 = new R2BucketService({ env, bucketType: 'catalog' }); + const queueChunks: Array<{ objectKey: string; byteStart?: number; byteEnd?: number }> = []; + + for (const objectKey of chunks) { + const meta = await r2.head(objectKey); + if (!meta || meta.size <= CHUNK_BYTES) { + queueChunks.push({ objectKey }); + } else { + const n = Math.ceil(meta.size / CHUNK_BYTES); + for (let i = 0; i < n; i++) { + queueChunks.push({ + objectKey, + byteStart: i * CHUNK_BYTES, + byteEnd: Math.min((i + 1) * CHUNK_BYTES - 1, meta.size - 1), + }); + } + } + } + await queueCatalogETL({ queue: env.ETL_QUEUE, - objectKeys: chunks, + chunks: queueChunks, jobId, }); diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 7637e2ef03..05420aaede 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -33,21 +33,42 @@ export async function processCatalogETL({ message: CatalogETLMessage; env: Env; }): Promise { - const { objectKey } = message.data; + const { objectKey, byteStart, byteEnd } = message.data; const jobId = message.id; const db = createDbClient(env); try { - console.log(`🔄 Processing file ${objectKey}, job ${jobId}`); + const chunkDesc = byteStart !== undefined ? ` [bytes ${byteStart}-${byteEnd ?? 'end'}]` : ''; + console.log(`🔄 Processing file ${objectKey}${chunkDesc}, job ${jobId}`); const r2Service = new R2BucketService({ env, bucketType: 'catalog', }); - console.log(`🔍 [TRACE] Getting stream for object: ${objectKey}`); - const r2Object = await r2Service.get(objectKey); + // For non-first chunks (byteStart > 0): fetch the header row separately via a + // cheap 4 KB range request so the CSV parser sees a valid header. + let injectedHeader = ''; + if (byteStart !== undefined && byteStart > 0) { + const headerSlice = await r2Service.get(objectKey, { range: { offset: 0, length: 4096 } }); + if (!headerSlice) throw new Error(`Failed to fetch header for ${objectKey}`); + const headerText = await headerSlice.text(); + injectedHeader = headerText.split('\n')[0] ?? ''; + } + + const rangeOptions = + byteStart !== undefined + ? { + range: { + offset: byteStart, + length: byteEnd !== undefined ? byteEnd - byteStart + 1 : undefined, + }, + } + : undefined; + + console.log(`🔍 [TRACE] Getting stream for object: ${objectKey}${chunkDesc}`); + const r2Object = await r2Service.get(objectKey, rangeOptions); if (!r2Object) { throw new Error(`Failed to get stream for object: ${objectKey}`); } @@ -66,11 +87,30 @@ export async function processCatalogETL({ }); (async () => { + // Non-first chunks: inject the header row so csv-parse sees a valid header, + // then skip the partial row at the chunk boundary (tail of the previous chunk). + if (injectedHeader) { + parser.write(`${injectedHeader}\n`); + } + let skipPartialRow = byteStart !== undefined && byteStart > 0; + for await (const chunk of streamToText(r2Object.body)) { + let text = chunk; + + if (skipPartialRow) { + // Discard bytes up to and including the first newline — those bytes are + // the tail of the row that the previous chunk already processed. + const nl = text.indexOf('\n'); + if (nl === -1) continue; // entire buffer is still the partial row tail + text = text.slice(nl + 1); + skipPartialRow = false; + if (!text) continue; + } + // Respect backpressure: if the parser buffer is full, wait for drain before // pushing more data. Without this, R2 fills the parser buffer for the entire // file (up to 600 MB) before the main loop processes any rows → Worker OOM. - const ok = parser.write(chunk); + const ok = parser.write(text); if (!ok) await new Promise((resolve) => parser.once('drain', resolve)); } parser.end(); diff --git a/packages/api/src/services/etl/queue.ts b/packages/api/src/services/etl/queue.ts index 0bc5f99db9..cbb4730c29 100644 --- a/packages/api/src/services/etl/queue.ts +++ b/packages/api/src/services/etl/queue.ts @@ -5,11 +5,11 @@ import type { CatalogETLMessage } from './types'; export async function queueCatalogETL({ queue, - objectKeys, + chunks, jobId, }: { queue: Queue; - objectKeys: string[]; + chunks: Array<{ objectKey: string; byteStart?: number; byteEnd?: number }>; jobId: string; }): Promise { const promises: Promise[] = []; @@ -17,14 +17,14 @@ export async function queueCatalogETL({ const batchSize = 100; // maximum batch size Cloudflare allows let batch: { body: CatalogETLMessage }[] = []; - for (const objectKey of objectKeys) { + for (const { objectKey, byteStart, byteEnd } of chunks) { if (batch.length === batchSize) { promises.push(queue.sendBatch(batch)); batch = []; } const message: CatalogETLMessage = { - data: { objectKey }, + data: { objectKey, byteStart, byteEnd }, timestamp: Date.now(), id: jobId, }; diff --git a/packages/api/src/services/etl/types.ts b/packages/api/src/services/etl/types.ts index 1bcd906c8b..be35e39eaf 100644 --- a/packages/api/src/services/etl/types.ts +++ b/packages/api/src/services/etl/types.ts @@ -5,13 +5,13 @@ export interface CatalogETLMessage { id: string; data: { objectKey: string; + byteStart?: number; + byteEnd?: number; }; } export interface QueueCatalogETLParams { queue: Queue; - objectKey: string; - userId: string; - source: string; - scraperRevision: string; + chunks: Array<{ objectKey: string; byteStart?: number; byteEnd?: number }>; + jobId: string; } From a73ed223f8ecc3e078a8daaccb699a8532beeecf Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 00:36:02 -0600 Subject: [PATCH 20/51] chore(deps): enroll 47 third-party packages into Bun workspace catalog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all CATALOG CANDIDATE dependencies (same version pinned in 2+ workspaces) into the root catalog section. Replace pinned version strings with "catalog:" in 16 workspace package.json files (164 replacements total). Root catalog grows from 46 → 93 entries. The no-duplicate-deps pre-push check now shows 0 third-party candidates; only workspace:* internal refs remain (intentional — they use the workspace protocol, not semver). --- apps/admin/package.json | 32 +- apps/expo/package.json | 28 +- apps/guides/package.json | 54 +- apps/landing/package.json | 46 +- apps/trails/package.json | 30 +- apps/web/package.json | 26 +- bun.lock | 1161 ++++++++--------- ...13-chore-enroll-catalog-candidates-plan.md | 122 ++ package.json | 47 + packages/analytics/package.json | 8 +- packages/api/package.json | 30 +- packages/app/package.json | 8 +- packages/cli/package.json | 6 +- packages/mcp/package.json | 8 +- packages/osm-db/package.json | 10 +- packages/osm-import/package.json | 2 +- packages/overpass/package.json | 2 +- packages/units/package.json | 2 +- packages/web-ui/package.json | 36 +- 19 files changed, 882 insertions(+), 776 deletions(-) create mode 100644 docs/plans/2026-05-13-chore-enroll-catalog-candidates-plan.md diff --git a/apps/admin/package.json b/apps/admin/package.json index 747634eb26..a0026c80e4 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -28,29 +28,29 @@ "@radix-ui/react-slot": "catalog:", "@radix-ui/react-tabs": "catalog:", "@radix-ui/react-tooltip": "catalog:", - "@tanstack/react-query": "^5.70.0", - "@types/leaflet": "^1.9.21", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "leaflet": "^1.9.4", - "lucide-react": "^1.8.0", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "@tanstack/react-query": "catalog:", + "@types/leaflet": "catalog:", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "leaflet": "catalog:", + "lucide-react": "catalog:", + "next": "catalog:", + "next-themes": "catalog:", "nuqs": "^2.8.9", "react": "catalog:", "react-dom": "catalog:", "react-error-boundary": "^6.1.1", - "recharts": "3.8.1", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", + "recharts": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", "zod": "catalog:" }, "devDependencies": { - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:" } diff --git a/apps/expo/package.json b/apps/expo/package.json index a2f8a08bbc..66c209211d 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -81,13 +81,13 @@ "@shopify/flash-list": "2.0.2", "@stardazed/streams-text-encoding": "^1.0.2", "@tanstack/react-form": "^1.0.5", - "@tanstack/react-query": "^5.70.0", + "@tanstack/react-query": "catalog:", "ai": "catalog:", - "better-auth": "^1.6.9", + "better-auth": "catalog:", "burnt": "^0.13.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "date-fns": "^4.1.0", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "date-fns": "catalog:", "expo": "~55.0.17", "expo-apple-authentication": "~55.0.13", "expo-blur": "~55.0.14", @@ -116,12 +116,12 @@ "expo-system-ui": "~55.0.17", "expo-updates": "~55.0.21", "expo-web-browser": "~55.0.15", - "google-auth-library": "^10.1.0", + "google-auth-library": "catalog:", "he": "^1.2.0", "i": "^0.3.7", "i18next": "^25.8.18", - "jotai": "^2.12.2", - "leaflet": "^1.9.4", + "jotai": "catalog:", + "leaflet": "catalog:", "llama.rn": "0.10.1", "nanoid": "^5.1.9", "nativewind": "^4.2.3", @@ -129,7 +129,7 @@ "react": "catalog:", "react-dom": "catalog:", "react-i18next": "^17.0.4", - "react-leaflet": "^5.0.0", + "react-leaflet": "catalog:", "react-native": "0.83.6", "react-native-blob-util": "^0.24.5", "react-native-css-interop": "^0.2.3", @@ -147,7 +147,7 @@ "react-native-web": "^0.21.0", "react-native-worklets": "0.7.4", "rn-icon-mapper": "^0.0.1", - "tailwind-merge": "^3.5.0", + "tailwind-merge": "catalog:", "use-debounce": "^10.0.5", "zod": "catalog:" }, @@ -155,12 +155,12 @@ "@babel/core": "^7.20.0", "@biomejs/biome": "2.4.6", "@types/he": "^1.2.3", - "@types/leaflet": "^1.9.21", - "@types/react": "~19.2.10", + "@types/leaflet": "catalog:", + "@types/react": "catalog:", "@types/ungap__structured-clone": "^1.2.0", "@typescript-eslint/eslint-plugin": "^7.7.0", "@typescript-eslint/parser": "^7.7.0", - "@vitest/coverage-v8": "~3.1.4", + "@vitest/coverage-v8": "catalog:", "ajv": "^8.12.0", "eslint": "^8.57.0", "eslint-config-universe": "^15.0.3", @@ -169,7 +169,7 @@ "rimraf": "^6.0.1", "tailwindcss": "catalog:", "typescript": "catalog:", - "vitest": "~3.1.4" + "vitest": "catalog:" }, "expo": { "install": { diff --git a/apps/guides/package.json b/apps/guides/package.json index 7c3ae05e6d..a5b4cb7f46 100644 --- a/apps/guides/package.json +++ b/apps/guides/package.json @@ -20,9 +20,9 @@ "update-authors": "bun run scripts/update-authors.ts" }, "dependencies": { - "@ai-sdk/openai": "^3.0.53", + "@ai-sdk/openai": "catalog:", "@elysiajs/eden": "catalog:", - "@hookform/resolvers": "^5.2.2", + "@hookform/resolvers": "catalog:", "@packrat/api": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", @@ -55,48 +55,48 @@ "@radix-ui/react-toggle-group": "catalog:", "@radix-ui/react-tooltip": "catalog:", "@tailwindcss/typography": "^0.5.16", - "@tanstack/react-query": "^5.70.0", - "@tanstack/react-query-devtools": "^5.70.0", + "@tanstack/react-query": "catalog:", + "@tanstack/react-query-devtools": "catalog:", "ai": "catalog:", - "autoprefixer": "^10.4.21", + "autoprefixer": "catalog:", "chalk": "catalog:", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.1.1", - "date-fns": "^4.1.0", - "embla-carousel-react": "8.6.0", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "cmdk": "catalog:", + "date-fns": "catalog:", + "embla-carousel-react": "catalog:", "fs": "^0.0.1-security", - "gray-matter": "^4.0.3", - "input-otp": "1.4.1", - "lucide-react": "^1.8.0", + "gray-matter": "catalog:", + "input-otp": "catalog:", + "lucide-react": "catalog:", "mdx": "^0.3.1", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "next": "catalog:", + "next-themes": "catalog:", "path": "^0.12.7", "react": "catalog:", - "react-day-picker": "9.14.0", + "react-day-picker": "catalog:", "react-dom": "catalog:", - "react-hook-form": "^7.58.1", - "react-resizable-panels": "^4.10.0", + "react-hook-form": "catalog:", + "react-resizable-panels": "catalog:", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-html": "^16.0.1", "remark-parse": "^11.0.0", "slugify": "^1.6.6", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", + "sonner": "catalog:", + "tailwind-merge": "catalog:", "unified": "^11.0.0", - "vaul": "^1.1.2", + "vaul": "catalog:", "zod": "catalog:" }, "devDependencies": { - "@lhci/cli": "^0.14.0", + "@lhci/cli": "catalog:", "@types/mdx": "^2.0.13", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", "vitest": "~3.1.4" diff --git a/apps/landing/package.json b/apps/landing/package.json index 50886e76d3..bf8725fd18 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@emotion/is-prop-valid": "^1.3.1", - "@hookform/resolvers": "^5.2.2", + "@hookform/resolvers": "catalog:", "@packrat/guards": "workspace:*", "@packrat/web-ui": "workspace:*", "@radix-ui/react-accordion": "catalog:", @@ -45,34 +45,34 @@ "@radix-ui/react-toggle": "catalog:", "@radix-ui/react-toggle-group": "catalog:", "@radix-ui/react-tooltip": "catalog:", - "autoprefixer": "^10.4.21", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.1.1", - "date-fns": "^4.1.0", - "embla-carousel-react": "8.6.0", + "autoprefixer": "catalog:", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "cmdk": "catalog:", + "date-fns": "catalog:", + "embla-carousel-react": "catalog:", "framer-motion": "^12.19.1", - "input-otp": "1.4.1", - "lucide-react": "^1.8.0", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "input-otp": "catalog:", + "lucide-react": "catalog:", + "next": "catalog:", + "next-themes": "catalog:", "react": "catalog:", - "react-day-picker": "9.14.0", + "react-day-picker": "catalog:", "react-dom": "catalog:", - "react-hook-form": "^7.58.1", - "react-resizable-panels": "^4.10.0", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", - "vaul": "^1.1.2", + "react-hook-form": "catalog:", + "react-resizable-panels": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", + "vaul": "catalog:", "zod": "catalog:" }, "devDependencies": { - "@lhci/cli": "^0.14.0", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@lhci/cli": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", "vitest": "~3.1.4" diff --git a/apps/trails/package.json b/apps/trails/package.json index e50c131da3..7d6afa4681 100644 --- a/apps/trails/package.json +++ b/apps/trails/package.json @@ -21,26 +21,26 @@ "@radix-ui/react-separator": "catalog:", "@radix-ui/react-tabs": "catalog:", "@radix-ui/react-toast": "catalog:", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "input-otp": "1.4.1", - "leaflet": "^1.9.4", - "lucide-react": "^1.8.0", - "next": "^15.3.4", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "input-otp": "catalog:", + "leaflet": "catalog:", + "lucide-react": "catalog:", + "next": "catalog:", "react": "catalog:", "react-dom": "catalog:", - "react-leaflet": "^5.0.0", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", + "react-leaflet": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", "zod": "catalog:" }, "devDependencies": { - "@types/leaflet": "^1.9.21", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@types/leaflet": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:" } diff --git a/apps/web/package.json b/apps/web/package.json index b4cd03aeb9..05da19afb8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -14,27 +14,27 @@ "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/web-ui": "workspace:*", - "@tanstack/react-query": "^5.70.0", - "@tanstack/react-query-devtools": "^5.70.0", - "jotai": "^2.12.2", + "@tanstack/react-query": "catalog:", + "@tanstack/react-query-devtools": "catalog:", + "jotai": "catalog:", "js-cookie": "^3.0.5", - "lucide-react": "^1.8.0", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "lucide-react": "catalog:", + "next": "catalog:", + "next-themes": "catalog:", "react": "catalog:", "react-dom": "catalog:", - "recharts": "3.8.1", + "recharts": "catalog:", "zod": "catalog:" }, "devDependencies": { "@types/js-cookie": "^3.0.6", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "autoprefixer": "^10.4.21", - "postcss": "^8.5.6", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "autoprefixer": "catalog:", + "postcss": "catalog:", "tailwindcss": "catalog:", - "tailwindcss-animate": "^1.0.7", + "tailwindcss-animate": "catalog:", "typescript": "catalog:" } } diff --git a/bun.lock b/bun.lock index aaa5027be7..d2ca53ff96 100644 --- a/bun.lock +++ b/bun.lock @@ -38,29 +38,29 @@ "@radix-ui/react-slot": "catalog:", "@radix-ui/react-tabs": "catalog:", "@radix-ui/react-tooltip": "catalog:", - "@tanstack/react-query": "^5.70.0", - "@types/leaflet": "^1.9.21", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "leaflet": "^1.9.4", - "lucide-react": "^1.8.0", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "@tanstack/react-query": "catalog:", + "@types/leaflet": "catalog:", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "leaflet": "catalog:", + "lucide-react": "catalog:", + "next": "catalog:", + "next-themes": "catalog:", "nuqs": "^2.8.9", "react": "catalog:", "react-dom": "catalog:", "react-error-boundary": "^6.1.1", - "recharts": "3.8.1", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", + "recharts": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", "zod": "catalog:", }, "devDependencies": { - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", }, @@ -105,13 +105,13 @@ "@shopify/flash-list": "2.0.2", "@stardazed/streams-text-encoding": "^1.0.2", "@tanstack/react-form": "^1.0.5", - "@tanstack/react-query": "^5.70.0", + "@tanstack/react-query": "catalog:", "ai": "catalog:", - "better-auth": "^1.6.9", + "better-auth": "catalog:", "burnt": "^0.13.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "date-fns": "^4.1.0", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "date-fns": "catalog:", "expo": "~55.0.17", "expo-apple-authentication": "~55.0.13", "expo-blur": "~55.0.14", @@ -140,12 +140,12 @@ "expo-system-ui": "~55.0.17", "expo-updates": "~55.0.21", "expo-web-browser": "~55.0.15", - "google-auth-library": "^10.1.0", + "google-auth-library": "catalog:", "he": "^1.2.0", "i": "^0.3.7", "i18next": "^25.8.18", - "jotai": "^2.12.2", - "leaflet": "^1.9.4", + "jotai": "catalog:", + "leaflet": "catalog:", "llama.rn": "0.10.1", "nanoid": "^5.1.9", "nativewind": "^4.2.3", @@ -153,7 +153,7 @@ "react": "catalog:", "react-dom": "catalog:", "react-i18next": "^17.0.4", - "react-leaflet": "^5.0.0", + "react-leaflet": "catalog:", "react-native": "0.83.6", "react-native-blob-util": "^0.24.5", "react-native-css-interop": "^0.2.3", @@ -171,7 +171,7 @@ "react-native-web": "^0.21.0", "react-native-worklets": "0.7.4", "rn-icon-mapper": "^0.0.1", - "tailwind-merge": "^3.5.0", + "tailwind-merge": "catalog:", "use-debounce": "^10.0.5", "zod": "catalog:", }, @@ -179,12 +179,12 @@ "@babel/core": "^7.20.0", "@biomejs/biome": "2.4.6", "@types/he": "^1.2.3", - "@types/leaflet": "^1.9.21", - "@types/react": "~19.2.10", + "@types/leaflet": "catalog:", + "@types/react": "catalog:", "@types/ungap__structured-clone": "^1.2.0", "@typescript-eslint/eslint-plugin": "^7.7.0", "@typescript-eslint/parser": "^7.7.0", - "@vitest/coverage-v8": "~3.1.4", + "@vitest/coverage-v8": "catalog:", "ajv": "^8.12.0", "eslint": "^8.57.0", "eslint-config-universe": "^15.0.3", @@ -193,16 +193,16 @@ "rimraf": "^6.0.1", "tailwindcss": "catalog:", "typescript": "catalog:", - "vitest": "~3.1.4", + "vitest": "catalog:", }, }, "apps/guides": { "name": "packrat-guides-app", "version": "2.0.25", "dependencies": { - "@ai-sdk/openai": "^3.0.53", + "@ai-sdk/openai": "catalog:", "@elysiajs/eden": "catalog:", - "@hookform/resolvers": "^5.2.2", + "@hookform/resolvers": "catalog:", "@packrat/api": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", @@ -235,48 +235,48 @@ "@radix-ui/react-toggle-group": "catalog:", "@radix-ui/react-tooltip": "catalog:", "@tailwindcss/typography": "^0.5.16", - "@tanstack/react-query": "^5.70.0", - "@tanstack/react-query-devtools": "^5.70.0", + "@tanstack/react-query": "catalog:", + "@tanstack/react-query-devtools": "catalog:", "ai": "catalog:", - "autoprefixer": "^10.4.21", + "autoprefixer": "catalog:", "chalk": "catalog:", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.1.1", - "date-fns": "^4.1.0", - "embla-carousel-react": "8.6.0", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "cmdk": "catalog:", + "date-fns": "catalog:", + "embla-carousel-react": "catalog:", "fs": "^0.0.1-security", - "gray-matter": "^4.0.3", - "input-otp": "1.4.1", - "lucide-react": "^1.8.0", + "gray-matter": "catalog:", + "input-otp": "catalog:", + "lucide-react": "catalog:", "mdx": "^0.3.1", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "next": "catalog:", + "next-themes": "catalog:", "path": "^0.12.7", "react": "catalog:", - "react-day-picker": "9.14.0", + "react-day-picker": "catalog:", "react-dom": "catalog:", - "react-hook-form": "^7.58.1", - "react-resizable-panels": "^4.10.0", + "react-hook-form": "catalog:", + "react-resizable-panels": "catalog:", "remark": "^15.0.1", "remark-gfm": "^4.0.1", "remark-html": "^16.0.1", "remark-parse": "^11.0.0", "slugify": "^1.6.6", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", + "sonner": "catalog:", + "tailwind-merge": "catalog:", "unified": "^11.0.0", - "vaul": "^1.1.2", + "vaul": "catalog:", "zod": "catalog:", }, "devDependencies": { - "@lhci/cli": "^0.14.0", + "@lhci/cli": "catalog:", "@types/mdx": "^2.0.13", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", "vitest": "~3.1.4", @@ -287,7 +287,7 @@ "version": "2.0.25", "dependencies": { "@emotion/is-prop-valid": "^1.3.1", - "@hookform/resolvers": "^5.2.2", + "@hookform/resolvers": "catalog:", "@packrat/guards": "workspace:*", "@packrat/web-ui": "workspace:*", "@radix-ui/react-accordion": "catalog:", @@ -317,34 +317,34 @@ "@radix-ui/react-toggle": "catalog:", "@radix-ui/react-toggle-group": "catalog:", "@radix-ui/react-tooltip": "catalog:", - "autoprefixer": "^10.4.21", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.1.1", - "date-fns": "^4.1.0", - "embla-carousel-react": "8.6.0", + "autoprefixer": "catalog:", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "cmdk": "catalog:", + "date-fns": "catalog:", + "embla-carousel-react": "catalog:", "framer-motion": "^12.19.1", - "input-otp": "1.4.1", - "lucide-react": "^1.8.0", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "input-otp": "catalog:", + "lucide-react": "catalog:", + "next": "catalog:", + "next-themes": "catalog:", "react": "catalog:", - "react-day-picker": "9.14.0", + "react-day-picker": "catalog:", "react-dom": "catalog:", - "react-hook-form": "^7.58.1", - "react-resizable-panels": "^4.10.0", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", - "vaul": "^1.1.2", + "react-hook-form": "catalog:", + "react-resizable-panels": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", + "vaul": "catalog:", "zod": "catalog:", }, "devDependencies": { - "@lhci/cli": "^0.14.0", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@lhci/cli": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", "vitest": "~3.1.4", @@ -364,26 +364,26 @@ "@radix-ui/react-separator": "catalog:", "@radix-ui/react-tabs": "catalog:", "@radix-ui/react-toast": "catalog:", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "input-otp": "1.4.1", - "leaflet": "^1.9.4", - "lucide-react": "^1.8.0", - "next": "^15.3.4", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "input-otp": "catalog:", + "leaflet": "catalog:", + "lucide-react": "catalog:", + "next": "catalog:", "react": "catalog:", "react-dom": "catalog:", - "react-leaflet": "^5.0.0", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", + "react-leaflet": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", "zod": "catalog:", }, "devDependencies": { - "@types/leaflet": "^1.9.21", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "postcss": "^8.5.6", - "postcss-import": "^16.1.1", + "@types/leaflet": "catalog:", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "postcss": "catalog:", + "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", }, @@ -396,27 +396,27 @@ "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/web-ui": "workspace:*", - "@tanstack/react-query": "^5.70.0", - "@tanstack/react-query-devtools": "^5.70.0", - "jotai": "^2.12.2", + "@tanstack/react-query": "catalog:", + "@tanstack/react-query-devtools": "catalog:", + "jotai": "catalog:", "js-cookie": "^3.0.5", - "lucide-react": "^1.8.0", - "next": "^15.3.4", - "next-themes": "^0.4.6", + "lucide-react": "catalog:", + "next": "catalog:", + "next-themes": "catalog:", "react": "catalog:", "react-dom": "catalog:", - "recharts": "3.8.1", + "recharts": "catalog:", "zod": "catalog:", }, "devDependencies": { "@types/js-cookie": "^3.0.6", - "@types/node": "^25.6.0", - "@types/react": "~19.2.10", - "@types/react-dom": "^19.1.6", - "autoprefixer": "^10.4.21", - "postcss": "^8.5.6", + "@types/node": "catalog:", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "autoprefixer": "catalog:", + "postcss": "catalog:", "tailwindcss": "catalog:", - "tailwindcss-animate": "^1.0.7", + "tailwindcss-animate": "catalog:", "typescript": "catalog:", }, }, @@ -424,17 +424,17 @@ "name": "@packrat/analytics", "version": "2.0.25", "dependencies": { - "@duckdb/node-api": "1.5.0-r.1", + "@duckdb/node-api": "catalog:", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", - "consola": "^3.4.2", + "consola": "catalog:", "magic-regexp": "catalog:", "radash": "catalog:", "zod": "catalog:", }, "devDependencies": { - "@types/bun": "latest", - "vitest": "~3.1.4", + "@types/bun": "catalog:", + "vitest": "catalog:", }, }, "packages/api": { @@ -442,7 +442,7 @@ "version": "2.0.25", "dependencies": { "@ai-sdk/google": "^3.0.64", - "@ai-sdk/openai": "^3.0.53", + "@ai-sdk/openai": "catalog:", "@ai-sdk/perplexity": "^3.0.29", "@aws-sdk/client-s3": "~3.787.0", "@aws-sdk/s3-request-presigner": "~3.787.0", @@ -451,7 +451,7 @@ "@elysiajs/eden": "catalog:", "@elysiajs/openapi": "catalog:", "@mozilla/readability": "^0.6.0", - "@neondatabase/serverless": "^1.0.0", + "@neondatabase/serverless": "catalog:", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/overpass": "workspace:*", @@ -461,20 +461,20 @@ "ai": "catalog:", "bcryptjs": "^3.0.2", "csv-parse": "^6.2.1", - "drizzle-kit": "^0.31.10", - "drizzle-orm": "^0.45.2", + "drizzle-kit": "catalog:", + "drizzle-orm": "catalog:", "drizzle-zod": "^0.8.3", "elysia": "catalog:", - "google-auth-library": "^10.1.0", - "gray-matter": "^4.0.3", + "google-auth-library": "catalog:", + "gray-matter": "catalog:", "jose": "^5.9.6", "linkedom": "^0.18.11", "nodemailer": "^6.10.0", - "pg": "^8.16.3", + "pg": "catalog:", "radash": "catalog:", "resend": "^6.10.0", "workers-ai-provider": "^0.7.2", - "ws": "^8.18.1", + "ws": "catalog:", "youtube-transcript": "^1.3.0", "zod": "catalog:", "zod-openapi": "^5.4.6", @@ -482,18 +482,18 @@ "devDependencies": { "@better-auth/drizzle-adapter": "^1.6.9", "@cloudflare/vitest-pool-workers": "0.8.71", - "@cloudflare/workers-types": "^4.20250405.0", - "@types/bun": "latest", + "@cloudflare/workers-types": "catalog:", + "@types/bun": "catalog:", "@types/pg": "^8.11.15", "@types/ws": "^8.5.14", - "@vitest/coverage-v8": "~3.1.4", - "better-auth": "^1.6.9", + "@vitest/coverage-v8": "catalog:", + "better-auth": "catalog:", "better-auth-cloudflare": "^0.3.0", "concurrently": "^8.2.2", - "drizzle-orm": "^0.45.2", + "drizzle-orm": "catalog:", "typed-htmx": "^0.3.1", - "vitest": "~3.1.4", - "wrangler": "^4.21.2", + "vitest": "catalog:", + "wrangler": "catalog:", }, }, "packages/api-client": { @@ -519,8 +519,8 @@ "version": "2.0.25", "dependencies": { "@packrat/api-client": "workspace:*", - "@tanstack/react-query": "^5.70.0", - "jotai": "^2.12.2", + "@tanstack/react-query": "catalog:", + "jotai": "catalog:", "react": "catalog:", "zod": "catalog:", }, @@ -529,8 +529,8 @@ "steiger": "^0.5.11", }, "peerDependencies": { - "@tanstack/react-query": "^5.70.0", - "jotai": "^2.12.2", + "@tanstack/react-query": "catalog:", + "jotai": "catalog:", "react": "catalog:", }, }, @@ -545,18 +545,18 @@ "packrat": "./src/index.ts", }, "dependencies": { - "@duckdb/node-api": "1.5.0-r.1", + "@duckdb/node-api": "catalog:", "@packrat/analytics": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "chalk": "catalog:", "citty": "^0.2.1", "cli-table3": "^0.6.5", - "consola": "^3.4.2", + "consola": "catalog:", "zod": "catalog:", }, "devDependencies": { - "@types/bun": "latest", + "@types/bun": "catalog:", }, }, "packages/config": { @@ -594,25 +594,25 @@ "zod": "catalog:", }, "devDependencies": { - "@cloudflare/workers-types": "^4.20250405.0", - "@vitest/coverage-v8": "~3.1.4", + "@cloudflare/workers-types": "catalog:", + "@vitest/coverage-v8": "catalog:", "partyserver": "^0.4.1", "typescript": "catalog:", - "vitest": "~3.1.4", - "wrangler": "^4.21.2", + "vitest": "catalog:", + "wrangler": "catalog:", }, }, "packages/osm-db": { "name": "@packrat/osm-db", "version": "2.0.25", "dependencies": { - "@neondatabase/serverless": "^1.0.0", - "drizzle-orm": "^0.45.2", - "pg": "^8.16.3", - "ws": "^8.18.1", + "@neondatabase/serverless": "catalog:", + "drizzle-orm": "catalog:", + "pg": "catalog:", + "ws": "catalog:", }, "devDependencies": { - "drizzle-kit": "^0.31.10", + "drizzle-kit": "catalog:", "typescript": "catalog:", }, }, @@ -621,7 +621,7 @@ "version": "2.0.25", "dependencies": { "@packrat/env": "workspace:*", - "pg": "^8.16.3", + "pg": "catalog:", }, }, "packages/overpass": { @@ -633,7 +633,7 @@ }, "devDependencies": { "typescript": "catalog:", - "vitest": "~3.1.4", + "vitest": "catalog:", }, }, "packages/ui": { @@ -651,7 +651,7 @@ }, "devDependencies": { "convert-units": "3.0.0-beta.8", - "vitest": "~3.1.4", + "vitest": "catalog:", }, }, "packages/web-ui": { @@ -686,32 +686,32 @@ "@radix-ui/react-toggle": "catalog:", "@radix-ui/react-toggle-group": "catalog:", "@radix-ui/react-tooltip": "catalog:", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.1.1", - "embla-carousel-react": "8.6.0", - "input-otp": "1.4.1", - "lucide-react": "^1.8.0", - "next-themes": "^0.4.6", - "react-day-picker": "9.14.0", - "react-hook-form": "^7.58.1", - "react-resizable-panels": "^4.10.0", - "recharts": "3.8.1", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", - "tailwindcss-animate": "^1.0.7", - "vaul": "^1.1.2", + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "cmdk": "catalog:", + "embla-carousel-react": "catalog:", + "input-otp": "catalog:", + "lucide-react": "catalog:", + "next-themes": "catalog:", + "react-day-picker": "catalog:", + "react-hook-form": "catalog:", + "react-resizable-panels": "catalog:", + "recharts": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", + "tailwindcss-animate": "catalog:", + "vaul": "catalog:", }, "devDependencies": { - "@types/react": "~19.2.10", + "@types/react": "catalog:", "react": "catalog:", - "recharts": "3.8.1", + "recharts": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", }, "peerDependencies": { "react": "catalog:", - "recharts": "3.8.1", + "recharts": "catalog:", "tailwindcss": "catalog:", }, }, @@ -727,12 +727,18 @@ "react": "19.2.6", }, "catalog": { + "@ai-sdk/openai": "^3.0.53", + "@cloudflare/workers-types": "^4.20250405.0", + "@duckdb/node-api": "1.5.0-r.1", "@elysiajs/cors": "^1.2.0", "@elysiajs/eden": "^1.2.0", "@elysiajs/openapi": "^1.2.0", "@hono/sentry": "^1.2.2", "@hono/zod-openapi": "^1.3.0", "@hono/zod-validator": "^0.7.6", + "@hookform/resolvers": "^5.2.2", + "@lhci/cli": "^0.14.0", + "@neondatabase/serverless": "^1.0.0", "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-alert-dialog": "^1.1.14", "@radix-ui/react-aspect-ratio": "^1.1.7", @@ -760,34 +766,75 @@ "@radix-ui/react-toggle": "^1.1.9", "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-tooltip": "^1.2.7", + "@tanstack/react-query": "^5.70.0", + "@tanstack/react-query-devtools": "^5.70.0", + "@types/bun": "latest", + "@types/leaflet": "^1.9.21", + "@types/node": "^25.6.0", + "@types/react": "~19.2.10", + "@types/react-dom": "^19.1.6", + "@vitest/coverage-v8": "~3.1.4", "ai": "^6.0.168", + "autoprefixer": "^10.4.21", + "better-auth": "^1.6.9", "chalk": "^5.6.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "1.1.1", + "consola": "^3.4.2", + "date-fns": "^4.1.0", + "drizzle-kit": "^0.31.10", + "drizzle-orm": "^0.45.2", "elysia": "^1.4.0", + "embla-carousel-react": "8.6.0", + "google-auth-library": "^10.1.0", + "gray-matter": "^4.0.3", "hono": "^4.10.7", + "input-otp": "1.4.1", + "jotai": "^2.12.2", + "leaflet": "^1.9.4", + "lucide-react": "^1.8.0", "magic-regexp": "^0.11.0", + "next": "^15.3.4", + "next-themes": "^0.4.6", + "pg": "^8.16.3", + "postcss": "^8.5.6", + "postcss-import": "^16.1.1", "radash": "^12.1.1", "react": "19.2.6", + "react-day-picker": "9.14.0", "react-dom": "19.2.6", + "react-hook-form": "^7.58.1", + "react-leaflet": "^5.0.0", + "react-resizable-panels": "^4.10.0", + "recharts": "3.8.1", "semver": "^7.7.4", + "sonner": "^2.0.7", + "tailwind-merge": "^3.5.0", "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7", "ts-extras": "^1.0.0", "typescript": "~5.9.2", + "vaul": "^1.1.2", + "vitest": "~3.1.4", + "wrangler": "^4.21.2", + "ws": "^8.18.1", "zod": "^3.24.2", }, "packages": { - "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.104", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ZKX5n74io8VIRlhIMSLWVlvT3sXC8Z7cZ9GHuWBWZDVi96+62AIsWuLGvMfcBA1STYuSoDrp6rIziZmvrTq0TA=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@3.0.114", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MqkZ5sd+qiq6RgIxELkoFQXg2/JwK+WCMaot7U+rtrZpWJl3fSyYvc28SC03b256o4F7OXjQtdjTqs81B2w+dA=="], - "@ai-sdk/google": ["@ai-sdk/google@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CbR82EgGPNrj/6q0HtclwuCqe0/pDShyv3nWDP/A9DroujzWXnLMlUJVrgPOsg4b40zQCwwVs2XSKCxvt/4QaA=="], + "@ai-sdk/google": ["@ai-sdk/google@3.0.73", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-o2MuIeyvZrFIeIbnbA8Thrr63irdyUBh0uWBZ2lY6yFeXuE/tcwyXF74bDKS4KvTu84uFpQfpbS/LXHGKKXz+g=="], - "@ai-sdk/openai": ["@ai-sdk/openai@3.0.53", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Wld+Rbc05KaUn08uBt06eEuwcgalcIFtIl32Yp+GxuZXUQwOb6YeAuq+C6da4ch6BurFoqEaLemJVwjBb7x+PQ=="], + "@ai-sdk/openai": ["@ai-sdk/openai@3.0.63", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-4yY/m8a57MNNVoJCsXuNblKf6BO4yuAuLKRX4tzSNffBEBSp1FlcWdPE0Z4FkqUeS0AJhYSSqp0GIiA/cIcDNA=="], - "@ai-sdk/perplexity": ["@ai-sdk/perplexity@3.0.29", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-9UfV7ywpnxNLPI/hdheFPHXDdLG9vLqNoPSdRTPV+nPAX117zMtBmqD5KSvmXTjeF7IXpObUZ9bWzwMR/ewL1g=="], + "@ai-sdk/perplexity": ["@ai-sdk/perplexity@3.0.33", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-aNt6pTAzq+akadDXVdg2SjN2dODtaVlkKbw8/35c+sekr+Tx0sJwVqMR1udxrjLzhQvz8qtfsWRuz+hB9pmOnQ=="], - "@ai-sdk/provider": ["@ai-sdk/provider@3.0.8", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-oGMAgGoQdBXbZqNG0Ze56CHjDZ1IDYOwGYxYjO5KLSlz5HiNQ9udIXsPZ61VWaHGZ5XW/jyjmr6t2xz2jGVwbQ=="], + "@ai-sdk/provider": ["@ai-sdk/provider@3.0.10", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-Q3BZ27qfpYqnCYGvE3vt+Qi6LGOF9R5Nmzn+9JoM1lCRsD9mYaIhfJLkSunN48nfGXJ6n+XNV0J/XVpqGQl7Dw=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.23", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-z8GlDaCmRSDlqkMF2f4/RFgWxdarvIbyuk+m6WXT1LYgsnGiXRJGTD2Z1+SDl3LqtFuRtGX1aghYvQLoHL/9pg=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="], - "@ai-sdk/react": ["@ai-sdk/react@3.0.170", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.23", "ai": "6.0.168", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-YUDn+mK0c8iUz14rCBf1A0zg6SV5b5aSVUz+azF1bdBd1SFXVI19dKYR+PQSpZY+0+z+zs252AAsacUqiO98Kw=="], + "@ai-sdk/react": ["@ai-sdk/react@3.0.184", "", { "dependencies": { "@ai-sdk/provider-utils": "4.0.27", "ai": "6.0.182", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, "sha512-k8fQ11U3+lKzUCkiitevuH0MF++b7QPX7zrPRfXfNayLRZwrwvNuqXifB/6iIyQpSLNCfzhkqG117FW2EXCI5w=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -875,7 +922,7 @@ "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], - "@babel/compat-data": ["@babel/compat-data@7.29.0", "", {}, "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg=="], + "@babel/compat-data": ["@babel/compat-data@7.29.3", "", {}, "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg=="], "@babel/core": ["@babel/core@7.29.0", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-module-transforms": "^7.28.6", "@babel/helpers": "^7.28.6", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA=="], @@ -885,7 +932,7 @@ "@babel/helper-compilation-targets": ["@babel/helper-compilation-targets@7.28.6", "", { "dependencies": { "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" } }, "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA=="], - "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.28.6", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow=="], + "@babel/helper-create-class-features-plugin": ["@babel/helper-create-class-features-plugin@7.29.3", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-member-expression-to-functions": "^7.28.5", "@babel/helper-optimise-call-expression": "^7.27.1", "@babel/helper-replace-supers": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", "@babel/traverse": "^7.29.0", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA=="], "@babel/helper-create-regexp-features-plugin": ["@babel/helper-create-regexp-features-plugin@7.28.5", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "regexpu-core": "^6.3.1", "semver": "^6.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw=="], @@ -919,7 +966,7 @@ "@babel/helpers": ["@babel/helpers@7.29.2", "", { "dependencies": { "@babel/template": "^7.28.6", "@babel/types": "^7.29.0" } }, "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw=="], - "@babel/parser": ["@babel/parser@7.29.2", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA=="], + "@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], "@babel/plugin-proposal-decorators": ["@babel/plugin-proposal-decorators@7.29.0", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6", "@babel/plugin-syntax-decorators": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA=="], @@ -1061,21 +1108,21 @@ "@bcoe/v8-coverage": ["@bcoe/v8-coverage@1.0.2", "", {}, "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA=="], - "@better-auth/core": ["@better-auth/core@1.6.9", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.39.0", "@standard-schema/spec": "^1.1.0", "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21", "@cloudflare/workers-types": ">=4", "@opentelemetry/api": "^1.9.0", "better-call": "1.3.5", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" }, "optionalPeers": ["@cloudflare/workers-types", "@opentelemetry/api"] }, "sha512-ADFk5pwmLybmc+LvYvXJ6M1x2oY/EyYLkwLuH0x28FUq12DfjL0wnE7g+WRDf3yozDO+qIxTpFGXDGwLKbfz0w=="], + "@better-auth/core": ["@better-auth/core@1.6.11", "", { "dependencies": { "@opentelemetry/semantic-conventions": "^1.39.0", "@standard-schema/spec": "^1.1.0", "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21", "@cloudflare/workers-types": ">=4", "@opentelemetry/api": "^1.9.0", "better-call": "1.3.5", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" }, "optionalPeers": ["@cloudflare/workers-types", "@opentelemetry/api"] }, "sha512-LrwidLCV8azdMGjvtwp30nj9tIv1BwI3VhtC0UaGSjQkAVWw4bN42I8qwbxRziPeSQoj+zUVkOpxZzAWBDARtQ=="], - "@better-auth/drizzle-adapter": ["@better-auth/drizzle-adapter@1.6.9", "", { "peerDependencies": { "@better-auth/core": "^1.6.9", "@better-auth/utils": "0.4.0", "drizzle-orm": "^0.45.2" }, "optionalPeers": ["drizzle-orm"] }, "sha512-Lcco5hOGrMgc4XKAkvB6x72eQm4wCcya8IevMg4wBHY9W9GVg8pu23rpRX6VsVQSO4Ux13S7lFwUWtF7/r9aKw=="], + "@better-auth/drizzle-adapter": ["@better-auth/drizzle-adapter@1.6.11", "", { "peerDependencies": { "@better-auth/core": "^1.6.11", "@better-auth/utils": "0.4.0", "drizzle-orm": "^0.45.2" }, "optionalPeers": ["drizzle-orm"] }, "sha512-4jpkETIGZOHCf7BK4jnu22fdN6jjomH0/HhEzkaWy3+Eppi5PYlHTF/460jrTmA3Xc+Vqwp9t282ymHiEPypGw=="], - "@better-auth/expo": ["@better-auth/expo@1.6.9", "", { "dependencies": { "@better-fetch/fetch": "1.1.21", "better-call": "1.3.5", "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/core": "^1.6.9", "better-auth": "^1.6.9", "expo-constants": ">=17.0.0", "expo-linking": ">=7.0.0", "expo-network": ">=8.0.7", "expo-web-browser": ">=14.0.0" }, "optionalPeers": ["expo-constants", "expo-linking", "expo-network", "expo-web-browser"] }, "sha512-ch8DRTAWvnn4k1mFvLxi5h9+pg/KWYXJEZ2XANXtdPp84H3qA0mxzFQ//OIuKxm0Ohuubc7WNQD1IsVlKP4/ew=="], + "@better-auth/expo": ["@better-auth/expo@1.6.11", "", { "dependencies": { "@better-fetch/fetch": "1.1.21", "better-call": "1.3.5", "zod": "^4.3.6" }, "peerDependencies": { "@better-auth/core": "^1.6.11", "better-auth": "^1.6.11", "expo-constants": ">=17.0.0", "expo-linking": ">=7.0.0", "expo-network": ">=8.0.7", "expo-web-browser": ">=14.0.0" }, "optionalPeers": ["expo-constants", "expo-linking", "expo-network", "expo-web-browser"] }, "sha512-ahqtpj5DRF4Tu8+PZuLPkR10Q6b8AntQNsn4LPcOIp6za5IJDAsaD/go1I5qCYIRi+8YKiIXk9vh4qQM54u+hA=="], - "@better-auth/kysely-adapter": ["@better-auth/kysely-adapter@1.6.9", "", { "peerDependencies": { "@better-auth/core": "^1.6.9", "@better-auth/utils": "0.4.0", "kysely": "^0.28.14" }, "optionalPeers": ["kysely"] }, "sha512-gyjuuxJtZ4o9G9z9q4kqn24X2kvMSp7F+KHogYxF03SnXY/2WleAcuj57iC4wP3e9mGDbjPOrnM5K6Kr3Ktdpw=="], + "@better-auth/kysely-adapter": ["@better-auth/kysely-adapter@1.6.11", "", { "peerDependencies": { "@better-auth/core": "^1.6.11", "@better-auth/utils": "0.4.0", "kysely": "^0.28.17" }, "optionalPeers": ["kysely"] }, "sha512-/g8M9RfIjdcZDnbstSUvQiINkvdNlCeZr248zwqx2/PVksQI1MhQofbzUn3RnQnbPKp0EPwpX/dR3oudRFenUg=="], - "@better-auth/memory-adapter": ["@better-auth/memory-adapter@1.6.9", "", { "peerDependencies": { "@better-auth/core": "^1.6.9", "@better-auth/utils": "0.4.0" } }, "sha512-XmIG4tUnOXZ+KEcWjHUjOI9Z5donD09dC2t/AQTXifAUIqx7cySg86w0KTM09ArzAxRx1fCqO36Wkt5nULnrkQ=="], + "@better-auth/memory-adapter": ["@better-auth/memory-adapter@1.6.11", "", { "peerDependencies": { "@better-auth/core": "^1.6.11", "@better-auth/utils": "0.4.0" } }, "sha512-hpdfw0BBf8MuzLkIdmbcUZICbY9r/bhLO2RxSnkzT5+/O+0I0u2I8+m0YUP7vNllP/ZCKASHOYgXPLO75Z0f9Q=="], - "@better-auth/mongo-adapter": ["@better-auth/mongo-adapter@1.6.9", "", { "peerDependencies": { "@better-auth/core": "^1.6.9", "@better-auth/utils": "0.4.0", "mongodb": "^6.0.0 || ^7.0.0" }, "optionalPeers": ["mongodb"] }, "sha512-h+AiRJ/TsBSi+ZDjySASBpbJ/9QCXBre34PSKgCz7QmTHrFM9Cg2EM4AM7LjR5lPXipEE+2rWPBc9wfnUBjhcw=="], + "@better-auth/mongo-adapter": ["@better-auth/mongo-adapter@1.6.11", "", { "peerDependencies": { "@better-auth/core": "^1.6.11", "@better-auth/utils": "0.4.0", "mongodb": "^6.0.0 || ^7.0.0" }, "optionalPeers": ["mongodb"] }, "sha512-3Tor8rSv8vSEIMEaV2PFpPEuVhqc1gNoZ6eGvoh3LwExXXuj8madew6ob+H1pH7Aphn3Ar5PQ08AguT8TbwFAA=="], - "@better-auth/prisma-adapter": ["@better-auth/prisma-adapter@1.6.9", "", { "peerDependencies": { "@better-auth/core": "^1.6.9", "@better-auth/utils": "0.4.0", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@prisma/client", "prisma"] }, "sha512-XHks01ntK20orqK/jICq8wmEbJ/zT6dct49Fk8zTQKN9QNGDc+Ix5+7z/Kvui0DXGFf790GfvRozquzaLtXa8Q=="], + "@better-auth/prisma-adapter": ["@better-auth/prisma-adapter@1.6.11", "", { "peerDependencies": { "@better-auth/core": "^1.6.11", "@better-auth/utils": "0.4.0", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@prisma/client", "prisma"] }, "sha512-Pw+7q7zTp+VSci1V+CYMvuxIbAeVMZLe4lRo46LJoAKMHfjFl5T/ycsyFvWs/DkWC7n9gZZzRDEbHp0I5FiKKw=="], - "@better-auth/telemetry": ["@better-auth/telemetry@1.6.9", "", { "peerDependencies": { "@better-auth/core": "^1.6.9", "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21" } }, "sha512-0u5zkhSCAQFoN3DHvUkLHOF6MBbVTDAa6mU8mhPwiysdz1x21vMzhzfaAKN/ZGWaQ09v91/F+2qu42G/bhUV4A=="], + "@better-auth/telemetry": ["@better-auth/telemetry@1.6.11", "", { "peerDependencies": { "@better-auth/core": "^1.6.11", "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21" } }, "sha512-hsjDHc8MZbm6/AHeNdtywrWedXevnBjmdvnHTcZub+rTVjOv+Td0roI8USKuC6uUibmrl//2rJfVCsGbopihNA=="], "@better-auth/utils": ["@better-auth/utils@0.4.0", "", { "dependencies": { "@noble/hashes": "^2.0.1" } }, "sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA=="], @@ -1109,25 +1156,25 @@ "@cloudflare/containers": ["@cloudflare/containers@0.0.30", "", {}, "sha512-i148xBgmyn/pje82ZIyuTr/Ae0BT/YWwa1/GTJcw6DxEjUHAzZLaBCiX446U9OeuJ2rBh/L/9FIzxX5iYNt1AQ=="], - "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.2", "", {}, "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ=="], + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.5.0", "", {}, "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg=="], "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.16.1", "", { "peerDependencies": { "unenv": "2.0.0-rc.24", "workerd": ">1.20260305.0 <2.0.0-0" }, "optionalPeers": ["workerd"] }, "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw=="], "@cloudflare/vitest-pool-workers": ["@cloudflare/vitest-pool-workers@0.8.71", "", { "dependencies": { "birpc": "0.2.14", "cjs-module-lexer": "^1.2.3", "devalue": "^5.3.2", "miniflare": "4.20250906.0", "semver": "^7.7.1", "wrangler": "4.35.0", "zod": "^3.22.3" }, "peerDependencies": { "@vitest/runner": "2.0.x - 3.2.x", "@vitest/snapshot": "2.0.x - 3.2.x", "vitest": "2.0.x - 3.2.x" } }, "sha512-keu2HCLQfRNwbmLBCDXJgCFpANTaYnQpE01fBOo4CNwiWHUT7SZGN7w64RKiSWRHyYppStXBuE5Ng7F42+flpg=="], - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260424.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-yFR1XaJbSDLg/qbwtrYaU2xwFXatIPKR5nrMQCN1q/m6+Qe/j6r+kCnFEvOJjMZOm9iCKsE6Qly5clgl4u32qw=="], + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20260511.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-xQXYe0ILEGKyFFgZ9SlxZV2RkWXxp9mIA5mlqfdEmmSInMHlWRO4H/l6oCXZnQXNH6o3bvmGuNzl6Mac5+Omgg=="], - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260424.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LqWKcE7x/9KyC2iQvKPeb20hKST3dYXDZlYTvFymgR1DfLS0OFOCzVGTloVNd7WqvK4SkdzBYfxo7QMIAeBK0w=="], + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20260511.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QOaY4/BlQfDEZgTlrwCPreRIyenYmFWREVP79SSz6K6QBf7bf8ubaATN11NZbouEg19iTMtl5L7FLYdcz1tz7g=="], - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260424.1", "", { "os": "linux", "cpu": "x64" }, "sha512-YlEBFbAYZHe/ylzl8WEYQEU/jr+0XMqXaST2oBk5oVjksdb1NGuJaggluCdZAzuJJ8UqdTmyhY5u/qrasbiFWA=="], + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20260511.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5ZV6SHjU7WSsb9pRPSssckLJgDI5xemv7XpKWM023hMv9Q3jb4ZpkZEeIOGt2Q46E4/fcxfnXeQbL1nukDIpLg=="], - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260424.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-qJ0X0m6cL8fWDUPDg8K4IxYZXNJI6XbeOihqjnqKbAClrjdPDn8VUSd+z2XiCQ5NylMtMrpa/skC9UfaR6mh8g=="], + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20260511.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-BG8rVZZCqTvnwOMPuh+cjt/VbZ3QchxXaBOlfromw+zLQoblSHJorMPZ/JY4/phV7RoP8gB2C84bBPcahvZUnQ=="], - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260424.1", "", { "os": "win32", "cpu": "x64" }, "sha512-tZ7Z9qmYNAP6z1/+8r/zKbk8F8DZmpmwNzMeN+zkde2Wnhfr3FBqOkJXT/5zmli8HPoWrIXxSiyqcNDMy8V2Zg=="], + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20260511.1", "", { "os": "win32", "cpu": "x64" }, "sha512-tGEyfKFArBL0NdqmABqzsIK1vmHdshkdFzCAUo1j6LqYsEpgJWCzvqwX9IpNHEc/AAm0UtPOLhLKhz1DA7HQLA=="], "@cloudflare/workers-oauth-provider": ["@cloudflare/workers-oauth-provider@0.4.0", "", {}, "sha512-UtbV8hjC2NloB+Ds6J6v/9HiG8rx8MbdeYGCyFwOACT5vANWzDL6SKo3W5UZymsXiameAgC7jAmtUx4cc+Qpaw=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260425.1", "", {}, "sha512-f6dlo3SsA+TNqjveavPDN73nxRfCOOd0iMdf8iEosgR/RJtQlrGwfr5L5Vf7x/5cpeeguxScKevuaMmdjpOECw=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260511.1", "", {}, "sha512-FA+si7cOq9i/gtCHhIc0XJL0l1F/ApF+m00752Aj7WZFJrj3ZulT2T8/+rT3BabMT0QEnqFEGIqCgrmqhgEfMg=="], "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], @@ -1239,15 +1286,15 @@ "@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="], - "@expo-google-fonts/material-symbols": ["@expo-google-fonts/material-symbols@0.4.34", "", {}, "sha512-PdwETUhvu1gHF1e8eIyEHnBJLq/dRNoTrT5yhsGUfGyRxH5pbm54dF3+QPknxwMKj0M1trN7PSelYz+yzlt3lA=="], + "@expo-google-fonts/material-symbols": ["@expo-google-fonts/material-symbols@0.4.36", "", {}, "sha512-hFIN8h99qUid9OppB9Sj18sUQib2O0I9c0soBmgb932Kz+20pAaGe/PRH6NdAqm8/DdOs+Hwx8A4Fqn9ZNadhg=="], - "@expo/cli": ["@expo/cli@55.0.29", "", { "dependencies": { "@expo/code-signing-certificates": "^0.0.6", "@expo/config": "~55.0.16", "@expo/config-plugins": "~55.0.8", "@expo/devcert": "^1.2.1", "@expo/env": "~2.1.2", "@expo/image-utils": "^0.8.14", "@expo/json-file": "^10.0.14", "@expo/log-box": "55.0.12", "@expo/metro": "~55.1.1", "@expo/metro-config": "~55.0.20", "@expo/osascript": "^2.4.3", "@expo/package-manager": "^1.10.5", "@expo/plist": "^0.5.3", "@expo/prebuild-config": "^55.0.17", "@expo/require-utils": "^55.0.5", "@expo/router-server": "^55.0.16", "@expo/schema-utils": "^55.0.4", "@expo/spawn-async": "^1.7.2", "@expo/ws-tunnel": "^1.0.1", "@expo/xcpretty": "^4.4.0", "@react-native/dev-middleware": "0.83.6", "accepts": "^1.3.8", "arg": "^5.0.2", "better-opn": "~3.0.2", "bplist-creator": "0.1.0", "bplist-parser": "^0.3.1", "chalk": "^4.0.0", "ci-info": "^3.3.0", "compression": "^1.7.4", "connect": "^3.7.0", "debug": "^4.3.4", "dnssd-advertise": "^1.1.4", "expo-server": "^55.0.9", "fetch-nodeshim": "^0.4.10", "getenv": "^2.0.0", "glob": "^13.0.0", "lan-network": "^0.2.1", "multitars": "^1.0.0", "node-forge": "^1.3.3", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "picomatch": "^4.0.3", "pretty-format": "^29.7.0", "progress": "^2.0.3", "prompts": "^2.3.2", "resolve-from": "^5.0.0", "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "terminal-link": "^2.1.1", "toqr": "^0.1.1", "wrap-ansi": "^7.0.0", "ws": "^8.12.1", "zod": "^3.25.76" }, "peerDependencies": { "expo": "*", "expo-router": "*", "react-native": "*" }, "optionalPeers": ["expo-router", "react-native"], "bin": { "expo-internal": "build/bin/cli" } }, "sha512-r2dXQ82e/3nwxS7faLRL6HBD8UWDo/IyptQ0Vg6Z5Bgyp2Kd24h8xPn3RHfY3LLJ3wfEXglf4E79/Dqkm1Z6WA=="], + "@expo/cli": ["@expo/cli@55.0.30", "", { "dependencies": { "@expo/code-signing-certificates": "^0.0.6", "@expo/config": "~55.0.17", "@expo/config-plugins": "~55.0.9", "@expo/devcert": "^1.2.1", "@expo/env": "~2.1.2", "@expo/image-utils": "^0.8.14", "@expo/json-file": "^10.0.14", "@expo/log-box": "55.0.12", "@expo/metro": "~55.1.1", "@expo/metro-config": "~55.0.21", "@expo/osascript": "^2.4.3", "@expo/package-manager": "^1.10.5", "@expo/plist": "^0.5.3", "@expo/prebuild-config": "^55.0.18", "@expo/require-utils": "^55.0.5", "@expo/router-server": "^55.0.16", "@expo/schema-utils": "^55.0.4", "@expo/spawn-async": "^1.7.2", "@expo/ws-tunnel": "^1.0.1", "@expo/xcpretty": "^4.4.0", "@react-native/dev-middleware": "0.83.6", "accepts": "^1.3.8", "arg": "^5.0.2", "better-opn": "~3.0.2", "bplist-creator": "0.1.0", "bplist-parser": "^0.3.1", "chalk": "^4.0.0", "ci-info": "^3.3.0", "compression": "^1.7.4", "connect": "^3.7.0", "debug": "^4.3.4", "dnssd-advertise": "^1.1.4", "expo-server": "^55.0.9", "fetch-nodeshim": "^0.4.10", "getenv": "^2.0.0", "glob": "^13.0.0", "lan-network": "^0.2.1", "multitars": "^1.0.0", "node-forge": "^1.3.3", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "picomatch": "^4.0.3", "pretty-format": "^29.7.0", "progress": "^2.0.3", "prompts": "^2.3.2", "resolve-from": "^5.0.0", "semver": "^7.6.0", "send": "^0.19.0", "slugify": "^1.3.4", "source-map-support": "~0.5.21", "stacktrace-parser": "^0.1.10", "structured-headers": "^0.4.1", "terminal-link": "^2.1.1", "toqr": "^0.1.1", "wrap-ansi": "^7.0.0", "ws": "^8.12.1", "zod": "^3.25.76" }, "peerDependencies": { "expo": "*", "expo-router": "*", "react-native": "*" }, "optionalPeers": ["expo-router", "react-native"], "bin": { "expo-internal": "build/bin/cli" } }, "sha512-luWcCgompncWtCi1HqQfY32MVOuD0kUeARpr1Le1LeKVtZykjOwnz7YWXZo5zjISiD7L/gQnBNGVrRjvREsJqg=="], "@expo/code-signing-certificates": ["@expo/code-signing-certificates@0.0.6", "", { "dependencies": { "node-forge": "^1.3.3" } }, "sha512-iNe0puxwBNEcuua9gmTGzq+SuMDa0iATai1FlFTMHJ/vUmKvN/V//drXoLJkVb5i5H3iE/n/qIJxyoBnXouD0w=="], - "@expo/config": ["@expo/config@55.0.16", "", { "dependencies": { "@expo/config-plugins": "~55.0.8", "@expo/config-types": "^55.0.5", "@expo/json-file": "^10.0.14", "@expo/require-utils": "^55.0.5", "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", "resolve-workspace-root": "^2.0.0", "semver": "^7.6.0", "slugify": "^1.3.4" } }, "sha512-H5dpQv5TfyZDNheZAWO3SmP10diGWZwN5QOUsArkDJih0QKNtahQBOmrV2xbhgln/nrUGoy41U/ZIY/MEx63Ug=="], + "@expo/config": ["@expo/config@55.0.17", "", { "dependencies": { "@expo/config-plugins": "~55.0.9", "@expo/config-types": "^55.0.5", "@expo/json-file": "^10.0.14", "@expo/require-utils": "^55.0.5", "deepmerge": "^4.3.1", "getenv": "^2.0.0", "glob": "^13.0.0", "resolve-workspace-root": "^2.0.0", "semver": "^7.6.0", "slugify": "^1.3.4" } }, "sha512-Y3VaRg7Jllg3MhlUOTQqHm6/dttsqcjYlnS9enhAllZvPUpTHnRA4YPETtUZlxkdMJy6y3UZe986pd/KfJ6OTg=="], - "@expo/config-plugins": ["@expo/config-plugins@55.0.8", "", { "dependencies": { "@expo/config-types": "^55.0.5", "@expo/json-file": "~10.0.13", "@expo/plist": "^0.5.2", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", "getenv": "^2.0.0", "glob": "^13.0.0", "resolve-from": "^5.0.0", "semver": "^7.5.4", "slugify": "^1.6.6", "xcode": "^3.0.1", "xml2js": "0.6.0" } }, "sha512-8WfWTRntTCcowfOS+tHdB0z98gKetTwktg4G5TWkCkXVa8Jt1NUnvzaaU4UHk2vbR2U4N84RyZJFizSwfF6C9g=="], + "@expo/config-plugins": ["@expo/config-plugins@55.0.9", "", { "dependencies": { "@expo/config-types": "^55.0.5", "@expo/json-file": "~10.0.14", "@expo/plist": "^0.5.3", "@expo/sdk-runtime-versions": "^1.0.0", "chalk": "^4.1.2", "debug": "^4.3.5", "getenv": "^2.0.0", "glob": "^13.0.0", "resolve-from": "^5.0.0", "semver": "^7.5.4", "slugify": "^1.6.6", "xcode": "^3.0.1", "xml2js": "0.6.0" } }, "sha512-jLfpxru8dTo7eU0cqeTWuQav7byyjb37eF/mbXl1/3eTBHBvFU1VGxpeKxanUdTQAAjqzH8KGgWb0fWcce+z1w=="], "@expo/config-types": ["@expo/config-types@55.0.5", "", {}, "sha512-sCmSUZG4mZ/ySXvfyyBdhjivz8Q539X1NondwDdYG7s3SBsk+wsgPJzYsqgAG/P9+l0xWjUD2F+kQ1cAJ6NNLg=="], @@ -1265,23 +1312,23 @@ "@expo/json-file": ["@expo/json-file@10.0.14", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "json5": "^2.2.3" } }, "sha512-yWwBFywFv+SxkJp/pIzzA416JVYflNUh7pqQzgaA6nXDqRyK7KfrqVzk8PdUfDnqbBcaZZxpzNssfQZzp5KHrA=="], - "@expo/local-build-cache-provider": ["@expo/local-build-cache-provider@55.0.12", "", { "dependencies": { "@expo/config": "~55.0.16", "chalk": "^4.1.2" } }, "sha512-Wqhe7ajt6lyIEQvqDC1zm0MQ1RqQLlM9awCepY9pz+tm9rvhuxGPZTSddWeD8k4kolinBlDbLDFnNi06XgaDWQ=="], + "@expo/local-build-cache-provider": ["@expo/local-build-cache-provider@55.0.13", "", { "dependencies": { "@expo/config": "~55.0.17", "chalk": "^4.1.2" } }, "sha512-Vg5BE10UL+0yg3BVtIeiSoeHU31Qe1m3UxhBPS478ACY1zzKuxZE30x2sym/B2OIWypjmPzXDRt8J9TOGFuFNw=="], "@expo/log-box": ["@expo/log-box@55.0.12", "", { "dependencies": { "@expo/dom-webview": "^55.0.6", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-f9ARS8J60cq3LLNdIqmUjYwyerBzVS5Ecp7KjIf3GOIPjW0571rkcwLz4/U18l/1DeSkSzIkYsNl2TC9oTdWaQ=="], "@expo/metro": ["@expo/metro@55.1.1", "", { "dependencies": { "metro": "0.83.7", "metro-babel-transformer": "0.83.7", "metro-cache": "0.83.7", "metro-cache-key": "0.83.7", "metro-config": "0.83.7", "metro-core": "0.83.7", "metro-file-map": "0.83.7", "metro-minify-terser": "0.83.7", "metro-resolver": "0.83.7", "metro-runtime": "0.83.7", "metro-source-map": "0.83.7", "metro-symbolicate": "0.83.7", "metro-transform-plugins": "0.83.7", "metro-transform-worker": "0.83.7" } }, "sha512-/wfXo5hTuAVpVLG/4hzlmD9NBGJkzkmBEMm/4VICajYRbj7y8OmqqPWbbymzHiBiHB6tI9BnsyXpQM6zVZEECg=="], - "@expo/metro-config": ["@expo/metro-config@55.0.20", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", "@expo/config": "~55.0.16", "@expo/env": "~2.1.2", "@expo/json-file": "~10.0.14", "@expo/metro": "~55.1.1", "@expo/spawn-async": "^1.7.2", "browserslist": "^4.25.0", "chalk": "^4.1.0", "debug": "^4.3.2", "getenv": "^2.0.0", "glob": "^13.0.0", "hermes-parser": "^0.32.0", "jsc-safe-url": "^0.2.4", "lightningcss": "^1.30.1", "picomatch": "^4.0.3", "postcss": "~8.4.32", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*" }, "optionalPeers": ["expo"] }, "sha512-dUv0simEyPbN2wbOjI+BdEZyXdghgCZD0+3rrA1WxXZN1lRofUx6g2+Nik2Qg61v/BXFrCTh8reYEzQPzHOhdQ=="], + "@expo/metro-config": ["@expo/metro-config@55.0.21", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "@babel/core": "^7.20.0", "@babel/generator": "^7.20.5", "@expo/config": "~55.0.17", "@expo/env": "~2.1.2", "@expo/json-file": "~10.0.14", "@expo/metro": "~55.1.1", "@expo/spawn-async": "^1.7.2", "browserslist": "^4.25.0", "chalk": "^4.1.0", "debug": "^4.3.2", "getenv": "^2.0.0", "glob": "^13.0.0", "hermes-parser": "^0.32.0", "jsc-safe-url": "^0.2.4", "lightningcss": "^1.30.1", "picomatch": "^4.0.3", "postcss": "~8.4.32", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*" }, "optionalPeers": ["expo"] }, "sha512-pJ8G0uCxqA9KK+XCzXZF7ZI37rduD2l7Cun2e3rVAgB2yeOZagUD+VBvooU9QPiWx9e/7EbimH5/JP81JyhQlg=="], - "@expo/metro-runtime": ["@expo/metro-runtime@55.0.10", "", { "dependencies": { "@expo/log-box": "55.0.11", "anser": "^1.4.9", "pretty-format": "^29.7.0", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-dom": "*", "react-native": "*" }, "optionalPeers": ["react-dom"] }, "sha512-7v+ldTvMWRa1ml83Jel9W2f8qT/NZZWrlHaEjf29nb72JTEO50+Xac9PWLo+X3LCDAAuyYuBGKYXOJwfqxV0fQ=="], + "@expo/metro-runtime": ["@expo/metro-runtime@55.0.11", "", { "dependencies": { "@expo/log-box": "55.0.12", "anser": "^1.4.9", "pretty-format": "^29.7.0", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-dom": "*", "react-native": "*" }, "optionalPeers": ["react-dom"] }, "sha512-4KKi/jGrIEXi2YGu0hYTVr0CEeRJy5SXbCrz9+KDZkuD3ROwKNpM1DBawni5rhPVovFnR323HBck9GaxhnfrRw=="], "@expo/osascript": ["@expo/osascript@2.4.3", "", { "dependencies": { "@expo/spawn-async": "^1.7.2" } }, "sha512-wbuj3EebM7W9hN/Wp4xTzKd6rQ2zKJzAxkFxkOOwyysLp0HOAgQ4/5RINyoS241pZUX2rUHq7mAJ7pcCQ8U0Ow=="], "@expo/package-manager": ["@expo/package-manager@1.10.5", "", { "dependencies": { "@expo/json-file": "^10.0.14", "@expo/spawn-async": "^1.7.2", "chalk": "^4.0.0", "npm-package-arg": "^11.0.0", "ora": "^3.4.0", "resolve-workspace-root": "^2.0.0" } }, "sha512-nCP9Mebfl3jvOr0/P6VAuyah6PAtun+aihIL2zAtuE8uSe94JWkVZ7051i0MUVO+y3gFpBqnr8IIH5ch+VJjHA=="], - "@expo/plist": ["@expo/plist@0.5.2", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-o4xdVdBpe4aTl3sPMZ2u3fJH4iG1I768EIRk1xRZP+GaFI93MaR3JvoFibYqxeTmLQ1p1kNEVqylfUjezxx45g=="], + "@expo/plist": ["@expo/plist@0.5.3", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-jz5oPcPDd3fygwVxwSwmO6wodTwm0Qa14NUyPy0ka7H8sFmCtNZUI2+DzVe/EXjOhq1FbEjrwl89gdlWYOnVjQ=="], - "@expo/prebuild-config": ["@expo/prebuild-config@55.0.17", "", { "dependencies": { "@expo/config": "~55.0.16", "@expo/config-plugins": "~55.0.8", "@expo/config-types": "^55.0.5", "@expo/image-utils": "^0.8.14", "@expo/json-file": "^10.0.14", "@react-native/normalize-colors": "0.83.6", "debug": "^4.3.1", "resolve-from": "^5.0.0", "semver": "^7.6.0", "xml2js": "0.6.0" }, "peerDependencies": { "expo": "*" } }, "sha512-Mcs+dg4Ripu0yCtzf66KZr18PehI1O8HxzJw+G5SUF8VWX+ic99aci1PltvmydWepLwTQL6ykmpXicAUA31IqA=="], + "@expo/prebuild-config": ["@expo/prebuild-config@55.0.18", "", { "dependencies": { "@expo/config": "~55.0.17", "@expo/config-plugins": "~55.0.9", "@expo/config-types": "^55.0.5", "@expo/image-utils": "^0.8.14", "@expo/json-file": "^10.0.14", "@react-native/normalize-colors": "0.83.6", "debug": "^4.3.1", "resolve-from": "^5.0.0", "semver": "^7.6.0", "xml2js": "0.6.0" }, "peerDependencies": { "expo": "*" } }, "sha512-2oKXyy5pyM87DJqXW5Z+Sakle6rApFFtpPhWOiNsOdoh6rOAD+EqVgyrs2OEEic8CE0tTt27w3SRfSZe/PZrxg=="], "@expo/react-native-action-sheet": ["@expo/react-native-action-sheet@4.1.1", "", { "dependencies": { "@types/hoist-non-react-statics": "^3.3.1", "hoist-non-react-statics": "^3.3.0" }, "peerDependencies": { "react": ">=18.0.0" } }, "sha512-4KRaba2vhqDRR7ObBj6nrD5uJw8ePoNHdIOMETTpgGTX7StUbrF4j/sfrP1YUyaPEa1P8FXdwG6pB+2WtrJd1A=="], @@ -1301,11 +1348,11 @@ "@expo/ws-tunnel": ["@expo/ws-tunnel@1.0.6", "", {}, "sha512-nDRbLmSrJar7abvUjp3smDwH8HcbZcoOEa5jVPUv9/9CajgmWw20JNRwTuBRzWIWIkEJDkz20GoNA+tSwUqk0Q=="], - "@expo/xcpretty": ["@expo/xcpretty@4.4.3", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "chalk": "^4.1.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-wC562eD3gS6vO2tWHToFhlFnmHKfKHgF1oyvojeSkLK/ZYop1bMU+7cOMiF9Sq70CzcsLy/EMRy/uRc76QmNRw=="], + "@expo/xcpretty": ["@expo/xcpretty@4.4.4", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "chalk": "^4.1.0", "js-yaml": "^4.1.0" }, "bin": { "excpretty": "build/cli.js" } }, "sha512-4aQzz9vgxcNXFfo/iyNgDDYfsU5XGKKxWxZopw0cVotHiW+U8IJbIxMaxsINs6bHhtkG3StKNPcOrn3eBuxKPw=="], "@feature-sliced/filesystem": ["@feature-sliced/filesystem@3.1.0", "", { "dependencies": { "typescript": "^5.8.3" } }, "sha512-kdNcVwJMCiORdi2Aog7epDeQMEREFUvdqqQHmE7D3ZjSdsQzZWMH6Ex7gtGAttTT/oRBFysqCtP5pl0CETEc9Q=="], - "@feature-sliced/steiger-plugin": ["@feature-sliced/steiger-plugin@0.5.7", "", { "dependencies": { "@feature-sliced/filesystem": "^3.0.1", "fastest-levenshtein": "^1.0.16", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", "precinct": "^12.1.2", "tsconfck": "^3.1.4" } }, "sha512-xENW2fvfU+UyMgcB3S+CCMPrH2Tq/BG1W9IxGp5B8+j0TtbgmbzT3DIj3A2HG4kouVYUjzgWA6JIuHbGo3lDzg=="], + "@feature-sliced/steiger-plugin": ["@feature-sliced/steiger-plugin@0.5.8", "", { "dependencies": { "@feature-sliced/filesystem": "^3.1.0", "fastest-levenshtein": "^1.0.16", "lodash-es": "^4.17.21", "pluralize": "^8.0.0", "precinct": "^12.2.0", "tsconfck": "^3.1.6" } }, "sha512-SXn2PQS0yVRzhltSM968hz0hWmNj93l7dNU4Qqry3OgSkvrO4kl1/daWYwRYzmm9sImsAUsTztabwLUbF9U9YA=="], "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], @@ -1325,7 +1372,7 @@ "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.2", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA=="], - "@gorhom/bottom-sheet": ["@gorhom/bottom-sheet@5.2.10", "", { "dependencies": { "@gorhom/portal": "1.0.14", "invariant": "^2.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-native": "*", "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", "react-native-reanimated": ">=3.16.0 || >=4.0.0-" }, "optionalPeers": ["@types/react", "@types/react-native"] }, "sha512-MnFddmVOlaoash0d9g1ClqFqX+32h/sV3PNEFz9A8XCvUbZGQM9OG6HHAzTb+eQfUGA8DkaurI+wfpNFyzj5Yw=="], + "@gorhom/bottom-sheet": ["@gorhom/bottom-sheet@5.2.14", "", { "dependencies": { "@gorhom/portal": "1.0.14", "invariant": "^2.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-native": "*", "react": "*", "react-native": "*", "react-native-gesture-handler": ">=2.16.1", "react-native-reanimated": ">=3.16.0 || >=4.0.0-" }, "optionalPeers": ["@types/react", "@types/react-native"] }, "sha512-uLQFlDjp9z+jrOFcMSEldPqL5JdaXL3vXOh+juhwoNvXgTsEorJLjHTugXu+YccAG/0KJnShzKCrb71MHBsvJg=="], "@gorhom/portal": ["@gorhom/portal@1.0.14", "", { "dependencies": { "nanoid": "^3.3.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-MXyL4xvCjmgaORr/rtryDNFy3kU4qUbKlwtQqqsygd0xX3mhKjOLn6mQK8wfu0RkoE0pBE0nAasRoHua+/QZ7A=="], @@ -1421,7 +1468,7 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@legendapp/state": ["@legendapp/state@3.0.0-beta.46", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "expo-sqlite": "^15.0.0" }, "optionalPeers": ["expo-sqlite"] }, "sha512-TcCabsE9jPW2r0sKQbUet46L0hbWiupKoun9UUkcHyF/6Jec1RyJCmLrdgFPnYZ9HwupJKIRxJVlxNrg2tG3SQ=="], + "@legendapp/state": ["@legendapp/state@3.0.0-beta.47", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "expo-sqlite": "^15.0.0" }, "optionalPeers": ["expo-sqlite"] }, "sha512-MPgPacXXSoAazAv7ulW/o0ZAtK4YHk3twvXZ241l2HqAHciHozb7tg5SMbEAc2HKUUfC3JBh+9+DXfMsYokLpQ=="], "@lhci/cli": ["@lhci/cli@0.14.0", "", { "dependencies": { "@lhci/utils": "0.14.0", "chrome-launcher": "^0.13.4", "compression": "^1.7.4", "debug": "^4.3.1", "express": "^4.17.1", "inquirer": "^6.3.1", "isomorphic-fetch": "^3.0.0", "lighthouse": "12.1.0", "lighthouse-logger": "1.2.0", "open": "^7.1.0", "proxy-agent": "^6.4.0", "tmp": "^0.1.0", "uuid": "^8.3.1", "yargs": "^15.4.1", "yargs-parser": "^13.1.2" }, "bin": { "lhci": "./src/cli.js" } }, "sha512-TxOH9pFBnmmN7Jmo2Aimxx5UhE8veqXpHfFJDMWsCVxkwh7mGxcAWchGl84mK139SZbbRmerqZ72c+h2nG9/QQ=="], @@ -1443,23 +1490,23 @@ "@neondatabase/serverless": ["@neondatabase/serverless@1.1.0", "", {}, "sha512-r3ZZhRjEcfEdKIZnoB1RusNgvHuaBRqfCzV4Gi+5A9yUX0S4HTws/ASWqt13wL4y4I+0rqsWGdA2w7EQXHi3+Q=="], - "@next/env": ["@next/env@15.5.15", "", {}, "sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg=="], + "@next/env": ["@next/env@15.5.18", "", {}, "sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g=="], - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6PvFO2Tzt10GFK2Ro9tAVEtacMqRmTarYMFKAnV2vYMdwWc73xzmDQyAV7SwEdMhzmiRoo7+m88DuiXlJlGeaw=="], + "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.5.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-w0WvQf1n+txiwns/9pwIQteCJpZTbxzO2SE0FLcwuD4v0WEh1JPOjdyxWL21XwJsdpx8cFRjyzxzCS/siP7HcQ=="], - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-G+YNV+z6FDZTp/+IdGyIMFqalBTaQSnvAA+X/hrt+eaTRFSznRMz9K7rTmzvM6tDmKegNtyzgufZW0HwVzEqaQ=="], + "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.5.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-znn71QmDuxm+BOaglihMZfvyySMnNljkVIY5Z2TCssBmm+WqL6c19VhtH5ktFkHa8EZ2bnTUpcNcmNSQsg67og=="], - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eVkrMcVIBqGfXB+QUC7jjZ94Z6uX/dNStbQFabewAnk13Uy18Igd1YZ/GtPRzdhtm7QwC0e6o7zOQecul4iC1w=="], + "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.5.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-yPPe5MNL+igZUa+OsqQJisqSfh6oarIuA1Q0BDxljGJhRQyZeP+WRHh7rs/jZUGMh5aY0YdIjXZG0VohkKkUdw=="], - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-RwSHKMQ7InLy5GfkY2/n5PcFycKA08qI1VST78n09nN36nUPqCvGSMiLXlfUmzmpQpF6XeBYP2KRWHi0UW3uNg=="], + "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.5.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-glaCczEWIrHsokFZ3pP08U4BpKxwIdnT+txdOM32OBgpL9Yw4aqx8NejmgtZQZOdstQ5f0L3CasIZudzCuD+nw=="], - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.15", "", { "os": "linux", "cpu": "x64" }, "sha512-nplqvY86LakS+eeiuWsNWvfmK8pFcOEW7ZtVRt4QH70lL+0x6LG/m1OpJ/tvrbwjmR8HH9/fH2jzW1GlL03TIg=="], + "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.5.18", "", { "os": "linux", "cpu": "x64" }, "sha512-oUfg2EgJmU3R0OCOWiokGFUTvZiPfXtriXiuF3YNxRoROCdgvTedHIzYoeKH34gsZxS/V7mHbfq2hpAHwhH1/A=="], - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.15", "", { "os": "linux", "cpu": "x64" }, "sha512-eAgl9NKQ84/sww0v81DQINl/vL2IBxD7sMybd0cWRw6wqgouVI53brVRBrggqBRP/NWeIAE1dm5cbKYoiMlqDQ=="], + "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.5.18", "", { "os": "linux", "cpu": "x64" }, "sha512-JLxSP3KTd9iu/bvUMQxH7RJo9xKSHf55/6RPE4a6FTSZygGn7uvZbCej0AHXydwkggQGSD9UddSjwv6Xz5ESfA=="], - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-GJVZC86lzSquh0MtvZT+L7G8+jMnJcldloOjA8Kf3wXvBrvb6OGe2MzPuALxFshSm/IpwUtD2mIoof39ymf52A=="], + "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.5.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-ir1v7enP52K2HNz3tQQvwF+x7VNxBk1ciiZ18WBPvxf4C59IqdfmHPJYK3vH7rSxpuCVw/8C712wTXNAtEp+NA=="], - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.15", "", { "os": "win32", "cpu": "x64" }, "sha512-nFucjVdwlFqxh/JG3hWSJ4p8+YJV7Ii8aPDuBQULB6DzUF4UNZETXLfEUk+oI2zEznWWULPt7MeuTE6xtK1HSA=="], + "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.5.18", "", { "os": "win32", "cpu": "x64" }, "sha512-LIu5me6QTANCd25E7I5uIEfvgQ06RK7tvHAbYo3zCb3VpxQEPvMcSpd87NwUABDT6MbGPdEGR5VRiK4PPTJhQg=="], "@noble/ciphers": ["@noble/ciphers@2.2.0", "", {}, "sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA=="], @@ -1471,11 +1518,11 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + "@opentelemetry/api": ["@opentelemetry/api@1.9.1", "", {}, "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q=="], - "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.40.0", "", {}, "sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw=="], + "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="], - "@oxc-project/types": ["@oxc-project/types@0.127.0", "", {}, "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ=="], + "@oxc-project/types": ["@oxc-project/types@0.130.0", "", {}, "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q=="], "@packrat-ai/nativewindui": ["@packrat-ai/nativewindui@2.0.3-2", "https://npm.pkg.github.com/download/@packrat-ai/nativewindui/2.0.3-2/cae289eeb09d41ea394e5273bc4694596e3facc3", { "peerDependencies": { "@expo/vector-icons": ">=15.0.0", "@gorhom/bottom-sheet": "^5.1.2", "@react-native-community/datetimepicker": "^8.4.0", "@react-native-community/slider": "^5.0.0", "@react-native-picker/picker": "^2.11.0", "@react-native-segmented-control/segmented-control": "^2.5.0", "@react-navigation/drawer": "^7.1.1", "@react-navigation/elements": "^2.3.1", "@react-navigation/native": "^7.0.14", "@rn-primitives/alert-dialog": "^1.1.0", "@rn-primitives/avatar": "^1.0.4", "@rn-primitives/checkbox": "^1.1.0", "@rn-primitives/context-menu": "^1.1.0", "@rn-primitives/dropdown-menu": "^1.1.0", "@rn-primitives/hooks": "^1.1.0", "@rn-primitives/portal": "^1.1.0", "@rn-primitives/slot": "^1.1.0", "@shopify/flash-list": "^2.0.0", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "expo-blur": "~15.0.8", "expo-device": "~8.0.0", "expo-glass-effect": "*", "expo-haptics": "~15.0.8", "expo-image": "~3.0.11", "expo-linear-gradient": "~15.0.8", "expo-navigation-bar": "~5.0.10", "expo-router": ">=6.0.23", "expo-symbols": "~1.0.8", "nativewind": "^4.2.3", "react": ">=19.0.0", "react-native": ">=0.79.0", "react-native-keyboard-controller": "^1.16.7", "react-native-reanimated": ">=3.17.0", "react-native-safe-area-context": ">=5.4.0", "react-native-screens": ">=4.11.0", "react-native-uitextview": "^1.1.4", "rn-icon-mapper": "^0.0.1", "tailwind-merge": "^2.2.1" } }, "sha512-gimhxLYi3IiAqV4h0s1pzPb4mxy07XOcgRkhN8nOVRi7t6Ucb5dOGv2ArWY3WfQlSrN52dPRxzDtdsnIn33FRg=="], @@ -1683,19 +1730,19 @@ "@react-native/virtualized-lists": ["@react-native/virtualized-lists@0.83.6", "", { "dependencies": { "invariant": "^2.2.4", "nullthrows": "^1.1.1" }, "peerDependencies": { "@types/react": "^19.2.0", "react": "*", "react-native": "*" }, "optionalPeers": ["@types/react"] }, "sha512-gNSFXeb4P7qHtauLvl+zESroULIyX6Ltpvau3dhwy/QmfanBv0KUcrIU/7aVXxtWcXgp+54oWJyu2LIrsZ9+LQ=="], - "@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.15.10", "", { "dependencies": { "@react-navigation/elements": "^2.9.15", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.2.2", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-Ao/yYlrpr0cwYYGxt9FDMQk+tTSHNm4WTaszyhroINLdoEMuKH19k1tGFdYbRBKHJx1UIH8kD+EZTYW1w6LL3Q=="], + "@react-navigation/bottom-tabs": ["@react-navigation/bottom-tabs@7.16.1", "", { "dependencies": { "@react-navigation/elements": "^2.9.18", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0" }, "peerDependencies": { "@react-navigation/native": "^7.2.4", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-wjFATJmbq0K8B96Ax0JcK2+Eu7syfYvQ5qUd/tgcv8JuCYLwKKqojJMAl31qdjpKqFG09pQ6TSdEDHOek60CAA=="], - "@react-navigation/core": ["@react-navigation/core@7.17.2", "", { "dependencies": { "@react-navigation/routers": "^7.5.3", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "query-string": "^7.1.3", "react-is": "^19.1.0", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-Rt2OZwcgOmjv401uLGAKaRM6xo0fiBce/A7LfRHI1oe5FV+KooWcgAoZ2XOtgKj6UzVMuQWt3b2e6rxo/mDJRA=="], + "@react-navigation/core": ["@react-navigation/core@7.17.4", "", { "dependencies": { "@react-navigation/routers": "^7.5.5", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "query-string": "^7.1.3", "react-is": "^19.1.0", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": ">= 18.2.0" } }, "sha512-Rv9E2oNNQEkPGpmu9q+vJwGJRSQR6LBg5L+Yo1QHjtwGbHUbjkIKOdYymDZoZYgNzX2OD4rAIlfuzbDKa3cCeA=="], - "@react-navigation/drawer": ["@react-navigation/drawer@7.9.9", "", { "dependencies": { "@react-navigation/elements": "^2.9.15", "color": "^4.2.3", "react-native-drawer-layout": "^4.2.2", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "@react-navigation/native": "^7.2.2", "react": ">= 18.2.0", "react-native": "*", "react-native-gesture-handler": ">= 2.0.0", "react-native-reanimated": ">= 2.0.0", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-ZeHhx5MH7Y/qG+28KU0PDtBjNcNnpvnafPwIoSzSrN8M55HvtQex90TP3ylmHtErhw2RDWlp30vpmWvG0wvFIA=="], + "@react-navigation/drawer": ["@react-navigation/drawer@7.10.2", "", { "dependencies": { "@react-navigation/elements": "^2.9.18", "color": "^4.2.3", "react-native-drawer-layout": "^4.2.4", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "@react-navigation/native": "^7.2.4", "react": ">= 18.2.0", "react-native": "*", "react-native-gesture-handler": ">= 2.0.0", "react-native-reanimated": ">= 2.0.0", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-/ccYFvBPJNzOYioiMQsqjAR4dcQ+7+yjzcuMDTKgsMahLD7Jn7FdOFNtGwMaIQWhfK8KFVMH2KOXAlH/uAGZXw=="], - "@react-navigation/elements": ["@react-navigation/elements@2.9.15", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.2.2", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-cyz/pPiyyC6gaTVLsGFc1g0MYgrmuCFqklAWGXMWPscr5YU3ui94vPI4vnZwcsEy0T758TQWLzmS5XudZeRKcA=="], + "@react-navigation/elements": ["@react-navigation/elements@2.9.18", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "@react-native-masked-view/masked-view": ">= 0.2.0", "@react-navigation/native": "^7.2.4", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0" }, "optionalPeers": ["@react-native-masked-view/masked-view"] }, "sha512-mKEvDr6CkCVYZSb8W9WubNseihL+1c8M7ktZJCTCbMk8rQgdQfkdRNwpSUQKspdGpUHCb9cyzvaiuzl1NtjVgw=="], - "@react-navigation/native": ["@react-navigation/native@7.2.2", "", { "dependencies": { "@react-navigation/core": "^7.17.2", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*" } }, "sha512-kem1Ko2BcbAjmbQIv66dNmr6EtfDut3QU0qjsVhMnLLhktwyXb6FzZYp8gTrUb6AvkAbaJoi+BF5Pl55pAUa5w=="], + "@react-navigation/native": ["@react-navigation/native@7.2.4", "", { "dependencies": { "@react-navigation/core": "^7.17.4", "escape-string-regexp": "^4.0.0", "fast-deep-equal": "^3.1.3", "nanoid": "^3.3.11", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*" } }, "sha512-eWC2D3JjhYLId2fVTZhhCiUpWIaPhO9XyEb7Wq8ElmOHyIODlbOzgZ0rKia02OIsDKr9BzZl2sK1dL70yMxDaw=="], - "@react-navigation/native-stack": ["@react-navigation/native-stack@7.14.12", "", { "dependencies": { "@react-navigation/elements": "^2.9.15", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0", "warn-once": "^0.1.1" }, "peerDependencies": { "@react-navigation/native": "^7.2.2", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-dUfpkrVeVKKV8iqXsmoUp3Rv0iH3YaB3eZwScru/FlcqAp/r3/qA6zEXkGX9hZK+/ziWAPFrf1frBSNbgOYSFQ=="], + "@react-navigation/native-stack": ["@react-navigation/native-stack@7.15.1", "", { "dependencies": { "@react-navigation/elements": "^2.9.18", "color": "^4.2.3", "sf-symbols-typescript": "^2.1.0", "warn-once": "^0.1.1" }, "peerDependencies": { "@react-navigation/native": "^7.2.4", "react": ">= 18.2.0", "react-native": "*", "react-native-safe-area-context": ">= 4.0.0", "react-native-screens": ">= 4.0.0" } }, "sha512-kNrJggwoB/onC0MpZIuZ6qaqeAziFchz+W9txBzhd6qbWmB1OkPVUnu6fWgc6BQc7MeMf59djVmqgX+6kJU1Ug=="], - "@react-navigation/routers": ["@react-navigation/routers@7.5.3", "", { "dependencies": { "nanoid": "^3.3.11" } }, "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg=="], + "@react-navigation/routers": ["@react-navigation/routers@7.5.5", "", { "dependencies": { "nanoid": "^3.3.11" } }, "sha512-9/hhMte12Kgu+pMnLfA4EWJ0OQmIEAMVMX06FPH2yGkEQSQ3JhhCN/GkcRikzQhtEi97VYYQA15umptBUShcOQ=="], "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="], @@ -1719,89 +1766,89 @@ "@rn-primitives/utils": ["@rn-primitives/utils@1.4.0", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-nMFZ99AGKakMRDAlfbsYUfqwKO0LItWtp58YTwxmNuGVhXG43/zIfyWWaB3FJeOL+hhcpUn0YR7C1Vsrg0FgvQ=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.17", "", { "os": "android", "cpu": "arm64" }, "sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.1", "", { "os": "android", "cpu": "arm64" }, "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-4ksWc9n0mhlZpZ9PMZgTGjeOPRu8MB1Z3Tz0Mo02eWfWCHMW1zN82Qz/pL/rC+yQa+8ZnutMF0JjJe7PjwasYw=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-SUSDOI6WwUVNcWxd02QEBjLdY1VPHvlEkw6T/8nYG322iYWCTxRb1vzk4E+mWWYehTp7ERibq54LSJGjmouOsw=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.17", "", { "os": "freebsd", "cpu": "x64" }, "sha512-hwnz3nw9dbJ05EDO/PvcjaaewqqDy7Y1rn1UO81l8iIK1GjenME75dl16ajbvSSMfv66WXSRCYKIqfgq2KCfxw=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm" }, "sha512-IS+W7epTcwANmFSQFrS1SivEXHtl1JtuQA9wlxrZTcNi6mx+FDOYrakGevvvTwgj2JvWiK8B29/qD9BELZPyXQ=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.1", "", { "os": "linux", "cpu": "arm" }, "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-e6usGaHKW5BMNZOymS1UcEYGowQMWcgZ71Z17Sl/h2+ZziNJ1a9n3Zvcz6LdRyIW5572wBCTH/Z+bKuZouGk9Q=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-b/CgbwAJpmrRLp02RPfhbudf5tZnN9nsPWK82znefso832etkem8H7FSZwxrOI9djcdTP7U6YfNhbRnh7djErg=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4EII1iNGRUN5WwGbF/kOh/EIkoDN9HsupgLQoXfY+D1oyJm7/F4t5PYU5n8SWZgG0FEwakyM8pGgwcBYruGTlA=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "s390x" }, "sha512-AH8oq3XqQo4IibpVXvPeLDI5pzkpYn0WiZAfT05kFzoJ6tQNzwRdDYQ45M8I/gslbodRZwW8uxLhbSBbkv96rA=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-cLnjV3xfo7KslbU41Z7z8BH/E1y5mzUYzAqih1d1MDaIGZRCMqTijqLv76/P7fyHuvUcfGsIpqCdddbxLLK9rA=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.17", "", { "os": "linux", "cpu": "x64" }, "sha512-0phclDw1spsL7dUB37sIARuis2tAgomCJXAHZlpt8PXZ4Ba0dRP1e+66lsRqrfhISeN9bEGNjQs+T/Fbd7oYGw=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.17", "", { "os": "none", "cpu": "arm64" }, "sha512-0ag/hEgXOwgw4t8QyQvUCxvEg+V0KBcA6YuOx9g0r02MprutRF5dyljgm3EmR02O292UX7UeS6HzWHAl6KgyhA=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.1", "", { "os": "none", "cpu": "arm64" }, "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.17", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-LEXei6vo0E5wTGwpkJ4KoT3OZJRnglwldt5ziLzOlc6qqb55z4tWNq2A+PFqCJuvWWdP53CVhG1Z9NtToDPJrA=="], + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.1", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-gUmyzBl3SPMa6hrqFUth9sVfcLBlYsbMzBx5PlexMroZStgzGqlZ26pYG89rBb45Mnia+oil6YAIFeEWGWhoZA=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.17", "", { "os": "win32", "cpu": "x64" }, "sha512-3hkiolcUAvPB9FLb3UZdfjVVNWherN1f/skkGWJP/fgSQhYUZpSIRr0/I8ZK9TkF3F7kxvJAk0+IcKvPHk9qQg=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.1", "", { "os": "win32", "cpu": "x64" }, "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ=="], "@rolldown/plugin-babel": ["@rolldown/plugin-babel@0.2.3", "", { "dependencies": { "picomatch": "^4.0.4" }, "peerDependencies": { "@babel/core": "^7.29.0 || ^8.0.0-rc.1", "@babel/plugin-transform-runtime": "^7.29.0 || ^8.0.0-rc.1", "@babel/runtime": "^7.27.0 || ^8.0.0-rc.1", "rolldown": "^1.0.0-rc.5", "vite": "^8.0.0" }, "optionalPeers": ["@babel/plugin-transform-runtime", "@babel/runtime", "vite"] }, "sha512-+zEk16yGlz1F9STiRr6uG9hmIXb6nprjLczV/htGptYuLoCuxb+itZ03RKCEeOhBpDDd1NU7qF6x1VLMUp62bw=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.17", "", {}, "sha512-n8iosDOt6Ig1UhJ2AYqoIhHWh/isz0xpicHTzpKBeotdVsTEcxsSA/i3EVM7gQAj0rU27OLAxCjzlj15IWY7bg=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.2", "", { "os": "android", "cpu": "arm" }, "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.3", "", { "os": "android", "cpu": "arm" }, "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.2", "", { "os": "android", "cpu": "arm64" }, "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.60.3", "", { "os": "android", "cpu": "arm64" }, "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.60.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.60.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.60.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.60.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.2", "", { "os": "linux", "cpu": "arm" }, "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.60.3", "", { "os": "linux", "cpu": "arm" }, "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.2", "", { "os": "linux", "cpu": "arm" }, "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.60.3", "", { "os": "linux", "cpu": "arm" }, "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.60.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.60.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA=="], - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.60.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ=="], - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.60.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.2", "", { "os": "linux", "cpu": "none" }, "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.60.3", "", { "os": "linux", "cpu": "none" }, "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.60.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.2", "", { "os": "linux", "cpu": "x64" }, "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.60.3", "", { "os": "linux", "cpu": "x64" }, "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.2", "", { "os": "linux", "cpu": "x64" }, "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.60.3", "", { "os": "linux", "cpu": "x64" }, "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA=="], - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.60.3", "", { "os": "openbsd", "cpu": "x64" }, "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.2", "", { "os": "none", "cpu": "arm64" }, "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.60.3", "", { "os": "none", "cpu": "arm64" }, "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.60.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.60.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.2", "", { "os": "win32", "cpu": "x64" }, "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.60.3", "", { "os": "win32", "cpu": "x64" }, "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.2", "", { "os": "win32", "cpu": "x64" }, "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.60.3", "", { "os": "win32", "cpu": "x64" }, "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA=="], "@ronradtke/react-native-markdown-display": ["@ronradtke/react-native-markdown-display@8.1.0", "", { "dependencies": { "css-to-react-native": "^3.2.0", "markdown-it": "^13.0.1", "prop-types": "^15.7.2", "react-native-fit-image": "^1.5.5" }, "peerDependencies": { "react": ">=16.2.0", "react-native": ">=0.50.4" } }, "sha512-pAtefWI76vpkxsEgIFivyq1q6ej8rDyR7oVM/cWAxUydyBej9LOvULjLAeFuFLbYAelHTNoYXmGxQOlFLBa0+w=="], @@ -1865,105 +1912,87 @@ "@sinonjs/fake-timers": ["@sinonjs/fake-timers@10.3.0", "", { "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA=="], - "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-St+kVicSyayWQca+I1rGitaOEH6uKgE8IUWoYnnEX26SWdWQcL6LvMSD19Lg+vYHKdT9B2Zuu7rd3i6Wnyb/iw=="], - - "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.3", "", { "dependencies": { "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-jA5k5Udn7Y5717L86h4EIv06wIr3xn8GM1qHRi/Nf31annXcXHJjBKvgztnbn2TxH3xWrPBfgwHsOwZf0UmQWw=="], - - "@smithy/config-resolver": ["@smithy/config-resolver@4.4.17", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.4.2", "@smithy/util-middleware": "^4.2.14", "tslib": "^2.6.2" } }, "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ=="], + "@smithy/config-resolver": ["@smithy/config-resolver@4.5.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-Gxr1czgeGUKgUJBf3fUSvpb1d+EjEl17f5s8qAzn36QIWmVzNPZQb3C9Rdtfya1yu2qUSAhDGoHUvHI/GJjbBQ=="], - "@smithy/core": ["@smithy/core@3.23.17", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-stream": "^4.5.25", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ=="], + "@smithy/core": ["@smithy/core@3.24.2", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-IKS7qX59fAGCYBmt5JChcDswQDupZqT2Yn2ZBA3UgTlsjRNNkQzZobbn95xoAAdtTyJmBiJB3Y02qR3rgy3Zog=="], - "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.14", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.14", "@smithy/property-provider": "^4.2.14", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "tslib": "^2.6.2" } }, "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg=="], + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-iYr9ekBjmZ+FwkiHEopqGscBbl78X62cq3p5Dd0eC+gNd7fybNZFQQdDuOQjTVmFymleuA8YRWZnuXWZ8B3kKA=="], - "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.14", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw=="], + "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-wq3p8g8pyciXSTmysvOvpGeMQaPssgJqyJdfKquwAgpgRW5cKcXb4c0V/K48Lsn24oYK/cox/vHtj/eaGm0PEg=="], - "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.14", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-8IelTCtTctWRbb+0Dcy+C0aICh1qa0qWXqgjcXDmMuCvPJRnv26hiDZoAau2ILOniki65mCPKqOQs/BaWvO4CQ=="], + "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-/Z4Rn+d/HzU96Ciy2bPDZq2KdlRM18ZhGSiy1lSUZPCpGQxXzGQCIItsk3vMcioBHn8E5t48LEXpTL0rOogrSA=="], - "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-sqHiHpYRYo3FJlaIxD1J8PhbcmJAm7IuM16mVnwSkCToD7g00IBZzKuiLNMGmftULmEUX6/UAz8/NN5uMP8bVA=="], + "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-D4b+U+tOm9DVB8sk0/iDTVYLtBbid9T4+kX25k3rLie1SNqsaKNPTkx6dTWuB4TGNczKixeTCB4RGiV7KGO4Jw=="], - "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.14", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-Ht/8BuGlKfFTy0H3+8eEu0vdpwGztCnaLLXtpXNdQqiR7Hj4vFScU3T436vRAjATglOIPjJXronY+1WxxNLSiw=="], + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-3wF40g8OOCA5BnwQUvwtzZqYBbWWftDjpAlWIUo6Yld3ZzJaMAKqg7MWQBPjE8oLaqvZQUE7tVGlZPsae6A4bQ=="], - "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.14", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-lWyt4T2XQZUZgK3tQ3Wn0w3XBvZsK/vjTuJl6bXbnGZBHH0ZUSONTYiK9TgjTTzU54xQr3DRFwpjmhp0oLm3gg=="], + "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-p/X2kAc11zKkSZYW4bQb2CuWB0rZyXz52UAOtDpMpj8gXlVE7K9NU3sqh3gpTsPFS/2qvMoDS/w1a839k+815w=="], - "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.17", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/querystring-builder": "^4.2.14", "@smithy/types": "^4.14.1", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" } }, "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw=="], + "@smithy/hash-node": ["@smithy/hash-node@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-27ImyEVgDZ2ZQz1rnQMSlw9rm7x3244oZVIFI1WKi3vNtbxQ75XU2jA9BQuH8eb91zBLlyGjWQ/L/QGvmJfEHA=="], - "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.15", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.2", "@smithy/chunked-blob-reader-native": "^4.2.3", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-0PJ4Al3fg2nM4qKrAIxyNcApgqHAXcBkN8FeizOz69z0rb26uZ6lMESYtxegaTlXB5Hj84JfwMPavMrwDMjucA=="], + "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-eUIHSr4RWqMQVtsnZ4p4tNDFuyCuOFCO3cfkvMHLbTRUIbCAq/7XEeGor7h0o0KYMGLeBztOKLQ0WQyc0j/8gA=="], - "@smithy/hash-node": ["@smithy/hash-node@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g=="], + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-bP0RYUtgINyMsUEzTnjnwJ/NX9Aa8y7bj0NbqwrMMqT6vjpp7H9SqGuKsn0+wD+Fu4GXcxUex1mH3k0t6WsatQ=="], - "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-tw4GANWkZPb6+BdD4Fgucqzey2+r73Z/GRo9zklsCdwrnxxumUV83ZIaBDdudV4Ylazw3EPTiJZhpX42105ruQ=="], + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-JYNfdKj1kr3ke+Pip+qxiuXW/OuSe8KlCyJz93bU25uKVq6wQGdm6H0/1g2XoFjehJFUmNdS+2/nM6Oabe6/+A=="], - "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw=="], + "@smithy/md5-js": ["@smithy/md5-js@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-i7ES+52XcEuvIYaZrSePh8YHoLjI43hC3HO/KLO2osWEaUdX8kh4YpE6/iyfVud1M1vB3zEGHvwmsmqxDhw/Xw=="], - "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow=="], + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-Zq4MFXu6Cl/00FKXcc6mkio0xzd6D9Q8+AmtBOC6vHpN6Fz1MButZ0zfL46WxhJH/JgKjv5xl4Qo8HZsr6sDBg=="], - "@smithy/md5-js": ["@smithy/md5-js@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-V2v0vx+h0iUSNG1Alt+GNBMSLGCrl9iVsdd+Ap67HPM9PN479x12V8LkuMoKImNZxn3MXeuyUjls+/7ZACZghA=="], + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.5.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-1aiE05F2Er+ZYvGfrfI0+JRNeFY4M+hSjUp0SIKyq98k9iC0Lo71XwLbKWcFgFBZuhg5n6i+MQzRVmeCDlWOjw=="], - "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.14", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw=="], + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.6.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-ovt2p0LV3sSRpt8EBajI6imGMz/kq8F73P0eSOq6bhtvcOZM3xODFOjk6Lz2KvKfe7dVlxpNIiOYYyVe8ofv0A=="], - "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.32", "", { "dependencies": { "@smithy/core": "^3.23.17", "@smithy/middleware-serde": "^4.2.20", "@smithy/node-config-provider": "^4.3.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "@smithy/url-parser": "^4.2.14", "@smithy/util-middleware": "^4.2.14", "tslib": "^2.6.2" } }, "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q=="], + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-hM3b9/JgMjohYHcgh5780ymND7RWGvYuyjNDtQL6P/DawtdbUu5m4oon4FHas+rmjdQ2DHee3cmAetTgU6keJw=="], - "@smithy/middleware-retry": ["@smithy/middleware-retry@4.5.5", "", { "dependencies": { "@smithy/core": "^3.23.17", "@smithy/node-config-provider": "^4.3.14", "@smithy/protocol-http": "^5.3.14", "@smithy/service-error-classification": "^4.3.0", "@smithy/smithy-client": "^4.12.13", "@smithy/types": "^4.14.1", "@smithy/util-middleware": "^4.2.14", "@smithy/util-retry": "^4.3.4", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" } }, "sha512-wnYOpB5vATFKWrY2Z9Alb0KhjZI6AbzU6Fbz3Hq2GnURdRYWB4q+qWivQtSTwXcmWUA3MZ6krfwL6Cq5MAbxsA=="], + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-WthvBnmmksOBbW2I8CRc35QUJn/whgXucpW/hNmE70znXG16/yuC7LGtbDYTr7ak+LNZKzBGmaQR1uDNpeBd1g=="], - "@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.20", "", { "dependencies": { "@smithy/core": "^3.23.17", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ=="], + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-LKup5BaU51fQM1YI/T64SJnn561tUPCfbpStnEygz5u1AqSAOALYY4e8imzz2EuMjcUtaYhOBtMazaN3TRXTgw=="], - "@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA=="], + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.7.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-EdksTZ8UXYxGUgQ4mpIKrHoaj9WVGsp66TpZuixLAz1Jex8YDLnS4RH9ktGED5aOpN0OJlEtrsC9IGt76go1eA=="], - "@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.14", "", { "dependencies": { "@smithy/property-provider": "^4.2.14", "@smithy/shared-ini-file-loader": "^4.4.9", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg=="], + "@smithy/property-provider": ["@smithy/property-provider@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-utUCslUrmJxKqSEidPhE1yfBgox78yhZQcHfdU4qvh79Rhi0nNKq6xNG4J/IwPD+Pm9y7a5FdXOgJxmHU5CGoA=="], - "@smithy/node-http-handler": ["@smithy/node-http-handler@4.6.1", "", { "dependencies": { "@smithy/protocol-http": "^5.3.14", "@smithy/querystring-builder": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg=="], + "@smithy/protocol-http": ["@smithy/protocol-http@5.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-rUvFCkbaHglm1zgOJ3QKFcD8jx/e68OUme3n+kAEiefAcGHK46UtmIT0/cuVLuiuSZzTqo8ts0Ju5hy9wqMGZA=="], - "@smithy/property-provider": ["@smithy/property-provider@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ=="], + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-SrH20goaJWw2U3IHjBNxz2uVuIQWi36pDoCgxL0Uu1lu2T2ZPxsp+I0o+CNYSqsci5y6r029ZhOFibA9ZZFpeA=="], - "@smithy/protocol-http": ["@smithy/protocol-http@5.3.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ=="], + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.5.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-4Cyy2IrFCgZBdw8G5eqB0G5FQ3HwH9FRYMJXGAMdo7hVIguVwQtLvEMmveIBlHf6hKQBd116M9IQRCA/7sxrpg=="], - "@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A=="], + "@smithy/signature-v4": ["@smithy/signature-v4@5.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-1km1OjdLRFuITWpCPofjFqzZ+tbeWuB72ZhcYjbjkCxZ21tTPfIs4GUxRrelMyKMLxLghGD58RENnXorU/O8cw=="], - "@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw=="], - - "@smithy/service-error-classification": ["@smithy/service-error-classification@4.3.0", "", { "dependencies": { "@smithy/types": "^4.14.1" } }, "sha512-9jKsBYQRPR0xBLgc2415RsA5PIcP2sis4oBdN9s0D13cg1B1284mNTjx9Yc+BEERXzuPm5ObktI96OxsKh8E9A=="], - - "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.9", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ=="], - - "@smithy/signature-v4": ["@smithy/signature-v4@5.3.14", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.14", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA=="], - - "@smithy/smithy-client": ["@smithy/smithy-client@4.12.13", "", { "dependencies": { "@smithy/core": "^3.23.17", "@smithy/middleware-endpoint": "^4.4.32", "@smithy/middleware-stack": "^4.2.14", "@smithy/protocol-http": "^5.3.14", "@smithy/types": "^4.14.1", "@smithy/util-stream": "^4.5.25", "tslib": "^2.6.2" } }, "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA=="], + "@smithy/smithy-client": ["@smithy/smithy-client@4.13.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-lK+Ssl8FzZHvdiPwB6qWLlPV6ih8FCr2BbRV+6/7QWabtMoiTbMTiGrrKsfKu6fcYzt+5akGAY7Xqna+EySq5g=="], "@smithy/types": ["@smithy/types@4.14.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg=="], - "@smithy/url-parser": ["@smithy/url-parser@4.2.14", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ=="], + "@smithy/url-parser": ["@smithy/url-parser@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-ADEFjhX7Iv+cWrwkK+31tqoUzICzYAjB8GvpGDMoCQ71CA519Qd1ZRg1gDveYe20WQJxwW/ls4F0fSMZZTZnQQ=="], - "@smithy/util-base64": ["@smithy/util-base64@4.3.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ=="], + "@smithy/util-base64": ["@smithy/util-base64@4.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-+dxoGuQMmtP/m3ApPgliWQOTasVP9AHaKWCvczdiJlYk0SmTWrXMKq3lyiooeqMXWLuptcptQPiUYPitGzJjQQ=="], - "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ=="], + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-69ETVcLni5dcIneXl7/Kc9Km/MqxOB4rGtr0zhuiVAz6mEr4QLo0P+c9bHZZzqrL5Tizd3fbPOVYpUOW1w4+MQ=="], - "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.3", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g=="], + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-P6E2/3h8FZgMadSoUx/xZALw8pwDBQux4W/AYu1TwM/icy/P2G0LXjhLkBclgaR6AJr6xXjaT5p+vivgIQ+wYQ=="], - "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q=="], + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - "@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ=="], + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-gVuLsMyqePBzTD4BY+VeOxT/o09t+QEwGClHh7yNDL7Wl+XktX6qXmAkAAjAAzOPI8u+ZpoQFq9+ooH2ftKnFw=="], - "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.49", "", { "dependencies": { "@smithy/property-provider": "^4.2.14", "@smithy/smithy-client": "^4.12.13", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw=="], + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-e9TZwwffgUFLgafwzyx4wNnxtgbipHG515xHmurkmjtuyJyCRkAn+Tbd4+iF/fnoCyeJXsZAzPX/OSo2nW9XOQ=="], - "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.54", "", { "dependencies": { "@smithy/config-resolver": "^4.4.17", "@smithy/credential-provider-imds": "^4.2.14", "@smithy/node-config-provider": "^4.3.14", "@smithy/property-provider": "^4.2.14", "@smithy/smithy-client": "^4.12.13", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw=="], + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-RsmNY73PM8iO8iRreeXxE3MwY/kRRvBlbJpfm3VKMJc6MQ7vN+LulWj+VrDh+Wf5jCLdQyP43OrTSEH8d/lpCA=="], - "@smithy/util-endpoints": ["@smithy/util-endpoints@3.4.2", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.14", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg=="], + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.5.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-3qGB71aqXTJnNqNlOh3R9u+9nVbR3qgd7BhtBj1Na/fgD/KAJ/+nkUHt/CGmWyEa+P1Jl361hRO2zwlMvuKJGA=="], - "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg=="], + "@smithy/util-middleware": ["@smithy/util-middleware@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-0GFlqDcWjnJT+TsAj91L4iTPvpR7VySjsALxtGROZaIfR03LLM69nmJDr3V/GjhZfyv/DWlFEnAWyaoKkmby1g=="], - "@smithy/util-middleware": ["@smithy/util-middleware@4.2.14", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw=="], + "@smithy/util-retry": ["@smithy/util-retry@4.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-CwOWVLoMWyeHxaFM9a6jpq47yFKx2awaa8oFzQ6e+c5qlIATVc8MX0aN2PGuTgCaTZw//BDgHSjdAFaSbdbJRg=="], - "@smithy/util-retry": ["@smithy/util-retry@4.3.4", "", { "dependencies": { "@smithy/service-error-classification": "^4.3.0", "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-FY1UQQ1VFmMwiYp1GVS4MeaGD5O0blLNYK0xCRHU+mJgeoH/hSY8Ld8sJWKQ6uznkh14HveRGQJncgPyNl9J+A=="], + "@smithy/util-stream": ["@smithy/util-stream@4.6.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-b5kyVsNM3pU36cJGyxwAQajGxs4JkNuaKKNufDu7oASWmzKG5gtXEdVK1rvz99O2JV1B85Pp9bAcN7fXWbN6Aw=="], - "@smithy/util-stream": ["@smithy/util-stream@4.5.25", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.17", "@smithy/node-http-handler": "^4.6.1", "@smithy/types": "^4.14.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA=="], + "@smithy/util-utf8": ["@smithy/util-utf8@4.3.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-Bswgu9zDwZRp5HBQKIaW6QrKrbwQ9RuOyAg2adsIjJTHA2SmE0XXBk+mYP82Ay6I3XzkY5lM7K9R0XZDFzJ3tw=="], - "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw=="], - - "@smithy/util-utf8": ["@smithy/util-utf8@4.2.2", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw=="], - - "@smithy/util-waiter": ["@smithy/util-waiter@4.2.16", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-GtclrKoZ3Lt7jPQ7aTIYKfjY92OgceScftVnkTsG8e1KV8rkvZgN+ny6YSRhd9hxB8rZtwVbmln7NTvE5O3GmQ=="], - - "@smithy/uuid": ["@smithy/uuid@1.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g=="], + "@smithy/util-waiter": ["@smithy/util-waiter@4.4.2", "", { "dependencies": { "@smithy/core": "^3.24.2", "tslib": "^2.6.2" } }, "sha512-2lAaVh9CAMVi0yI5nGX8nFeZed2v3CstXqX9sqYcIo2OjABSziqk/o/xekYsNHI4nwLzXeTazcm+eN7DpdSbLA=="], "@speed-highlight/core": ["@speed-highlight/core@1.2.15", "", {}, "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw=="], @@ -1983,19 +2012,19 @@ "@tanstack/devtools-event-client": ["@tanstack/devtools-event-client@0.4.3", "", { "bin": { "intent": "bin/intent.js" } }, "sha512-OZI6QyULw0FI0wjgmeYzCIfbgPsOEzwJtCpa69XrfLMtNXLGnz3d/dIabk7frg0TmHo+Ah49w5I4KC7Tufwsvw=="], - "@tanstack/form-core": ["@tanstack/form-core@1.29.1", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.1", "@tanstack/pacer-lite": "^0.1.1", "@tanstack/store": "^0.9.1" } }, "sha512-NIYPO36eEu7nSWvMpbFDQaBWyVtnH/C8fsZ3/XpJUT4uOWgmxsiUvHGbTbDNIQTXAKIkhwEl0sUrqBNn2SfUnw=="], + "@tanstack/form-core": ["@tanstack/form-core@1.32.0", "", { "dependencies": { "@tanstack/devtools-event-client": "^0.4.1", "@tanstack/pacer-lite": "^0.1.1", "@tanstack/store": "^0.9.1" } }, "sha512-Tn5VRDSjyqjmaet2tJMuEWDRFyrCaon03vxXPlSSaiSs6C/N7lCIwGCXJbZXEUq1kTj8jYN9qyXHbsz4LQHcow=="], "@tanstack/pacer-lite": ["@tanstack/pacer-lite@0.1.1", "", {}, "sha512-y/xtNPNt/YeyoVxE/JCx+T7yjEzpezmbb+toK8DDD1P4m7Kzs5YR956+7OKexG3f8aXgC3rLZl7b1V+yNUSy5w=="], - "@tanstack/query-core": ["@tanstack/query-core@5.100.1", "", {}, "sha512-awvQhOO/2TrSCHE5LKKsXcvvj6WSBncwEcMFCB/ez0Qs0b17iyyivoGArNV3HFfXryZwCpnb/olsaBBKrIbtSw=="], + "@tanstack/query-core": ["@tanstack/query-core@5.100.10", "", {}, "sha512-8UR0yJR+GiQ40m3lPhUr0xbfAupe6GSQiksSBSa9SM2NjezFyxXCIA69/lz8cSoNKZLrw1/PktIyQBJcVeMi3w=="], - "@tanstack/query-devtools": ["@tanstack/query-devtools@5.100.1", "", {}, "sha512-jZLV2l7XjYxXCrXHj9pj15gZuY8Te+idoSPS2hIh3+SxOd20Gn0rfUoqEw9vc+us/b16hi0/DWqpzx9O1ZsyIQ=="], + "@tanstack/query-devtools": ["@tanstack/query-devtools@5.100.10", "", {}, "sha512-3DmJf25hDPus5IpVvp6ujXv6bKV2zPzI9vpbAmpJigsL/H6DPvPjmf7/Q9yVKEke//8fgeQ45abjgnLuyYxAiw=="], - "@tanstack/react-form": ["@tanstack/react-form@1.29.1", "", { "dependencies": { "@tanstack/form-core": "1.29.1", "@tanstack/react-store": "^0.9.1" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-hVHk4g0phd0HxRsv2ry6Xt8BqmalT55Q3cokhJBCC1St0hcGZhgwJJbohm9atao45BPG9e55DGvtbwExqZe35g=="], + "@tanstack/react-form": ["@tanstack/react-form@1.32.0", "", { "dependencies": { "@tanstack/form-core": "1.32.0", "@tanstack/react-store": "^0.9.1" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-6WP5SQTA6/H9crCpvpq3ZppYWqtrdE5NjOy6ebABi6uAQPqhfTzrdjS9t40mCZCFtGI5585OhJV6zBP/KN2zcw=="], - "@tanstack/react-query": ["@tanstack/react-query@5.100.1", "", { "dependencies": { "@tanstack/query-core": "5.100.1" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-UgWRLhQKprC37SsO6y1zRabOqDmM2gsdTNPbqTT35yl7kOOhwXU4nyfOiGHXPwoEFJV1IpSk85hjIFjNFWVpzw=="], + "@tanstack/react-query": ["@tanstack/react-query@5.100.10", "", { "dependencies": { "@tanstack/query-core": "5.100.10" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-FLaZf2RCrA/Zgp4aiu5tG3TyasTRO7aZ99skxQpr3Hg/zXOhu6yq5FZCYQ/tRaJtM9ylnoK8tFK7PolXQadv6Q=="], - "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.100.1", "", { "dependencies": { "@tanstack/query-devtools": "5.100.1" }, "peerDependencies": { "@tanstack/react-query": "^5.100.1", "react": "^18 || ^19" } }, "sha512-JuLinBUl/BlZhm0WVX83fJgE2a3YSbuEdxf3fgP+THg92hX7YfwuH5DzT35a6sL/rifZsPr0yJ9itB6jDOcdRg=="], + "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.100.10", "", { "dependencies": { "@tanstack/query-devtools": "5.100.10" }, "peerDependencies": { "@tanstack/react-query": "^5.100.10", "react": "^18 || ^19" } }, "sha512-zes0+o9ef5rAZXJ9f/SeaLs2nufJaeVkZkl/Or9NGrWVF41kL9Od9ED9nCwtQlgiF2VGtrzhEw5AU/igAO+aAg=="], "@tanstack/react-store": ["@tanstack/react-store@0.9.3", "", { "dependencies": { "@tanstack/store": "0.9.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg=="], @@ -2007,7 +2036,7 @@ "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], @@ -2017,7 +2046,7 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], - "@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="], + "@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="], @@ -2077,7 +2106,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@25.6.0", "", { "dependencies": { "undici-types": "~7.19.0" } }, "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ=="], + "@types/node": ["@types/node@25.7.0", "", { "dependencies": { "undici-types": "~7.21.0" } }, "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg=="], "@types/nodemailer": ["@types/nodemailer@6.4.23", "", { "dependencies": { "@types/node": "*" } }, "sha512-aFV3/NsYFLSx9mbb5gtirBSXJnAlrusoKNuPbxsASWc7vrKLmIrTQRpdcxNcSFL3VW2A2XpeLEavwb2qMi6nlQ=="], @@ -2107,11 +2136,11 @@ "@typescript-eslint/parser": ["@typescript-eslint/parser@7.18.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "7.18.0", "@typescript-eslint/types": "7.18.0", "@typescript-eslint/typescript-estree": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^8.56.0" } }, "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg=="], - "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.0", "@typescript-eslint/types": "^8.59.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw=="], + "@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.59.3", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.59.3", "@typescript-eslint/types": "^8.59.3", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng=="], "@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@7.18.0", "", { "dependencies": { "@typescript-eslint/types": "7.18.0", "@typescript-eslint/visitor-keys": "7.18.0" } }, "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA=="], - "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg=="], + "@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.59.3", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw=="], "@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@7.18.0", "", { "dependencies": { "@typescript-eslint/typescript-estree": "7.18.0", "@typescript-eslint/utils": "7.18.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, "peerDependencies": { "eslint": "^8.56.0" } }, "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA=="], @@ -2123,7 +2152,7 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@7.18.0", "", { "dependencies": { "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" } }, "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], @@ -2167,9 +2196,9 @@ "agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], - "agents": ["agents@0.11.5", "", { "dependencies": { "@babel/plugin-proposal-decorators": "^7.29.0", "@cfworker/json-schema": "^4.1.1", "@modelcontextprotocol/sdk": "1.29.0", "@rolldown/plugin-babel": "^0.2.3", "cron-schedule": "^6.0.0", "mimetext": "^3.0.28", "nanoid": "^5.1.9", "partyserver": "^0.4.1", "partysocket": "1.1.18", "yargs": "^18.0.0" }, "peerDependencies": { "@cloudflare/ai-chat": ">=0.0.8 <1.0.0", "@cloudflare/codemode": ">=0.0.7 <1.0.0", "@tanstack/ai": ">=0.10.2 <1.0.0", "@x402/core": "^2.0.0", "@x402/evm": "^2.0.0", "ai": "^6.0.0", "react": "^19.0.0", "vite": ">=6.0.0 <9.0.0", "zod": "^4.0.0" }, "optionalPeers": ["@cloudflare/ai-chat", "@cloudflare/codemode", "@tanstack/ai", "@x402/core", "@x402/evm", "vite"], "bin": { "agents": "dist/cli/index.js" } }, "sha512-1wPkA7OOfEdR4GKwaBmqdnZkOxutN2mCsolVU4ekg5QxrTLnC9Vz9LyZPcGqV2ldyfpUY7R73AUqtig5iYRLvQ=="], + "agents": ["agents@0.11.9", "", { "dependencies": { "@babel/plugin-proposal-decorators": "^7.29.0", "@cfworker/json-schema": "^4.1.1", "@modelcontextprotocol/sdk": "1.29.0", "@rolldown/plugin-babel": "^0.2.3", "cron-schedule": "^6.0.0", "mimetext": "^3.0.28", "nanoid": "^5.1.9", "partyserver": "^0.5.5", "partysocket": "1.1.18", "yargs": "^18.0.0" }, "peerDependencies": { "@cloudflare/ai-chat": ">=0.5.2 <1.0.0", "@cloudflare/codemode": ">=0.3.4 <1.0.0", "@tanstack/ai": ">=0.10.2 <1.0.0", "@x402/core": "^2.0.0", "@x402/evm": "^2.0.0", "ai": "^6.0.0", "react": "^19.0.0", "vite": ">=6.0.0 <9.0.0", "zod": "^4.0.0" }, "optionalPeers": ["@cloudflare/ai-chat", "@cloudflare/codemode", "@tanstack/ai", "@x402/core", "@x402/evm", "vite"], "bin": { "agents": "dist/cli/index.js" } }, "sha512-La8kXl/zEr9tu17Xc5BXb5Xz5yfrH+Oh98nnWtj1OxteO1AB0i2R26w77pXCT0ffViLaE3RtgN2dOq8QGDTwsA=="], - "ai": ["ai@6.0.168", "", { "dependencies": { "@ai-sdk/gateway": "3.0.104", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-2HqCJuO+1V2aV7vfYs5LFEUfxbkGX+5oa54q/gCCTL7KLTdbxcCu5D7TdLA5kwsrs3Szgjah9q6D9tpjHM3hUQ=="], + "ai": ["ai@6.0.182", "", { "dependencies": { "@ai-sdk/gateway": "3.0.114", "@ai-sdk/provider": "3.0.10", "@ai-sdk/provider-utils": "4.0.27", "@opentelemetry/api": "^1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ooJdziFjYrYRcsCx107roqA8gDTI3P82nUfroNWIhVvwrkYzEN3W1l50YK+XNqkUew8AiimaW0/SLBewRXMuHQ=="], "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], @@ -2223,7 +2252,7 @@ "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - "ast-module-types": ["ast-module-types@6.0.1", "", {}, "sha512-WHw67kLXYbZuHTmcdbIrVArCq5wxo6NEuj3hiYAWr8mwJeC+C2mMCIBIWCiDoCye/OF/xelc+teJ1ERoWmnEIA=="], + "ast-module-types": ["ast-module-types@6.0.2", "", {}, "sha512-6KuK/7nZ/2Qh7sGuVEiwxjCxzTY2Pdb5mTo5z1e6/J8BA0tvjR7G8vQJKrQMTqwmnA3UPEyKIFX4YUS1DO1Hvw=="], "ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], @@ -2269,7 +2298,7 @@ "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], - "bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="], + "bare-events": ["bare-events@2.8.3", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw=="], "bare-fs": ["bare-fs@4.7.1", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-WDRsyVN52eAx/lBamKD6uyw8H4228h/x0sGGGegOamM2cd7Pag88GfMQalobXI+HaEUxpCkbKQUDOQqt9wawRw=="], @@ -2285,13 +2314,13 @@ "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], - "baseline-browser-mapping": ["baseline-browser-mapping@2.10.21", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Q+rUQ7Uz8AHM7DEaNdwvfFCTq7a43lNTzuS94eiWqwyxfV/wJv+oUivef51T91mmRY4d4A1u9rcSvkeufCVXlA=="], + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.29", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ=="], "basic-ftp": ["basic-ftp@5.3.1", "", {}, "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw=="], "bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="], - "better-auth": ["better-auth@1.6.9", "", { "dependencies": { "@better-auth/core": "1.6.9", "@better-auth/drizzle-adapter": "1.6.9", "@better-auth/kysely-adapter": "1.6.9", "@better-auth/memory-adapter": "1.6.9", "@better-auth/mongo-adapter": "1.6.9", "@better-auth/prisma-adapter": "1.6.9", "@better-auth/telemetry": "1.6.9", "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.1.1", "@noble/hashes": "^2.0.1", "better-call": "1.3.5", "defu": "^6.1.4", "jose": "^6.1.3", "kysely": "^0.28.14", "nanostores": "^1.1.1", "zod": "^4.3.6" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": "^0.45.2", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-EBFURtglyiEZxbx4NJBoqUD8J65dX24yC+6I9AUbIXNgUkt76mshzGbHkxZ3n/lB7Dwq3kBC+hHt0hUQsnL7HA=="], + "better-auth": ["better-auth@1.6.11", "", { "dependencies": { "@better-auth/core": "1.6.11", "@better-auth/drizzle-adapter": "1.6.11", "@better-auth/kysely-adapter": "1.6.11", "@better-auth/memory-adapter": "1.6.11", "@better-auth/mongo-adapter": "1.6.11", "@better-auth/prisma-adapter": "1.6.11", "@better-auth/telemetry": "1.6.11", "@better-auth/utils": "0.4.0", "@better-fetch/fetch": "1.1.21", "@noble/ciphers": "^2.1.1", "@noble/hashes": "^2.0.1", "better-call": "1.3.5", "defu": "^6.1.4", "jose": "^6.1.3", "kysely": "^0.28.17", "nanostores": "^1.1.1", "zod": "^4.3.6" }, "peerDependencies": { "@lynx-js/react": "*", "@prisma/client": "^5.0.0 || ^6.0.0 || ^7.0.0", "@sveltejs/kit": "^2.0.0", "@tanstack/react-start": "^1.0.0", "@tanstack/solid-start": "^1.0.0", "better-sqlite3": "^12.0.0", "drizzle-kit": ">=0.31.4", "drizzle-orm": "^0.45.2", "mongodb": "^6.0.0 || ^7.0.0", "mysql2": "^3.0.0", "next": "^14.0.0 || ^15.0.0 || ^16.0.0", "pg": "^8.0.0", "prisma": "^5.0.0 || ^6.0.0 || ^7.0.0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "solid-js": "^1.0.0", "svelte": "^4.0.0 || ^5.0.0", "vitest": "^2.0.0 || ^3.0.0 || ^4.0.0", "vue": "^3.0.0" }, "optionalPeers": ["@lynx-js/react", "@prisma/client", "@sveltejs/kit", "@tanstack/react-start", "@tanstack/solid-start", "better-sqlite3", "drizzle-kit", "drizzle-orm", "mongodb", "mysql2", "next", "pg", "prisma", "react", "react-dom", "solid-js", "svelte", "vitest", "vue"] }, "sha512-Wwt6+q07dwIhsp6XiM7L1qSXVUWBEtNl+eZvwM778CguFqDZFBN9Pt6LtFaHl55t8Z+Zc//5kxcbgDY8/79vFQ=="], "better-auth-cloudflare": ["better-auth-cloudflare@0.3.0", "", { "dependencies": { "drizzle-orm": "^0.45.0", "mime": "^4.1.0", "zod": "^4.3.0" }, "peerDependencies": { "@better-auth/drizzle-adapter": "^1.5.0", "@cloudflare/workers-types": "^4.0.0", "better-auth": "^1.5.0" } }, "sha512-u0TrMbFhHNL2IFzkCbCQYyA/beeBSivdL+vfrNywYnsVrQO1qT5CC/yKhnRdrkwXLeNi9tCeoSwWoygTMSl0Yg=="], @@ -2319,7 +2348,7 @@ "bplist-parser": ["bplist-parser@0.3.2", "", { "dependencies": { "big-integer": "1.6.x" } }, "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ=="], - "brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], + "brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -2335,7 +2364,7 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], - "bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="], + "bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], "burnt": ["burnt@0.13.0", "", { "dependencies": { "sf-symbols-typescript": "^1.0.0", "sonner": "^2.0.1" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-LjlQa7CLkGWUdz08YUIaGCJ8BLXib31/ztKqowgwqd7UH283A/kmdCj+1PYAQwDQEMPNmvSUfFHrjXbcwZibFQ=="], @@ -2359,7 +2388,7 @@ "camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001790", "", {}, "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001792", "", {}, "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -2589,7 +2618,7 @@ "detective-vue2": ["detective-vue2@2.3.0", "", { "dependencies": { "@dependents/detective-less": "^5.0.1", "@vue/compiler-sfc": "^3.5.32", "detective-es6": "^5.0.1", "detective-sass": "^6.0.1", "detective-scss": "^5.0.1", "detective-stylus": "^5.0.1", "detective-typescript": "^14.1.0" }, "peerDependencies": { "typescript": "^5.4.4 || ^6.0.2" } }, "sha512-3gwbZPqVTm9sL9XdZsgEJ7x4x99O853VVZHapQAiEkGuMJMpFPjHDrecSgfqnS5JW3FJfYXesLZGvUOibjn49g=="], - "devalue": ["devalue@5.7.1", "", {}, "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA=="], + "devalue": ["devalue@5.8.0", "", {}, "sha512-2zA9pFEsnp7vWBZbXF5JAgAq0fsUIt/1XPbRiAmRV3lp/2C3upzH+sADiyy66aFCihoLEsrQHxNM5w1gIDfsBg=="], "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], @@ -2631,7 +2660,7 @@ "effector": ["effector@23.4.4", "", {}, "sha512-QkZboRN28K/iwxigDhlJcI3ux3aNbt8kYGGH/GkqWG0OlGeyuBhb7PdM89Iu+ogV8Lmz16xIlwnXR2UNWI6psg=="], - "electron-to-chromium": ["electron-to-chromium@1.5.344", "", {}, "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg=="], + "electron-to-chromium": ["electron-to-chromium@1.5.355", "", {}, "sha512-LUPZhKzZPYSPme1jEYohpkA+ybYCJztr1quAdBd7E7h3+VOBVcKkwwtBJu41nrjawrRzfb8mtMfzWozoaK0ZIQ=="], "elysia": ["elysia@1.4.28", "", { "dependencies": { "cookie": "^1.1.1", "exact-mirror": "^0.2.7", "fast-decode-uri-component": "^1.0.1", "memoirist": "^0.4.0" }, "peerDependencies": { "@sinclair/typebox": ">= 0.34.0 < 1", "@types/bun": ">= 1.2.0", "file-type": ">= 20.0.0", "openapi-types": ">= 12.0.0", "typescript": ">= 5.0.0" }, "optionalPeers": ["@types/bun", "typescript"] }, "sha512-Vrx8sBnvq8squS/3yNBzR1jBXI+SgmnmvwawPjNuEHndUe5l1jV2Gp6JJ4ulDkEB8On6bWmmuyPpA+bq4t+WYg=="], @@ -2649,7 +2678,7 @@ "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - "enhanced-resolve": ["enhanced-resolve@5.21.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-otxSQPw4lkOZWkHpB3zaEQs6gWYEsmX4xQF68ElXC/TWvGxGMSGOvoNbaLXm6/cS/fSfHtsEdw90y20PCd+sCA=="], + "enhanced-resolve": ["enhanced-resolve@5.21.3", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q=="], "enquirer": ["enquirer@2.4.1", "", { "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" } }, "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ=="], @@ -2681,7 +2710,7 @@ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - "es-toolkit": ["es-toolkit@1.46.0", "", {}, "sha512-IToJ6ct9OLl5zz6WsC/1vZEwfSZ7Myil+ygl5Tf30Xjn9AEkzNB4kqp2G7VUJKF1DtTx/ra5M5KLlXvzOg51BA=="], + "es-toolkit": ["es-toolkit@1.46.1", "", {}, "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ=="], "esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], @@ -2699,7 +2728,7 @@ "eslint-config-prettier": ["eslint-config-prettier@9.1.2", "", { "peerDependencies": { "eslint": ">=7.0.0" }, "bin": { "eslint-config-prettier": "bin/cli.js" } }, "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ=="], - "eslint-config-universe": ["eslint-config-universe@15.0.3", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "^8.29.1", "@typescript-eslint/parser": "^8.29.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-n": "^17.17.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", "globals": "^16.0.0" }, "peerDependencies": { "eslint": ">=8.10", "prettier": ">=3" }, "optionalPeers": ["prettier"] }, "sha512-fUMsNXp7GJBu7Sz9PXFBbXhkiixdQ5sbnViFIBbk6ORAfeokczJ+eVv5HQ2gwxPQdbfJarpkO9WZDtxIvJnEGw=="], + "eslint-config-universe": ["eslint-config-universe@15.0.4", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "^8.29.1", "@typescript-eslint/parser": "^8.29.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-import": "^2.31.0", "eslint-plugin-n": "^17.17.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", "globals": "^16.0.0" }, "peerDependencies": { "eslint": ">=8.10", "prettier": ">=3" }, "optionalPeers": ["prettier"] }, "sha512-7XTb/JTLzntJTUHXnR7ADl78kzRpQLm75NOjx1kYFnEMArJk69mDJ96WREzttro4/TOlQ9paGL+WFsRXk1vLkw=="], "eslint-import-resolver-node": ["eslint-import-resolver-node@0.3.10", "", { "dependencies": { "debug": "^3.2.7", "is-core-module": "^2.16.1", "resolve": "^2.0.0-next.6" } }, "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ=="], @@ -2761,7 +2790,7 @@ "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], - "expo": ["expo@55.0.23", "", { "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "55.0.29", "@expo/config": "~55.0.16", "@expo/config-plugins": "~55.0.8", "@expo/devtools": "55.0.3", "@expo/fingerprint": "0.16.7", "@expo/local-build-cache-provider": "55.0.12", "@expo/log-box": "55.0.12", "@expo/metro": "~55.1.1", "@expo/metro-config": "55.0.20", "@expo/vector-icons": "^15.0.2", "@ungap/structured-clone": "^1.3.0", "babel-preset-expo": "~55.0.21", "expo-asset": "~55.0.17", "expo-constants": "~55.0.16", "expo-file-system": "~55.0.19", "expo-font": "~55.0.7", "expo-keep-awake": "~55.0.8", "expo-modules-autolinking": "55.0.21", "expo-modules-core": "55.0.25", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.1" }, "peerDependencies": { "@expo/dom-webview": "*", "@expo/metro-runtime": "*", "react": "*", "react-native": "*", "react-native-webview": "*" }, "optionalPeers": ["@expo/dom-webview", "@expo/metro-runtime", "react-native-webview"], "bin": { "expo": "bin/cli", "fingerprint": "bin/fingerprint", "expo-modules-autolinking": "bin/autolinking" } }, "sha512-b+lKwfzJzFiSm9G0wVGWw3c2YoZyubbl9gHOF1ZFuK8FqtxSge8pDDJMuEFmTi14dbKwh/tirB7MiORq54r7CQ=="], + "expo": ["expo@55.0.24", "", { "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "55.0.30", "@expo/config": "~55.0.17", "@expo/config-plugins": "~55.0.9", "@expo/devtools": "55.0.3", "@expo/fingerprint": "0.16.7", "@expo/local-build-cache-provider": "55.0.13", "@expo/log-box": "55.0.12", "@expo/metro": "~55.1.1", "@expo/metro-config": "55.0.21", "@expo/vector-icons": "^15.0.2", "@ungap/structured-clone": "^1.3.0", "babel-preset-expo": "~55.0.21", "expo-asset": "~55.0.17", "expo-constants": "~55.0.16", "expo-file-system": "~55.0.20", "expo-font": "~55.0.7", "expo-keep-awake": "~55.0.8", "expo-modules-autolinking": "55.0.22", "expo-modules-core": "55.0.25", "pretty-format": "^29.7.0", "react-refresh": "^0.14.2", "whatwg-url-minimum": "^0.1.2" }, "peerDependencies": { "@expo/dom-webview": "*", "@expo/metro-runtime": "*", "react": "*", "react-native": "*", "react-native-webview": "*" }, "optionalPeers": ["@expo/dom-webview", "@expo/metro-runtime", "react-native-webview"], "bin": { "expo": "bin/cli", "fingerprint": "bin/fingerprint", "expo-modules-autolinking": "bin/autolinking" } }, "sha512-nU95y+GIfD1dm9CSjsitDdltSU83dDqemxD1UUBxJPH8zKf7B5AdGVNyE6/jLWyCM/p/EmHfCeiqdrWCy9ljZA=="], "expo-apple-authentication": ["expo-apple-authentication@55.0.13", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-Qvh3DmhXqhtWOe7BC9e7UVApR3XS1qE7+68tVLqb3KI/sET7QV9KT5JgOJogWmmCJVxA/kaot0M136yvW1pdWA=="], @@ -2773,19 +2802,19 @@ "expo-constants": ["expo-constants@55.0.16", "", { "dependencies": { "@expo/env": "~2.1.2" }, "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-Z15/No94UHoogD+pulxjudGAeOHTEIWZgb/vnX48Wx5D+apWTeCbnKxQZZtGQlosvduYL5kaic2/W8U+NHfBQQ=="], - "expo-dev-client": ["expo-dev-client@55.0.32", "", { "dependencies": { "expo-dev-launcher": "55.0.33", "expo-dev-menu": "55.0.27", "expo-dev-menu-interface": "55.0.2", "expo-manifests": "~55.0.16", "expo-updates-interface": "~55.1.6" }, "peerDependencies": { "expo": "*" } }, "sha512-rfZ0Xpgbw3RPymkivvLSQ2Koqefj+oVOReqNLN3JXDlqdC2jOr3MCqfTaJs5VFNzFKk7pOPyE60jh03UdvsHCQ=="], + "expo-dev-client": ["expo-dev-client@55.0.33", "", { "dependencies": { "expo-dev-launcher": "55.0.34", "expo-dev-menu": "55.0.28", "expo-dev-menu-interface": "55.0.2", "expo-manifests": "~55.0.17", "expo-updates-interface": "~55.1.6" }, "peerDependencies": { "expo": "*" } }, "sha512-tuwVlHEI1taWLtlNflkW6IX4OaY3fxgQQi+4sxIcn8UErBS1ip6EJuBmW/eBPBS67ZoKQhzLqg9W2a+WsJYfsg=="], - "expo-dev-launcher": ["expo-dev-launcher@55.0.33", "", { "dependencies": { "@expo/schema-utils": "^55.0.3", "expo-dev-menu": "55.0.27", "expo-manifests": "~55.0.16" }, "peerDependencies": { "expo": "*" } }, "sha512-WZsTtyEVgCBMj3vlgbDSKbYbUbAwijNhJY9jBqqlmbPLHtLE+Wc6nCTafb0dWY6+Si+afF98lvPyz6WSAu59uA=="], + "expo-dev-launcher": ["expo-dev-launcher@55.0.34", "", { "dependencies": { "@expo/schema-utils": "^55.0.4", "expo-dev-menu": "55.0.28", "expo-manifests": "~55.0.17" }, "peerDependencies": { "expo": "*" } }, "sha512-xEVD5zIX2gMlxlRZfU0b6rzhBd1eeA/IpX1ut1UoPa7G0Gukc+7j240t6cm9sDvPCErmAP+x+Z142UeueXj+1Q=="], - "expo-dev-menu": ["expo-dev-menu@55.0.27", "", { "dependencies": { "expo-dev-menu-interface": "55.0.2" }, "peerDependencies": { "expo": "*" } }, "sha512-Il+kkIXlPDfZ/Z3ZquV1r5niECEByJObUMkB24c0B4N4693f0SDoKyyaRqcGRsRCVXW9r0eAoTeEnXl1revQdA=="], + "expo-dev-menu": ["expo-dev-menu@55.0.28", "", { "dependencies": { "expo-dev-menu-interface": "55.0.2" }, "peerDependencies": { "expo": "*" } }, "sha512-JsysTyPlsMuholX3e/QB6SQ9JlEfsFPy9KZCVItapCwyJQjFboISqM3mXiyP8CF2OIUudOwywxOrYU6ktAl8Vg=="], "expo-dev-menu-interface": ["expo-dev-menu-interface@55.0.2", "", { "peerDependencies": { "expo": "*" } }, "sha512-DomUNvGzY/xliwnMdbAYY780sCv19N7zIbifc0ClcoCzJZpNSCkvJ2qGIFRPyM/7DmqmlHGCKi8di7kYYLKNEg=="], - "expo-device": ["expo-device@55.0.16", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-o6eQjO2reoniXpos0FnPcrAVMYUfFPcIUdMRUUpKwQys7cmTJBjJLbOo+SuctVXUrsHUm6zyoKI7nX3C3lpqJw=="], + "expo-device": ["expo-device@55.0.17", "", { "dependencies": { "ua-parser-js": "^0.7.33" }, "peerDependencies": { "expo": "*" } }, "sha512-ZcMrSeD0zWooosm5Bet5qluxUrhw+NPgcMmN6ySVF7cm4K8Bvh4KPogJePbI1qfhFAiJWcWeV9e/2uewb9ktfw=="], "expo-eas-client": ["expo-eas-client@55.0.5", "", {}, "sha512-wRagCeSbSnSGVXgP7V+qiGfXzZ9hTVKWvKIOP7lwrX3MIEenNmNlO4D3RVC3aNU2GhmO3ZCZIIEre80KZoUUHA=="], - "expo-file-system": ["expo-file-system@55.0.19", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-c4smCbMqELLI3YQrGpw21MwZIREXM2e53vQD/+KWQcae1q+hgw8J2TroEqcQ/jVOtFpZYVvyVfgu4HDKNEKmNw=="], + "expo-file-system": ["expo-file-system@55.0.20", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-sBCHhNlCT3EiqCcE6xSbyvOLUAlKx7+p0qjo+c+UPyC/gMrXUdva99g25uptM+fEMwy2co25MUQQ0U0guQLOQA=="], "expo-font": ["expo-font@55.0.7", "", { "dependencies": { "fontfaceobserver": "^2.1.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-oH39Xb+3i6Y69b7YRP+P+5WLx7621t+ep/RAgLwJJYpTjs7CnSohUG+873rEtqsTAuQGi63ms7x9ZeHj1E9LYw=="], @@ -2803,53 +2832,53 @@ "expo-keep-awake": ["expo-keep-awake@55.0.8", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-PfIpMfM+STOBwkR5XOE+yVtER86c44MD+W8QD8JxuO0sT9pF7Y1SJYakWlpvX8xsGA+bjKLxftm9403s9kQhKA=="], - "expo-linear-gradient": ["expo-linear-gradient@55.0.13", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-Qz2T4jpkA15RIk29DBqI1TwW+8O9AN8MyC4TJPbh/5UnihH0yNNz3waplUO8Szh5OZ3czTGvtPQU4ysF3RDxwQ=="], + "expo-linear-gradient": ["expo-linear-gradient@55.0.14", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-n01A9P0ZebRo8Rm4QHYEjMR8z4Y6MBc8uVnT8NB9cOJislvEmz2o2WQJoK6RD7FjoeFEW8KkRtggK7fQ3h7KIg=="], "expo-linking": ["expo-linking@55.0.15", "", { "dependencies": { "expo-constants": "~55.0.16", "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-/RQh2vkNqV8Bim9Owm/evVqn2fqTvCDYHkpYPoSKbLAdydSGdHC2xZNw7Odl4wu1i1/3L4Xz//LKd3NsPWYWBQ=="], - "expo-localization": ["expo-localization@55.0.13", "", { "dependencies": { "rtl-detect": "^1.0.2" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-fXiEUUihIrXmAEzoneaTOFcQ7TKmr25RR/ymrB/MvYTVnmevFA1zY2KI0VSiXY+NKKjZ8mG65YSn1wh4gEYKxA=="], + "expo-localization": ["expo-localization@55.0.14", "", { "dependencies": { "rtl-detect": "^1.0.2" }, "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-Q7VeW5gs0qMunYxIDB8+SpY/4T/h3CUE2kl6r6jnbYc6MPpmrK9bx/D9MeCfh0LmXW8oefy3MJYZQdPciEXU7Q=="], - "expo-location": ["expo-location@55.1.9", "", { "dependencies": { "@expo/image-utils": "^0.8.13" }, "peerDependencies": { "expo": "*" } }, "sha512-PIH9/qeyhtGh190FyIJNZYHXZOoi42SbVHY9IoTMBmqWLHf1BJyGhPpFlaLBSCjxObqfVZmrWsN5dtjueSwYQA=="], + "expo-location": ["expo-location@55.1.10", "", { "dependencies": { "@expo/image-utils": "^0.8.14" }, "peerDependencies": { "expo": "*" } }, "sha512-MkcFucsZ567Bn8ChElVTYVbOs2QXn27IKaBrVKogw7ZcbooImdj3L/UR6E7s3LkgF33YubKynAp9Opvixdwl7g=="], - "expo-manifests": ["expo-manifests@55.0.16", "", { "dependencies": { "expo-json-utils": "~55.0.2" }, "peerDependencies": { "expo": "*" } }, "sha512-BR9BPcNsSnCKlQ/d7ECywr+2T54+bTSr26HjRjSua949o4mO/iPIrLjK0lOAa1oIczju6a6oUFckZD2OljxP0g=="], + "expo-manifests": ["expo-manifests@55.0.17", "", { "dependencies": { "expo-json-utils": "~55.0.2" }, "peerDependencies": { "expo": "*" } }, "sha512-vKZvFivX3usVJKfBODKQcFHso0g38zlGbRGqGAppz+il0zKvG6umpJ47OZbzLod7iJpjd+ZDD2AGuOxacixonA=="], - "expo-modules-autolinking": ["expo-modules-autolinking@55.0.21", "", { "dependencies": { "@expo/require-utils": "^55.0.5", "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", "commander": "^7.2.0" }, "bin": { "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, "sha512-P9KsJgOwI7JVwxmGfRvcXkXO4LNRvHRdWmb4ukLmX15G/vZ7b6SM17yiYkPceWq1F5KeeZ11KFjEcl0y17xy7w=="], + "expo-modules-autolinking": ["expo-modules-autolinking@55.0.22", "", { "dependencies": { "@expo/require-utils": "^55.0.5", "@expo/spawn-async": "^1.7.2", "chalk": "^4.1.0", "commander": "^7.2.0" }, "bin": { "expo-modules-autolinking": "bin/expo-modules-autolinking.js" } }, "sha512-13x32V0HMHJDjND4K/gU2lQIZNxYn5S5rFzujqHmnXvOO6WGrVVELpk/0p5FmBfeuQ7GGFsATbhazQk+FeukUw=="], "expo-modules-core": ["expo-modules-core@55.0.25", "", { "dependencies": { "invariant": "^2.2.4" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-worklets": "^0.7.4 || ^0.8.0" }, "optionalPeers": ["react-native-worklets"] }, "sha512-yXpfg7aHLbuqoXocK34Vua6Aey5SCyqLygAsXAMbul9P8vfBjLpaOPiTJ5cLVF7Drfq8ownqVJO6qpGEtZ6GOw=="], - "expo-navigation-bar": ["expo-navigation-bar@55.0.12", "", { "dependencies": { "debug": "^4.3.2", "react-native-is-edge-to-edge": "^1.2.1" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-G7olnyAqGd7I3hLFAgP4WdcZFMD9pV6UY79P7EHyRdMuRZrYJfDdwcelyYB2+tekOdQEktZ3WlLVK+uS7f7TYw=="], + "expo-navigation-bar": ["expo-navigation-bar@55.0.13", "", { "dependencies": { "debug": "^4.3.2", "react-native-is-edge-to-edge": "^1.2.1" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-etU3o7+IqyX5tp+X6+UT5OqrQXIWpSiomv8rZmHTIAL8+AviKFELAgw1TaOddrRNfw849nM6UqGWAZnoIQxhMQ=="], - "expo-network": ["expo-network@55.0.13", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-7u+npCmCPRpVrjkUlQtUetPnTN1gRyj7z13bBM5w9w1AHMb4PfoxtIys5EB9ukzNYBg/gaZ/y5dtxomGpc6BKw=="], + "expo-network": ["expo-network@55.0.14", "", { "peerDependencies": { "expo": "*", "react": "*" } }, "sha512-Sy544zTPjVh+tbOLUOU8fBX87oRSrNQqUZY6TLO0w0WF/QTNb7yxlwRh6v6wfKKRg9xpZypTIIEtdG/s6q8ZQA=="], "expo-router": ["expo-router@55.0.14", "", { "dependencies": { "@expo/metro-runtime": "^55.0.11", "@expo/schema-utils": "^55.0.4", "@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-tabs": "^1.1.12", "@react-navigation/bottom-tabs": "^7.15.5", "@react-navigation/native": "^7.1.33", "@react-navigation/native-stack": "^7.14.5", "client-only": "^0.0.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", "expo-glass-effect": "^55.0.11", "expo-image": "^55.0.10", "expo-server": "^55.0.9", "expo-symbols": "^55.0.8", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.8", "query-string": "^7.1.3", "react-fast-compare": "^3.2.2", "react-native-is-edge-to-edge": "^1.2.1", "semver": "~7.6.3", "server-only": "^0.0.1", "sf-symbols-typescript": "^2.1.0", "shallowequal": "^1.1.0", "use-latest-callback": "^0.2.1", "vaul": "^1.1.2" }, "peerDependencies": { "@expo/log-box": "55.0.12", "@react-navigation/drawer": "^7.9.4", "@testing-library/react-native": ">= 13.2.0", "expo": "*", "expo-constants": "^55.0.16", "expo-linking": "^55.0.15", "react": "*", "react-dom": "*", "react-native": "*", "react-native-gesture-handler": "*", "react-native-reanimated": "*", "react-native-safe-area-context": ">= 5.4.0", "react-native-screens": "*", "react-native-web": "*", "react-server-dom-webpack": "~19.0.4 || ~19.1.5 || ~19.2.4" }, "optionalPeers": ["@react-navigation/drawer", "@testing-library/react-native", "react-dom", "react-native-gesture-handler", "react-native-reanimated", "react-native-web", "react-server-dom-webpack"] }, "sha512-rOn/wosp2hAPM+O2o41hnarbP5Zqv9UkHWa31KoSoiOme1tpmZd2yc93XtRAtzP0P5E5xzqq7a2rbEAarpP5XA=="], - "expo-secure-store": ["expo-secure-store@55.0.13", "", { "peerDependencies": { "expo": "*" } }, "sha512-I6r0JNO1Fd4o0Gu7Ixiic7s89lqgdUHq17uBH9y1f/AntoyKn71TdtYJH82RgfsBbu5qNVzrwImmvlANyOlITQ=="], + "expo-secure-store": ["expo-secure-store@55.0.14", "", { "peerDependencies": { "expo": "*" } }, "sha512-OKp9pDiTa4kgChop8+pTRJGBPhkJUcAxP5c6JbivNr4bmx3I+gKmAj1ov4KOXkY95TpWdHO+GQ4+0BgSY2P3JQ=="], "expo-server": ["expo-server@55.0.9", "", {}, "sha512-N5Ipn1NwqaJzEm+G97o0Jbe4g/th3R/16N1DabnYryXKCiZwDkK13/w3VfGkQN9LOOaBP+JIRxGf4M8lQKPzyA=="], - "expo-sqlite": ["expo-sqlite@55.0.15", "", { "dependencies": { "await-lock": "^2.2.2" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-vxE5fs6l953QSIyievQ8TuSstj62eC7zUREjNzbUOwRWaHGGnhnlPJM1HLoTIv+oIt3+b1m7k2fmcDGkpK5t3w=="], + "expo-sqlite": ["expo-sqlite@55.0.16", "", { "dependencies": { "await-lock": "^2.2.2" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-v6EIL4ygqWt/+ZfI76jIIv+IIaU8PnWPNjkmIN95vEQgh0FrWqzwssqe5ffQmm79kIfqIPTtAgTdl8MuZv88gg=="], "expo-status-bar": ["expo-status-bar@55.0.6", "", { "dependencies": { "react-native-is-edge-to-edge": "^1.2.1" }, "peerDependencies": { "react": "*", "react-native": "*" } }, "sha512-ijOUptfdiqYt7rObZ6jrPQ8sE5YN/8MxKCIJx0b7TY4nGkSJxhPIxeoW4GXcXCA8mTQ9PiOHH/ThLZgRVZvUlQ=="], - "expo-store-review": ["expo-store-review@55.0.13", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-3cIfDUOBArAeuDQEiYToTZdB1UGSHSe4NhLunEf7hYG86ICgSYIXcosisYnsMLTE6GxL/0XJ34sQOfrP7HfASA=="], + "expo-store-review": ["expo-store-review@55.0.14", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-1cUGDgu7nrOKqr1Zs5jwPxCkv3l0TbdavftYAjambPNPxl9+AYOG1JQ95js3WavFOD3+Zceuq6NPwAdU/nza1Q=="], "expo-structured-headers": ["expo-structured-headers@55.0.2", "", {}, "sha512-KITovrWigTOtsII5hRQ9/3ydaNcxCux5g6O+eTPLyjnye9dpkDKl5GmCLVPVKIL/d7253OtbGtWMD4m0gha5pw=="], "expo-symbols": ["expo-symbols@55.0.8", "", { "dependencies": { "@expo-google-fonts/material-symbols": "^0.4.1", "sf-symbols-typescript": "^2.0.0" }, "peerDependencies": { "expo": "*", "expo-font": "*", "react": "*", "react-native": "*" } }, "sha512-Dg6BTu+fCWukdlh+3XYIr6NbqJWmK4aAQ6i6BInKnWU0ALuzVUJcMDq8Lk9bHok2hOh3OhzJqlCqEoBXPInIVQ=="], - "expo-system-ui": ["expo-system-ui@55.0.17", "", { "dependencies": { "@react-native/normalize-colors": "0.83.6", "debug": "^4.3.2" }, "peerDependencies": { "expo": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-sCrQbp1VyMe63c7y7/luz88P9Ro3/jeUBXby2uYk0wHtkawUzBK9V69J3HTC4rI5eXiJMJPF2oCKO71c/7wtTg=="], + "expo-system-ui": ["expo-system-ui@55.0.18", "", { "dependencies": { "@react-native/normalize-colors": "0.83.6", "debug": "^4.3.2" }, "peerDependencies": { "expo": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native-web"] }, "sha512-Fbc0HJgqMpABeA/gI7NJFnSXwUeLrEMjjXq8Nl+4gTXyacIK2iOOrzCkvq41rKBBde0CR6kVnB1DXj0j9ZYnjg=="], - "expo-updates": ["expo-updates@55.0.21", "", { "dependencies": { "@expo/code-signing-certificates": "^0.0.6", "@expo/plist": "^0.5.2", "@expo/spawn-async": "^1.7.2", "arg": "^4.1.0", "chalk": "^4.1.2", "debug": "^4.3.4", "expo-eas-client": "~55.0.5", "expo-manifests": "~55.0.16", "expo-structured-headers": "~55.0.2", "expo-updates-interface": "~55.1.6", "getenv": "^2.0.0", "glob": "^13.0.0", "ignore": "^5.3.1", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" }, "bin": { "expo-updates": "bin/cli.js" } }, "sha512-wpWQAqNeBw1LLjqSK85/P9aHB+2R0nuuFPHb8ZRPRMJLhRUIk7IF0FaOdEy2NbiRJvrnGfRW3SK4NVQqrT8ULQ=="], + "expo-updates": ["expo-updates@55.0.22", "", { "dependencies": { "@expo/code-signing-certificates": "^0.0.6", "@expo/plist": "^0.5.3", "@expo/spawn-async": "^1.7.2", "arg": "^4.1.0", "chalk": "^4.1.2", "debug": "^4.3.4", "expo-eas-client": "~55.0.5", "expo-manifests": "~55.0.17", "expo-structured-headers": "~55.0.2", "expo-updates-interface": "~55.1.6", "getenv": "^2.0.0", "glob": "^13.0.0", "ignore": "^5.3.1", "resolve-from": "^5.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" }, "bin": { "expo-updates": "bin/cli.js" } }, "sha512-xLprYCwHYLrH+rtI5yMHWWScv6vMRRRpc+JHGjkLTeaFKHt1Lo1Kk7RUSOgSd61uiWX3yvI9mLRypdJbRvD5Mw=="], "expo-updates-interface": ["expo-updates-interface@55.1.6", "", { "peerDependencies": { "expo": "*" } }, "sha512-evxNpagCkjT3lE6bGV570TFzRtKuIuLY8I37RYHoriXCJ+ZKCN1hbmklK29uAixya+BxGpeTI2K4FqYeJLvfrw=="], - "expo-web-browser": ["expo-web-browser@55.0.15", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-6hwZQob3EF+RWwZ+IvWLZjj2wI1frqx21+m/uzBqdUEHUhp2cVJi7kmxDolDmrve+ZldryZi1qfN78ALdvjHSA=="], + "expo-web-browser": ["expo-web-browser@55.0.16", "", { "peerDependencies": { "expo": "*", "react-native": "*" } }, "sha512-eeGs3439ewO/Q56Pzg3qbAVZSE0oH/R7XW9VCXI59k0m78ZIYbBtPT4PMFL/+sBgRkXm546Lq/DFcJQPTOfXJg=="], "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], - "express-rate-limit": ["express-rate-limit@8.4.1", "", { "dependencies": { "ip-address": "10.1.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw=="], + "express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="], "exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="], @@ -2879,7 +2908,7 @@ "fast-sha256": ["fast-sha256@1.3.0", "", {}, "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ=="], - "fast-uri": ["fast-uri@3.1.0", "", {}, "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA=="], + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], "fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="], @@ -2941,7 +2970,7 @@ "fs": ["fs@0.0.1-security", "", {}, "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="], - "fs-extra": ["fs-extra@11.3.4", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA=="], + "fs-extra": ["fs-extra@11.3.5", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -2965,7 +2994,7 @@ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="], + "get-east-asian-width": ["get-east-asian-width@1.6.0", "", {}, "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA=="], "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], @@ -3047,7 +3076,7 @@ "hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="], - "hono": ["hono@4.12.15", "", {}, "sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg=="], + "hono": ["hono@4.12.18", "", {}, "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ=="], "hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], @@ -3111,7 +3140,7 @@ "invariant": ["invariant@2.2.4", "", { "dependencies": { "loose-envify": "^1.0.0" } }, "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA=="], - "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], @@ -3129,7 +3158,7 @@ "is-callable": ["is-callable@1.2.7", "", {}, "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA=="], - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], "is-data-view": ["is-data-view@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "is-typed-array": "^1.1.13" } }, "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw=="], @@ -3239,7 +3268,7 @@ "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], - "jotai": ["jotai@2.19.1", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-sqm9lVZiqBHZH8aSRk32DSiZDHY3yUIlulXYn9GQj7/LvoUdYXSMti7ZPJGo+6zjzKFt5a25k/I6iBCi43PJcw=="], + "jotai": ["jotai@2.20.0", "", { "peerDependencies": { "@babel/core": ">=7.0.0", "@babel/template": ">=7.0.0", "@types/react": ">=17.0.0", "react": ">=17.0.0" }, "optionalPeers": ["@babel/core", "@babel/template", "@types/react", "react"] }, "sha512-b5GAqgmXmXzB4WPaTH26ppk9Sl7AA9WSQX7yfdM+gJ1rFROiWcVbi97gFuN/yVCojOcbcvop2sfLL+fjxW0JVg=="], "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="], @@ -3289,7 +3318,7 @@ "ky": ["ky@1.14.3", "", {}, "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw=="], - "kysely": ["kysely@0.28.16", "", {}, "sha512-3i5pmOiZvMDj00qhrIVbH0AnioVTx22DMP7Vn5At4yJO46iy+FM8Y/g61ltenLVSo3fiO8h8Q3QOFgf/gQ72ww=="], + "kysely": ["kysely@0.28.17", "", {}, "sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q=="], "lan-network": ["lan-network@0.2.1", "", { "bin": { "lan-network": "dist/lan-network-cli.js" } }, "sha512-ONPnazC96VKDntab9j9JKwIWhZ4ZUceB4A9Epu4Ssg0hYFmtHZSeQ+n15nIwTFmcBUKtExOer8WTJ4GF9MO64A=="], @@ -3387,11 +3416,11 @@ "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], - "lru-cache": ["lru-cache@11.3.5", "", {}, "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw=="], + "lru-cache": ["lru-cache@11.3.6", "", {}, "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A=="], "lru_map": ["lru_map@0.3.3", "", {}, "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ=="], - "lucide-react": ["lucide-react@1.11.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-UOhjdztXCgdBReRcIhsvz2siIBogfv/lhJEIViCpLt924dO+GDms9T7DNoucI23s6kEPpe988m5N0D2ajnzb2g=="], + "lucide-react": ["lucide-react@1.16.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ=="], "magic-regexp": ["magic-regexp@0.11.0", "", { "dependencies": { "magic-string": "^0.30.21", "regexp-tree": "^0.1.27", "type-level-regexp": "~0.1.17", "unplugin": "^3.0.0" } }, "sha512-LG77Z/gVnwz7oaDpD4heX6ryl+lcr4l1B2gnP4MMvt2pGhGC1Dfj7dl1pXpP4ih+VQFLuAadeKVa+lARAzfW+Q=="], @@ -3479,9 +3508,9 @@ "metro-resolver": ["metro-resolver@0.83.7", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-WSJIENlMcoSsuz66IfBHOkgfp3KJt2UW2TnEHPf1b8pIG2eEXNOVmo2+03A0H17WY2XGXWgxL0CG7FAopqgB1A=="], - "metro-runtime": ["metro-runtime@0.83.6", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-WQPua1G2VgYbwRn6vSKxOhTX7CFbSf/JdUu6Nd8bZnPXckOf7HQ2y51NXNQHoEsiuawathrkzL8pBhv+zgZFmg=="], + "metro-runtime": ["metro-runtime@0.83.7", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-9GKkJURaB2iyYoEExKnedzAHzxmKtSi+k0tsZUvMoU27tBZJElchYt7JH/Ai/XzYAI9lCAaV7u5HZSI8J5Z+wQ=="], - "metro-source-map": ["metro-source-map@0.83.6", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.6", "nullthrows": "^1.1.1", "ob1": "0.83.6", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-AqJbOMMpeyyM4iNI91pchqDIszzNuuHApEhg6OABqZ+9mjLEqzcIEQ/fboZ7x74fNU5DBd2K36FdUQYPqlGClA=="], + "metro-source-map": ["metro-source-map@0.83.7", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.7", "nullthrows": "^1.1.1", "ob1": "0.83.7", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw=="], "metro-symbolicate": ["metro-symbolicate@0.83.7", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.83.7", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-g4suyxw20WOHWI680c+Kq4wC/NF+Hx5pRH9afrMp+sMTxqLeKcPR1Xf4wMhsjlbvx7LbIREdke6q928jEjvJWw=="], @@ -3585,7 +3614,7 @@ "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - "nanoid": ["nanoid@5.1.9", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-ZUvP7KeBLe3OZ1ypw6dI/TzYJuvHP77IM4Ry73waSQTLn8/g8rpdjfyVAh7t1/+FjBtG4lCP42MEbDxOsRpBMw=="], + "nanoid": ["nanoid@5.1.11", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-v+KEsUv2ps74PaSKv0gHTxTCgMXOIfBEbaqa6w6ISIGC7ZsvHN4N9oJ8d4cmf0n5oTzQz2SLmThbQWhjd/8eKg=="], "nanostores": ["nanostores@1.3.0", "", {}, "sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA=="], @@ -3597,7 +3626,7 @@ "netmask": ["netmask@2.1.1", "", {}, "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA=="], - "next": ["next@15.5.15", "", { "dependencies": { "@next/env": "15.5.15", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.15", "@next/swc-darwin-x64": "15.5.15", "@next/swc-linux-arm64-gnu": "15.5.15", "@next/swc-linux-arm64-musl": "15.5.15", "@next/swc-linux-x64-gnu": "15.5.15", "@next/swc-linux-x64-musl": "15.5.15", "@next/swc-win32-arm64-msvc": "15.5.15", "@next/swc-win32-x64-msvc": "15.5.15", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-VSqCrJwtLVGwAVE0Sb/yikrQfkwkZW9p+lL/J4+xe+G3ZA+QnWPqgcfH1tDUEuk9y+pthzzVFp4L/U8JerMfMQ=="], + "next": ["next@15.5.18", "", { "dependencies": { "@next/env": "15.5.18", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.5.18", "@next/swc-darwin-x64": "15.5.18", "@next/swc-linux-arm64-gnu": "15.5.18", "@next/swc-linux-arm64-musl": "15.5.18", "@next/swc-linux-x64-gnu": "15.5.18", "@next/swc-linux-x64-musl": "15.5.18", "@next/swc-win32-arm64-msvc": "15.5.18", "@next/swc-win32-x64-msvc": "15.5.18", "sharp": "^0.34.3" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-eKL8zUJkX9Y5lE+RX/2YJoItVdGlIscyVyboeD9wSpp0PaGqjoA4tTpT2qPqz9ax+5IzGESyLSeZ/RCwbSZ2uQ=="], "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], @@ -3611,7 +3640,7 @@ "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], - "node-releases": ["node-releases@2.0.38", "", {}, "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw=="], + "node-releases": ["node-releases@2.0.44", "", {}, "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ=="], "node-source-walk": ["node-source-walk@7.0.2", "", { "dependencies": { "@babel/parser": "^7.29.0" } }, "sha512-71kFFjYaSshDTA8/a2HiTYPLdASWjLJxUyJxGE+ffxU+KhxSBtM9kiLUX+R2yooFdSFKMFpi4n3PFtDy6qXv8A=="], @@ -3629,7 +3658,7 @@ "nuqs": ["nuqs@2.8.9", "", { "dependencies": { "@standard-schema/spec": "1.0.0" }, "peerDependencies": { "@remix-run/react": ">=2", "@tanstack/react-router": "^1", "next": ">=14.2.0", "react": ">=18.2.0 || ^19.0.0-0", "react-router": "^5 || ^6 || ^7", "react-router-dom": "^5 || ^6 || ^7" }, "optionalPeers": ["@remix-run/react", "@tanstack/react-router", "next", "react-router", "react-router-dom"] }, "sha512-8ou6AEwsxMWSYo2qkfZtYFVzngwbKmg4c00HVxC1fF6CEJv3Fwm6eoZmfVPALB+vw8Udo7KL5uy96PFcYe1BIQ=="], - "ob1": ["ob1@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-m/xZYkwcjo6UqLMrUICEB3iHk7Bjt3RSR7KXMi6Y1MO/kGkPhoRmfUDF6KAan3rLAZ7ABRqnQyKUTwaqZgUV4w=="], + "ob1": ["ob1@0.83.7", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg=="], "object-assign": ["object-assign@4.0.1", "", {}, "sha512-c6legOHWepAbWnp3j5SRUMpxCXBKI4rD7A5Osn9IzZ8w4O/KccXdW0lqdkQKbpk0eHGjNgKihgzY6WuEq99Tfw=="], @@ -3767,7 +3796,7 @@ "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], - "plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="], + "plist": ["plist@3.1.1", "", { "dependencies": { "@xmldom/xmldom": "^0.9.10", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA=="], "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], @@ -3809,7 +3838,7 @@ "prettier-linter-helpers": ["prettier-linter-helpers@1.0.1", "", { "dependencies": { "fast-diff": "^1.1.2" } }, "sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg=="], - "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.3", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-lckXaWWdo2ZVXoMoUO3WIBiz9hVY+YBEh1gYyMFfrWP9WZW/wpFXQKizHx7WrFQFMkcG0bGShdpp531X1n+qpg=="], + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.7.4", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-hermes": "*", "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-hermes", "@prettier/plugin-oxc", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-svelte"] }, "sha512-UKii4RjY05SNt/WQi6/NcOn/LsT0/ILLXsxygjbRg5/YZelsSu5jTqorYHPDGq4nZy5q5hpCu+XdGZ1xaJEQgw=="], "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -3875,11 +3904,11 @@ "react-freeze": ["react-freeze@1.0.4", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA=="], - "react-hook-form": ["react-hook-form@7.73.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-VAfVYOPcx3piiEVQy95vyFmBwbVUsP/AUIN+mpFG8h11yshDd444nn0VyfaGWSRnhOLVgiDu7HIuBtAIzxn9dA=="], + "react-hook-form": ["react-hook-form@7.75.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-Ovv94H+0p3sJ7B9B5QxPuCP1u8V/cHuVGyH55cSwodYDtoJwK+fqk3vjfIgSX59I2U/bU4z0nRJ9HMLpNiWEmw=="], - "react-i18next": ["react-i18next@17.0.4", "", { "dependencies": { "@babel/runtime": "^7.29.2", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 26.0.1", "react": ">= 16.8.0", "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-hQipmK4EF0y6RO6tt6WuqnmWpWYEXmQUUzecmMBuNsIgYd3smXcG4GtYPWhvgxn0pqMOItKlEO8H24HCs5hc3g=="], + "react-i18next": ["react-i18next@17.0.7", "", { "dependencies": { "@babel/runtime": "^7.29.2", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 26.0.10", "react": ">= 16.8.0", "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-rwtPXsb/zwzDafN+gytcjF5YnqGQQIRmCQ6DctBC1VSipRB8GD/MWEVrFP42vjMyuYydxWxM8CZRt+yiNuuoHg=="], - "react-is": ["react-is@19.2.5", "", {}, "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ=="], + "react-is": ["react-is@19.2.6", "", {}, "sha512-XjBR15BhXuylgWGuslhDKqlSayuqvqBX91BP8pauG8kd1zY8kotkNWbXksTCNRarse4kuGbe2kIY05ARtwNIvw=="], "react-leaflet": ["react-leaflet@5.0.0", "", { "dependencies": { "@react-leaflet/core": "^3.0.0" }, "peerDependencies": { "leaflet": "^1.9.0", "react": "^19.0.0", "react-dom": "^19.0.0" } }, "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw=="], @@ -3889,7 +3918,7 @@ "react-native-css-interop": ["react-native-css-interop@0.2.3", "", { "dependencies": { "@babel/helper-module-imports": "^7.22.15", "@babel/traverse": "^7.23.0", "@babel/types": "^7.23.0", "debug": "^4.3.7", "lightningcss": "~1.27.0", "semver": "^7.6.3" }, "peerDependencies": { "react": ">=18", "react-native": "*", "react-native-reanimated": ">=3.6.2", "tailwindcss": "~3" } }, "sha512-wc+JI7iUfdFBqnE18HhMTtD0q9vkhuMczToA87UdHGWwMyxdT5sCcNy+i4KInPCE855IY0Ic8kLQqecAIBWz7w=="], - "react-native-drawer-layout": ["react-native-drawer-layout@4.2.2", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*", "react-native-gesture-handler": ">= 2.0.0", "react-native-reanimated": ">= 2.0.0" } }, "sha512-UG/PTTeyyr43KahbgoGyXri8LMO5USHY3/RUpeKBKwCc7xLVGnDLOVNSRrJw0dDc7YmPbmAyJ4oxp8nKboKKuw=="], + "react-native-drawer-layout": ["react-native-drawer-layout@4.2.4", "", { "dependencies": { "color": "^4.2.3", "use-latest-callback": "^0.2.4" }, "peerDependencies": { "react": ">= 18.2.0", "react-native": "*", "react-native-gesture-handler": ">= 2.0.0", "react-native-reanimated": ">= 2.0.0" } }, "sha512-l1Le5HcVidobnJm8xqFZo46Rs8FDHdxbTZhkjxpNSRgU+QMoQXilOfzTHAeNjEGiKVGgIs9cW3ctXeHqgp5jJg=="], "react-native-fit-image": ["react-native-fit-image@1.5.5", "", { "dependencies": { "prop-types": "^15.5.10" } }, "sha512-Wl3Vq2DQzxgsWKuW4USfck9zS7YzhvLNPpkwUUCF90bL32e1a0zOVQ3WsJILJOwzmPdHfzZmWasiiAUNBkhNkg=="], @@ -3929,7 +3958,7 @@ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], - "react-resizable-panels": ["react-resizable-panels@4.10.0", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-frjewRQt7TCv/vCH1pJfjZ7RxAhr5pKuqVQtVgzFq/vherxBFOWyC3xMbryx5Ti2wylViGUFc93Etg4rB3E0UA=="], + "react-resizable-panels": ["react-resizable-panels@4.11.1", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" } }, "sha512-kA4w58V6wYdRLm2rg9pzroZwGlqBLul1FjMP0J8kqTo3zSHtjeH+LXmZaldCo6+HWqs1e5hOcPoajKXdOze37Q=="], "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], @@ -3995,7 +4024,7 @@ "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], - "resend": ["resend@6.12.2", "", { "dependencies": { "postal-mime": "2.7.4", "svix": "1.90.0" }, "peerDependencies": { "@react-email/render": "*" }, "optionalPeers": ["@react-email/render"] }, "sha512-xwgmU4b0OqoabJsIoK/x0Whk0Fcs3bpbK4i/DEWPiE5hYJHyHl0TbB6QbI3gIr+bLdLUJ1GYm/fe41aVFuHXgw=="], + "resend": ["resend@6.12.3", "", { "dependencies": { "postal-mime": "2.7.4", "svix": "1.92.2" }, "peerDependencies": { "@react-email/render": "*" }, "optionalPeers": ["@react-email/render"] }, "sha512-FkEi6YPnVL96/LvH8+QP7NaeaBy5brYXwlRqUCqZZeNL0/iyKij18IPmyPXYauT/2ODn1JG04qKz+qlJfzqzTw=="], "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], @@ -4015,9 +4044,9 @@ "robots-parser": ["robots-parser@3.0.1", "", {}, "sha512-s+pyvQeIKIZ0dx5iJiQk1tPLJAWln39+MI5jtM8wnyws+G5azk+dMnMX0qfbqNetKKNgcWWOdi0sfm+FbQbgdQ=="], - "rolldown": ["rolldown@1.0.0-rc.17", "", { "dependencies": { "@oxc-project/types": "=0.127.0", "@rolldown/pluginutils": "1.0.0-rc.17" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-arm64": "1.0.0-rc.17", "@rolldown/binding-darwin-x64": "1.0.0-rc.17", "@rolldown/binding-freebsd-x64": "1.0.0-rc.17", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.17", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.17", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.17", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.17", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.17", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.17", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.17" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-ZrT53oAKrtA4+YtBWPQbtPOxIbVDbxT0orcYERKd63VJTF13zPcgXTvD4843L8pcsI7M6MErt8QtON6lrB9tyA=="], + "rolldown": ["rolldown@1.0.1", "", { "dependencies": { "@oxc-project/types": "=0.130.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.1", "@rolldown/binding-darwin-arm64": "1.0.1", "@rolldown/binding-darwin-x64": "1.0.1", "@rolldown/binding-freebsd-x64": "1.0.1", "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", "@rolldown/binding-linux-arm64-gnu": "1.0.1", "@rolldown/binding-linux-arm64-musl": "1.0.1", "@rolldown/binding-linux-ppc64-gnu": "1.0.1", "@rolldown/binding-linux-s390x-gnu": "1.0.1", "@rolldown/binding-linux-x64-gnu": "1.0.1", "@rolldown/binding-linux-x64-musl": "1.0.1", "@rolldown/binding-openharmony-arm64": "1.0.1", "@rolldown/binding-wasm32-wasi": "1.0.1", "@rolldown/binding-win32-arm64-msvc": "1.0.1", "@rolldown/binding-win32-x64-msvc": "1.0.1" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ=="], - "rollup": ["rollup@4.60.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.2", "@rollup/rollup-android-arm64": "4.60.2", "@rollup/rollup-darwin-arm64": "4.60.2", "@rollup/rollup-darwin-x64": "4.60.2", "@rollup/rollup-freebsd-arm64": "4.60.2", "@rollup/rollup-freebsd-x64": "4.60.2", "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", "@rollup/rollup-linux-arm-musleabihf": "4.60.2", "@rollup/rollup-linux-arm64-gnu": "4.60.2", "@rollup/rollup-linux-arm64-musl": "4.60.2", "@rollup/rollup-linux-loong64-gnu": "4.60.2", "@rollup/rollup-linux-loong64-musl": "4.60.2", "@rollup/rollup-linux-ppc64-gnu": "4.60.2", "@rollup/rollup-linux-ppc64-musl": "4.60.2", "@rollup/rollup-linux-riscv64-gnu": "4.60.2", "@rollup/rollup-linux-riscv64-musl": "4.60.2", "@rollup/rollup-linux-s390x-gnu": "4.60.2", "@rollup/rollup-linux-x64-gnu": "4.60.2", "@rollup/rollup-linux-x64-musl": "4.60.2", "@rollup/rollup-openbsd-x64": "4.60.2", "@rollup/rollup-openharmony-arm64": "4.60.2", "@rollup/rollup-win32-arm64-msvc": "4.60.2", "@rollup/rollup-win32-ia32-msvc": "4.60.2", "@rollup/rollup-win32-x64-gnu": "4.60.2", "@rollup/rollup-win32-x64-msvc": "4.60.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ=="], + "rollup": ["rollup@4.60.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.3", "@rollup/rollup-android-arm64": "4.60.3", "@rollup/rollup-darwin-arm64": "4.60.3", "@rollup/rollup-darwin-x64": "4.60.3", "@rollup/rollup-freebsd-arm64": "4.60.3", "@rollup/rollup-freebsd-x64": "4.60.3", "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", "@rollup/rollup-linux-arm-musleabihf": "4.60.3", "@rollup/rollup-linux-arm64-gnu": "4.60.3", "@rollup/rollup-linux-arm64-musl": "4.60.3", "@rollup/rollup-linux-loong64-gnu": "4.60.3", "@rollup/rollup-linux-loong64-musl": "4.60.3", "@rollup/rollup-linux-ppc64-gnu": "4.60.3", "@rollup/rollup-linux-ppc64-musl": "4.60.3", "@rollup/rollup-linux-riscv64-gnu": "4.60.3", "@rollup/rollup-linux-riscv64-musl": "4.60.3", "@rollup/rollup-linux-s390x-gnu": "4.60.3", "@rollup/rollup-linux-x64-gnu": "4.60.3", "@rollup/rollup-linux-x64-musl": "4.60.3", "@rollup/rollup-openbsd-x64": "4.60.3", "@rollup/rollup-openharmony-arm64": "4.60.3", "@rollup/rollup-win32-arm64-msvc": "4.60.3", "@rollup/rollup-win32-ia32-msvc": "4.60.3", "@rollup/rollup-win32-x64-gnu": "4.60.3", "@rollup/rollup-win32-x64-msvc": "4.60.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A=="], "rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="], @@ -4051,7 +4080,7 @@ "sembear": ["sembear@0.7.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-XyLTEich2D02FODCkfdto3mB9DetWPLuTzr4tvoofe9SvyM27h4nQSbV3+iVcYQz94AFyKtqBv5pcZbj3k2hdA=="], - "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], @@ -4111,7 +4140,7 @@ "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], - "socks": ["socks@2.8.8", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-NlGELfPrgX2f1TAAcz0WawlLn+0r3FyhhCRpFFK2CemXenPYvzMWWZINv3eDNo9ucdwme7oCHRY0Jnbs4aIkog=="], + "socks": ["socks@2.8.9", "", { "dependencies": { "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" } }, "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw=="], "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], @@ -4161,7 +4190,7 @@ "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], - "steiger": ["steiger@0.5.11", "", { "dependencies": { "@clack/prompts": "^0.9.1", "@feature-sliced/steiger-plugin": "0.5.7", "chokidar": "^4.0.3", "cosmiconfig": "^9.0.0", "effector": "^23.4.2", "empathic": "^1.1.0", "fastest-levenshtein": "^1.0.16", "globby": "^14.1.0", "immer": "^10.1.1", "lodash-es": "^4.17.21", "micromatch": "^4.0.8", "patronum": "^2.3.0", "picocolors": "^1.1.1", "prexit": "^2.3.0", "yargs": "^17.7.2", "zod": "^3.25.76", "zod-validation-error": "^3.5.3" }, "bin": { "steiger": "dist/cli.mjs" } }, "sha512-Sv6ovPX8tVlUufvAvGSUnwDQLupccYjcgfK5pbqECGP5KCe9M257U0IbRdjuLfUXfey4FprZWQQtnIz3qOPpWA=="], + "steiger": ["steiger@0.5.12", "", { "dependencies": { "@clack/prompts": "^0.9.1", "@feature-sliced/steiger-plugin": "0.5.8", "chokidar": "^4.0.3", "cosmiconfig": "^9.0.0", "effector": "^23.4.2", "empathic": "^1.1.0", "fastest-levenshtein": "^1.0.16", "globby": "^14.1.0", "immer": "^10.1.1", "lodash-es": "^4.17.21", "micromatch": "^4.0.8", "patronum": "^2.3.0", "picocolors": "^1.1.1", "prexit": "^2.3.0", "yargs": "^17.7.2", "zod": "^3.25.76", "zod-validation-error": "^3.5.3" }, "bin": { "steiger": "dist/cli.mjs" } }, "sha512-ZIqsRMRVG0Yr3Y+TQ3kfH+3FXQKRAza/sC77UuCzaXOBRD7NnuXVzWqB/SklBAZC8GMO10neo2V3M53ZnpSJrA=="], "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], @@ -4219,7 +4248,7 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - "svix": ["svix@1.90.0", "", { "dependencies": { "standardwebhooks": "1.0.0", "uuid": "^10.0.0" } }, "sha512-ljkZuyy2+IBEoESkIpn8sLM+sxJHQcPxlZFxU+nVDhltNfUMisMBzWX/UR8SjEnzoI28ZjCzMbmYAPwSTucoMw=="], + "svix": ["svix@1.92.2", "", { "dependencies": { "standardwebhooks": "1.0.0" } }, "sha512-ZmuA3UVvlnF9EgxlzmPtF7CKjQb64Z6OFlyfdDfU0sdcC7dJa+3aOYX5B9mA+RS6ch1AxBa4UP/l6KmqfGtWBQ=="], "swr": ["swr@2.4.1", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA=="], @@ -4227,7 +4256,7 @@ "tagged-tag": ["tagged-tag@1.0.0", "", {}, "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng=="], - "tailwind-merge": ["tailwind-merge@3.5.0", "", {}, "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A=="], + "tailwind-merge": ["tailwind-merge@3.6.0", "", {}, "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w=="], "tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="], @@ -4243,7 +4272,7 @@ "terminal-link": ["terminal-link@2.1.1", "", { "dependencies": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" } }, "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ=="], - "terser": ["terser@5.46.2", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw=="], + "terser": ["terser@5.47.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw=="], "test-exclude": ["test-exclude@7.0.2", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^10.4.1", "minimatch": "^10.2.2" } }, "sha512-u9E6A+ZDYdp7a4WnarkXPZOx8Ilz46+kby6p1yZ8zsGTz9gYa6FIS7lj2oezzNKmtdyyJNNmmXDppga5GB7kSw=="], @@ -4267,7 +4296,7 @@ "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - "tinyexec": ["tinyexec@1.1.1", "", {}, "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg=="], + "tinyexec": ["tinyexec@1.1.2", "", {}, "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA=="], "tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], @@ -4317,7 +4346,7 @@ "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + "tsx": ["tsx@4.22.0", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-8ccZMPD69s1AbKXx0C5ddTNZfNjwV04iIKgjZmKfKxMynEtSYcK0Lh7iQFh53fI5Yu4pb9usgAiqyPmEONaALg=="], "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], @@ -4325,7 +4354,7 @@ "type-fest": ["type-fest@5.6.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA=="], - "type-is": ["type-is@2.0.1", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw=="], + "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], "type-level-regexp": ["type-level-regexp@0.1.17", "", {}, "sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg=="], @@ -4349,7 +4378,7 @@ "uc.micro": ["uc.micro@1.0.6", "", {}, "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="], - "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], + "ufo": ["ufo@1.6.4", "", {}, "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA=="], "uhyphen": ["uhyphen@0.2.0", "", {}, "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA=="], @@ -4361,7 +4390,7 @@ "undici": ["undici@7.25.0", "", {}, "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ=="], - "undici-types": ["undici-types@7.19.2", "", {}, "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg=="], + "undici-types": ["undici-types@7.21.0", "", {}, "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ=="], "unenv": ["unenv@2.0.0-rc.24", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="], @@ -4459,7 +4488,7 @@ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "whatwg-url-minimum": ["whatwg-url-minimum@0.1.1", "", {}, "sha512-u2FNVjFVFZhdjb502KzXy1gKn1mEisQRJssmSJT8CPhZdZa0AP6VCbWlXERKyGu0l09t0k50FiDiralpGhBxgA=="], + "whatwg-url-minimum": ["whatwg-url-minimum@0.1.2", "", {}, "sha512-XPEm0XFQWNVG292lII1PrRRJl3sItrs7CettZ4ncYxuDVpLyy+NwlGyut2hXI0JswcJUxeCH+CyOJK0ZzAXD6A=="], "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], @@ -4477,11 +4506,11 @@ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "workerd": ["workerd@1.20260424.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260424.1", "@cloudflare/workerd-darwin-arm64": "1.20260424.1", "@cloudflare/workerd-linux-64": "1.20260424.1", "@cloudflare/workerd-linux-arm64": "1.20260424.1", "@cloudflare/workerd-windows-64": "1.20260424.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-oKsB0Xo/mfkYMdSACoS06XZg09VUK4rXwHfF/1t3P++sMbwzf4UHQvMO57+zxpEB2nVrY/ZkW0bYFGq4GdAFSQ=="], + "workerd": ["workerd@1.20260511.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260511.1", "@cloudflare/workerd-darwin-arm64": "1.20260511.1", "@cloudflare/workerd-linux-64": "1.20260511.1", "@cloudflare/workerd-linux-arm64": "1.20260511.1", "@cloudflare/workerd-windows-64": "1.20260511.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-ecODCw4iWIuvuXdfy358zNpGPuO8k2WLMTaM1TKd+DuR6IF/oItVuvjf4BNhiKXlhIYBbD5GQ0gGHBY/IxSJVA=="], "workers-ai-provider": ["workers-ai-provider@0.7.5", "", { "dependencies": { "@ai-sdk/provider": "^1.1.3", "@ai-sdk/provider-utils": "^2.2.8" } }, "sha512-dhCwgc3D65oDDTpH3k8Gf0Ek7KItzvaQidn2N5L5cqLo3WG8GM/4+Nr4rU56o8O3oZRsloB1gUCHYaRv2j7Y0A=="], - "wrangler": ["wrangler@4.85.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.2", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260424.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260424.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260424.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-93cwt2RPb1qdcmEgPzH7ybiLN4BIKoWpscIX6SywjHrQOeIZrQk2haoc3XMLKtQTmzapxza9OuDD+kMHpsuuhg=="], + "wrangler": ["wrangler@4.91.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", "miniflare": "4.20260511.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20260511.1" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260511.1" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-dFzhAd2/DpaHGRrQMQkL8F6AKmjzK2hfbhyTJ+5dcZS6jdl9KG8oxO5oAflIdlsRYnrOzxRxQnvIqwewIq1FXA=="], "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], @@ -4491,7 +4520,7 @@ "write-file-atomic": ["write-file-atomic@4.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg=="], - "ws": ["ws@8.20.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA=="], + "ws": ["ws@8.20.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w=="], "xcode": ["xcode@3.0.1", "", { "dependencies": { "simple-plist": "^1.1.0", "uuid": "^7.0.3" } }, "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA=="], @@ -4507,7 +4536,7 @@ "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], - "yaml": ["yaml@2.8.3", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg=="], + "yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="], "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], @@ -4521,7 +4550,7 @@ "youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="], - "youtube-transcript": ["youtube-transcript@1.3.0", "", {}, "sha512-laWv9RcKIWh6rZUH3hVnOngEvtKAhFMV5UepUO6AgevPYqe2zv8KW/uCkZJDSnPwf5/AdVu0Q66/1RDblKsp6Q=="], + "youtube-transcript": ["youtube-transcript@1.3.1", "", {}, "sha512-NDCjwad113TGybbYF51y9Z4tcwzBHUZWQdF9veULNca18L+FdDbHHtTHIr69WVa3bB90l67S8kN0HtL2JO9fhg=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], @@ -4531,28 +4560,16 @@ "zod-validation-error": ["zod-validation-error@3.5.4", "", { "peerDependencies": { "zod": "^3.24.4" } }, "sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw=="], - "zustand": ["zustand@5.0.12", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g=="], + "zustand": ["zustand@5.0.13", "", { "peerDependencies": { "@types/react": ">=18.0.0", "immer": ">=9.0.6", "react": ">=18.0.0", "use-sync-external-store": ">=1.2.0" }, "optionalPeers": ["@types/react", "immer", "react", "use-sync-external-store"] }, "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], "@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@aws-crypto/crc32/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="], - - "@aws-crypto/crc32c/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="], - - "@aws-crypto/sha1-browser/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="], - "@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - "@aws-crypto/sha256-browser/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="], - "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], - "@aws-crypto/sha256-js/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="], - - "@aws-crypto/util/@aws-sdk/types": ["@aws-sdk/types@3.973.8", "", { "dependencies": { "@smithy/types": "^4.14.1", "tslib": "^2.6.2" } }, "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw=="], - "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -4569,11 +4586,11 @@ "@better-auth/core/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - "@better-auth/core/jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + "@better-auth/core/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], - "@better-auth/core/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@better-auth/core/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], - "@better-auth/expo/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@better-auth/expo/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@cloudflare/vitest-pool-workers/wrangler": ["wrangler@4.35.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.7.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250906.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.21", "workerd": "1.20250906.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250906.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-HbyXtbrh4Fi3mU8ussY85tVdQ74qpVS1vctUgaPc+bPrXBTqfDLkZ6VRtHAVF/eBhz4SFmhJtCQpN1caY2Ak8A=="], @@ -4587,8 +4604,6 @@ "@eslint/eslintrc/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "@expo/cli/@expo/plist": ["@expo/plist@0.5.3", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-jz5oPcPDd3fygwVxwSwmO6wodTwm0Qa14NUyPy0ka7H8sFmCtNZUI2+DzVe/EXjOhq1FbEjrwl89gdlWYOnVjQ=="], - "@expo/cli/accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], "@expo/cli/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -4599,8 +4614,6 @@ "@expo/config/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], - "@expo/config-plugins/@expo/json-file": ["@expo/json-file@10.0.13", "", { "dependencies": { "@babel/code-frame": "^7.20.0", "json5": "^2.2.3" } }, "sha512-pX/XjQn7tgNw6zuuV2ikmegmwe/S7uiwhrs2wXrANMkq7ozrA+JcZwgW9Q/8WZgciBzfAhNp5hnackHcrmapQA=="], - "@expo/config-plugins/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@expo/config-plugins/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], @@ -4619,10 +4632,6 @@ "@expo/local-build-cache-provider/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "@expo/metro/metro-runtime": ["metro-runtime@0.83.7", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-9GKkJURaB2iyYoEExKnedzAHzxmKtSi+k0tsZUvMoU27tBZJElchYt7JH/Ai/XzYAI9lCAaV7u5HZSI8J5Z+wQ=="], - - "@expo/metro/metro-source-map": ["metro-source-map@0.83.7", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.7", "nullthrows": "^1.1.1", "ob1": "0.83.7", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw=="], - "@expo/metro-config/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@expo/metro-config/glob": ["glob@13.0.6", "", { "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" } }, "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw=="], @@ -4631,15 +4640,13 @@ "@expo/metro-config/postcss": ["postcss@8.4.49", "", { "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA=="], - "@expo/metro-runtime/@expo/log-box": ["@expo/log-box@55.0.11", "", { "dependencies": { "@expo/dom-webview": "^55.0.5", "anser": "^1.4.9", "stacktrace-parser": "^0.1.10" }, "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-JQHFLWkskIbJi6cxYMjErx8lQqfFJilDQLKmdTO3m3YkdmN9GE/CrzjOfVlCG0DGEGZJ90br0pGKvGPdXNsHKw=="], - "@expo/package-manager/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@expo/xcpretty/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@expo/xcpretty/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "@gorhom/portal/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "@gorhom/portal/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "@humanwhocodes/config-array/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], @@ -4651,15 +4658,15 @@ "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "@lhci/cli/express": ["express@4.22.1", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.3", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.14.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g=="], + "@lhci/cli/express": ["express@4.22.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="], "@lhci/cli/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], "@manypkg/tools/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], - "@modelcontextprotocol/sdk/jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + "@modelcontextprotocol/sdk/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], - "@packrat/api/@types/bun": ["@types/bun@1.3.14", "", { "dependencies": { "bun-types": "1.3.14" } }, "sha512-h1hFqFVcvAvD9j9K7ZW7vd82aSA+rTdznZa+5bwvCwqSB1jmmfLcbIWhOLx1/+boy/xmjgCs/OMUL8hRJSmnPw=="], + "@modelcontextprotocol/sdk/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], @@ -4697,49 +4704,33 @@ "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@react-native-ai/apple/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], - - "@react-native-ai/apple/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-60GYsRj5wIJQRcq5YwYJq4KhwLeStceXEJiZdecP1miiH+6FMmrnc7lZDOJoQ6m9lrudEb+uI4LEwddLz5+rPQ=="], - - "@react-native-ai/apple/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@react-native-ai/apple/@ai-sdk/provider": ["@ai-sdk/provider@2.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww=="], - "@react-native-ai/llama/@ai-sdk/provider": ["@ai-sdk/provider@2.0.1", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng=="], + "@react-native-ai/apple/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.25", "", { "dependencies": { "@ai-sdk/provider": "2.0.3", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CvsRu+32Y8a167s+lrIBtsybvgTHp8j9y+6BeTvLeoW3Q+okw/b4CnNUFOLIXsRaKHQKAH+IHNJPYWywfpw0LA=="], - "@react-native-ai/llama/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.23", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-60GYsRj5wIJQRcq5YwYJq4KhwLeStceXEJiZdecP1miiH+6FMmrnc7lZDOJoQ6m9lrudEb+uI4LEwddLz5+rPQ=="], + "@react-native-ai/apple/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], - "@react-native-ai/llama/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "@react-native-ai/llama/@ai-sdk/provider": ["@ai-sdk/provider@2.0.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-h88OPkavHTiN9tMn2l5awAznGB0lXzjcLhgR1/rvjB2zlLprsNxbM2tt6OJsHUxduLC3klq0/eqaSf6fX5XVww=="], - "@react-native/babel-preset/@babel/plugin-transform-class-properties": ["@babel/plugin-transform-class-properties@7.28.6", "", { "dependencies": { "@babel/helper-create-class-features-plugin": "^7.28.6", "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw=="], + "@react-native-ai/llama/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.25", "", { "dependencies": { "@ai-sdk/provider": "2.0.3", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CvsRu+32Y8a167s+lrIBtsybvgTHp8j9y+6BeTvLeoW3Q+okw/b4CnNUFOLIXsRaKHQKAH+IHNJPYWywfpw0LA=="], - "@react-native/babel-preset/@babel/plugin-transform-classes": ["@babel/plugin-transform-classes@7.28.6", "", { "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-compilation-targets": "^7.28.6", "@babel/helper-globals": "^7.28.0", "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-replace-supers": "^7.28.6", "@babel/traverse": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q=="], - - "@react-native/babel-preset/@babel/plugin-transform-nullish-coalescing-operator": ["@babel/plugin-transform-nullish-coalescing-operator@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg=="], - - "@react-native/babel-preset/@babel/plugin-transform-optional-chaining": ["@babel/plugin-transform-optional-chaining@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6", "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w=="], + "@react-native-ai/llama/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@react-native/codegen/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "@react-native/community-cli-plugin/metro": ["metro@0.83.6", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "accepts": "^2.0.0", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.35.0", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.6", "metro-cache": "0.83.6", "metro-cache-key": "0.83.6", "metro-config": "0.83.6", "metro-core": "0.83.6", "metro-file-map": "0.83.6", "metro-resolver": "0.83.6", "metro-runtime": "0.83.6", "metro-source-map": "0.83.6", "metro-symbolicate": "0.83.6", "metro-transform-plugins": "0.83.6", "metro-transform-worker": "0.83.6", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-pbdndsAZ2F/ceopDdhVbttpa/hfLzXPJ/husc+QvQ33R0D9UXJKzTn5+OzOXx4bpQNtAKF2bY88cCI3Zl44xDQ=="], - - "@react-native/community-cli-plugin/metro-config": ["metro-config@0.83.6", "", { "dependencies": { "connect": "^3.6.5", "flow-enums-runtime": "^0.0.6", "jest-validate": "^29.7.0", "metro": "0.83.6", "metro-cache": "0.83.6", "metro-core": "0.83.6", "metro-runtime": "0.83.6", "yaml": "^2.6.1" } }, "sha512-G5622400uNtnAMlppEA5zkFAZltEf7DSGhOu09BkisCxOlVMWfdosD/oPyh4f2YVQsc1MBYyp4w6OzbExTYarg=="], - - "@react-native/community-cli-plugin/metro-core": ["metro-core@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "lodash.throttle": "^4.1.1", "metro-resolver": "0.83.6" } }, "sha512-l+yQ2fuIgR//wszUlMrrAa9+Z+kbKazd0QOh0VQY7jC4ghb7yZBBSla/UMYRBZZ6fPg9IM+wD3+h+37a5f9etw=="], - "@react-native/dev-middleware/chrome-launcher": ["chrome-launcher@0.15.2", "", { "dependencies": { "@types/node": "*", "escape-string-regexp": "^4.0.0", "is-wsl": "^2.2.0", "lighthouse-logger": "^1.0.0" }, "bin": { "print-chrome-path": "bin/print-chrome-path.js" } }, "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ=="], "@react-native/dev-middleware/serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], "@react-native/dev-middleware/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - "@react-navigation/core/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "@react-navigation/core/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], - "@react-navigation/native/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "@react-navigation/native/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], - "@react-navigation/routers/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "@react-navigation/routers/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], - "@reduxjs/toolkit/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "@reduxjs/toolkit/immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="], + "@reduxjs/toolkit/immer": ["immer@11.1.8", "", {}, "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA=="], "@sentry/cli/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], @@ -4767,9 +4758,11 @@ "@sentry/utils/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + "@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + "@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], - "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "@typescript-eslint/project-service/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], "@typescript-eslint/typescript-estree/globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="], @@ -4779,19 +4772,17 @@ "@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.1.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-cqv9H9GvAEoTaoq+cYqUTCGscUjKqlJZC7PRwY5FMySVj5J+xOm1KQcCiYHJOEzOKRUhLH4R2pTwvFlWCEScsg=="], - "@vue/compiler-core/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], - "@vue/compiler-core/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "@vue/compiler-sfc/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], - "@vue/compiler-sfc/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "accepts/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + "agents/partyserver": ["partyserver@0.5.6", "", { "dependencies": { "nanoid": "^5.1.9" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20260424.1" } }, "sha512-/LKCqlq9nWzNXA8UXZFO/Xz15QDCjJnGAgRQVLnXJO9bA0HKt5J8VM8wLnGc814WatzuQgeG17tqzI//y5WFGA=="], + "agents/yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="], - "agents/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "agents/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -4803,13 +4794,11 @@ "babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "babel-preset-expo/@babel/preset-typescript": ["@babel/preset-typescript@7.28.5", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1", "@babel/helper-validator-option": "^7.27.1", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/plugin-transform-modules-commonjs": "^7.27.1", "@babel/plugin-transform-typescript": "^7.28.5" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g=="], - - "better-auth/jose": ["jose@6.2.2", "", {}, "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ=="], + "better-auth/jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], - "better-auth/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "better-auth/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], - "better-auth-cloudflare/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], + "better-auth-cloudflare/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], @@ -4819,14 +4808,10 @@ "chrome-launcher/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="], - "chrome-launcher/lighthouse-logger": ["lighthouse-logger@1.4.2", "", { "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" } }, "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g=="], - "chrome-launcher/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], "chromium-bidi/zod": ["zod@3.23.8", "", {}, "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g=="], - "chromium-edge-launcher/lighthouse-logger": ["lighthouse-logger@1.4.2", "", { "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" } }, "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g=="], - "chromium-edge-launcher/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], "chromium-edge-launcher/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], @@ -4851,7 +4836,7 @@ "cross-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "detective-typescript/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.0", "@typescript-eslint/tsconfig-utils": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw=="], + "detective-typescript/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.3", "@typescript-eslint/tsconfig-utils": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg=="], "dir-glob/path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="], @@ -4869,9 +4854,9 @@ "eslint/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.59.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.59.0", "@typescript-eslint/type-utils": "8.59.0", "@typescript-eslint/utils": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.59.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.59.3", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/type-utils": "8.59.3", "@typescript-eslint/utils": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.59.3", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw=="], - "eslint-config-universe/@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/typescript-estree": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg=="], + "eslint-config-universe/@typescript-eslint/parser": ["@typescript-eslint/parser@8.59.3", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg=="], "eslint-config-universe/globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], @@ -4909,13 +4894,11 @@ "expo-modules-autolinking/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], - "expo-router/@expo/metro-runtime": ["@expo/metro-runtime@55.0.11", "", { "dependencies": { "@expo/log-box": "55.0.12", "anser": "^1.4.9", "pretty-format": "^29.7.0", "stacktrace-parser": "^0.1.10", "whatwg-fetch": "^3.0.0" }, "peerDependencies": { "expo": "*", "react": "*", "react-dom": "*", "react-native": "*" }, "optionalPeers": ["react-dom"] }, "sha512-4KKi/jGrIEXi2YGu0hYTVr0CEeRJy5SXbCrz9+KDZkuD3ROwKNpM1DBawni5rhPVovFnR323HBck9GaxhnfrRw=="], - - "expo-router/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "expo-router/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "expo-router/semver": ["semver@7.6.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A=="], - "expo-updates/arg": ["arg@4.1.0", "", {}, "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg=="], + "expo-updates/arg": ["arg@4.1.3", "", {}, "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="], "expo-updates/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -5013,30 +4996,14 @@ "merge-options/is-plain-obj": ["is-plain-obj@2.1.0", "", {}, "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="], - "metro/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], - "metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], "metro/hermes-parser": ["hermes-parser@0.35.0", "", { "dependencies": { "hermes-estree": "0.35.0" } }, "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA=="], - "metro/metro-runtime": ["metro-runtime@0.83.7", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-9GKkJURaB2iyYoEExKnedzAHzxmKtSi+k0tsZUvMoU27tBZJElchYt7JH/Ai/XzYAI9lCAaV7u5HZSI8J5Z+wQ=="], - - "metro/metro-source-map": ["metro-source-map@0.83.7", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.7", "nullthrows": "^1.1.1", "ob1": "0.83.7", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw=="], - "metro/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], "metro-babel-transformer/hermes-parser": ["hermes-parser@0.35.0", "", { "dependencies": { "hermes-estree": "0.35.0" } }, "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA=="], - "metro-config/metro-runtime": ["metro-runtime@0.83.7", "", { "dependencies": { "@babel/runtime": "^7.25.0", "flow-enums-runtime": "^0.0.6" } }, "sha512-9GKkJURaB2iyYoEExKnedzAHzxmKtSi+k0tsZUvMoU27tBZJElchYt7JH/Ai/XzYAI9lCAaV7u5HZSI8J5Z+wQ=="], - - "metro-source-map/metro-symbolicate": ["metro-symbolicate@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.83.6", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-4nvkmv9T7ozhprlPwk/+xm0SVPsxly5kYyMHdNaOlFemFz4df9BanvD46Ac6OISu/4Idinzfk2KVb++6OfzPAQ=="], - - "metro-symbolicate/metro-source-map": ["metro-source-map@0.83.7", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.7", "nullthrows": "^1.1.1", "ob1": "0.83.7", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw=="], - - "metro-transform-worker/@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="], - - "metro-transform-worker/metro-source-map": ["metro-source-map@0.83.7", "", { "dependencies": { "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-symbolicate": "0.83.7", "nullthrows": "^1.1.1", "ob1": "0.83.7", "source-map": "^0.5.6", "vlq": "^1.0.0" } }, "sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw=="], - "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], "mimetext/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -5067,7 +5034,9 @@ "p-locate/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "plist/@xmldom/xmldom": ["@xmldom/xmldom@0.9.10", "", {}, "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw=="], + + "postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], @@ -5113,8 +5082,6 @@ "simple-swizzle/is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="], - "socks/ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], - "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], @@ -5125,12 +5092,8 @@ "supports-hyperlinks/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "svix/uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], - "tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - "tailwindcss/postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="], - "tailwindcss/postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], "terminal-link/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], @@ -5145,19 +5108,21 @@ "tsconfig-paths/json5": ["json5@1.0.2", "", { "dependencies": { "minimist": "^1.2.0" }, "bin": { "json5": "lib/cli.js" } }, "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA=="], + "tsx/esbuild": ["esbuild@0.28.0", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.0", "@esbuild/android-arm": "0.28.0", "@esbuild/android-arm64": "0.28.0", "@esbuild/android-x64": "0.28.0", "@esbuild/darwin-arm64": "0.28.0", "@esbuild/darwin-x64": "0.28.0", "@esbuild/freebsd-arm64": "0.28.0", "@esbuild/freebsd-x64": "0.28.0", "@esbuild/linux-arm": "0.28.0", "@esbuild/linux-arm64": "0.28.0", "@esbuild/linux-ia32": "0.28.0", "@esbuild/linux-loong64": "0.28.0", "@esbuild/linux-mips64el": "0.28.0", "@esbuild/linux-ppc64": "0.28.0", "@esbuild/linux-riscv64": "0.28.0", "@esbuild/linux-s390x": "0.28.0", "@esbuild/linux-x64": "0.28.0", "@esbuild/netbsd-arm64": "0.28.0", "@esbuild/netbsd-x64": "0.28.0", "@esbuild/openbsd-arm64": "0.28.0", "@esbuild/openbsd-x64": "0.28.0", "@esbuild/openharmony-arm64": "0.28.0", "@esbuild/sunos-x64": "0.28.0", "@esbuild/win32-arm64": "0.28.0", "@esbuild/win32-ia32": "0.28.0", "@esbuild/win32-x64": "0.28.0" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw=="], + + "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + "util/inherits": ["inherits@2.0.3", "", {}, "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="], "vite/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - "vite/postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="], - "vitest/tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], "workers-ai-provider/@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], "workers-ai-provider/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], - "wrangler/miniflare": ["miniflare@4.20260424.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.8", "workerd": "1.20260424.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-B6MKBBd5TJ19daUc3Ae9rWctn1nDA/VCXykXfCsp9fTxyfGxnZY27tJs1caxgE9MWEMMKGbGHouqVtgKbKGxmw=="], + "wrangler/miniflare": ["miniflare@4.20260511.0", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.8", "workerd": "1.20260511.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-GjTfjtdrDb4LTUcA1S/iz/YKMvSxlbgAfgXpyhM1PQV/xzUYRpKWayq3R1vOQg53Y/g0epaBFaZLA2QbnS/meA=="], "write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -5167,12 +5132,6 @@ "yargs/yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], - "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - - "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - - "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], - "@cloudflare/vitest-pool-workers/wrangler/@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], "@cloudflare/vitest-pool-workers/wrangler/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.7.3", "", { "peerDependencies": { "unenv": "2.0.0-rc.21", "workerd": "^1.20250828.1" }, "optionalPeers": ["workerd"] }, "sha512-tsQQagBKjvpd9baa6nWVIv399ejiqcrUBBW6SZx6Z22+ymm+Odv5+cFimyuCsD/fC1fQTwfRmwXBNpzvHSeGCw=="], @@ -5279,11 +5238,7 @@ "@expo/metro-config/lightningcss/lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="], - "@expo/metro-config/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "@expo/metro-runtime/@expo/log-box/@expo/dom-webview": ["@expo/dom-webview@55.0.5", "", { "peerDependencies": { "expo": "*", "react": "*", "react-native": "*" } }, "sha512-lt3uxYOCk3wmWvtOOvsC35CKGbDAOx5C2EaY8SH1JVSfBzqmF8Cs0Xp1MPxncDPMyxpMiWx5SvvV/iLF1rJU4A=="], - - "@expo/metro/metro-source-map/ob1": ["ob1@0.83.7", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg=="], + "@expo/metro-config/postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "@expo/package-manager/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -5319,8 +5274,6 @@ "@lhci/cli/express/path-to-regexp": ["path-to-regexp@0.1.13", "", {}, "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA=="], - "@lhci/cli/express/qs": ["qs@6.14.2", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q=="], - "@lhci/cli/express/send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], "@lhci/cli/express/serve-static": ["serve-static@1.16.3", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "~0.19.1" } }, "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA=="], @@ -5337,44 +5290,12 @@ "@manypkg/tools/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "@packrat/api/@types/bun/bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="], - "@react-native-ai/apple/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@react-native-ai/llama/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], "@react-native/codegen/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "@react-native/community-cli-plugin/metro/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - - "@react-native/community-cli-plugin/metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], - - "@react-native/community-cli-plugin/metro/hermes-parser": ["hermes-parser@0.35.0", "", { "dependencies": { "hermes-estree": "0.35.0" } }, "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA=="], - - "@react-native/community-cli-plugin/metro/metro-babel-transformer": ["metro-babel-transformer@0.83.6", "", { "dependencies": { "@babel/core": "^7.25.2", "flow-enums-runtime": "^0.0.6", "hermes-parser": "0.35.0", "metro-cache-key": "0.83.6", "nullthrows": "^1.1.1" } }, "sha512-1AnuazBpzY3meRMr04WUw14kRBkV0W3Ez+AA75FAeNpRyWNN5S3M3PHLUbZw7IXq7ZeOzceyRsHStaFrnWd+8w=="], - - "@react-native/community-cli-plugin/metro/metro-cache": ["metro-cache@0.83.6", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.6" } }, "sha512-DpvZE32feNkqfZkI4Fic7YI/Kw8QP9wdl1rC4YKPrA77wQbI9vXbxjmfkCT/EGwBTFOPKqvIXo+H3BNe93YyiQ=="], - - "@react-native/community-cli-plugin/metro/metro-cache-key": ["metro-cache-key@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-5gdK4PVpgNOHi7xCGrgesNP1AuOA2TiPqpcirGXZi4RLLzX1VMowpkgTVtBfpQQCqWoosQF9yrSo9/KDQg1eBg=="], - - "@react-native/community-cli-plugin/metro/metro-file-map": ["metro-file-map@0.83.6", "", { "dependencies": { "debug": "^4.4.0", "fb-watchman": "^2.0.0", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "nullthrows": "^1.1.1", "walker": "^1.0.7" } }, "sha512-Jg3oN604C7GWbQwFAUXt8KsbMXeKfsxbZ5HFy4XFM3ggTS+ja9QgUmq9B613kgXv3G4M6rwiI6cvh9TRly4x3w=="], - - "@react-native/community-cli-plugin/metro/metro-resolver": ["metro-resolver@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-lAwR/FsT1uJ5iCt4AIsN3boKfJ88aN8bjvDT5FwBS0tKeKw4/sbdSTWlFxc7W/MUTN5RekJ3nQkJRIWsvs28tA=="], - - "@react-native/community-cli-plugin/metro/metro-symbolicate": ["metro-symbolicate@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", "metro-source-map": "0.83.6", "nullthrows": "^1.1.1", "source-map": "^0.5.6", "vlq": "^1.0.0" }, "bin": { "metro-symbolicate": "src/index.js" } }, "sha512-4nvkmv9T7ozhprlPwk/+xm0SVPsxly5kYyMHdNaOlFemFz4df9BanvD46Ac6OISu/4Idinzfk2KVb++6OfzPAQ=="], - - "@react-native/community-cli-plugin/metro/metro-transform-plugins": ["metro-transform-plugins@0.83.6", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "flow-enums-runtime": "^0.0.6", "nullthrows": "^1.1.1" } }, "sha512-V+zoY2Ul0v0BW6IokJkTud3raXmDdbdwkUQ/5eiSoy0jKuKMhrDjdH+H5buCS5iiJdNbykOn69Eip+Sqymkodg=="], - - "@react-native/community-cli-plugin/metro/metro-transform-worker": ["metro-transform-worker@0.83.6", "", { "dependencies": { "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "flow-enums-runtime": "^0.0.6", "metro": "0.83.6", "metro-babel-transformer": "0.83.6", "metro-cache": "0.83.6", "metro-cache-key": "0.83.6", "metro-minify-terser": "0.83.6", "metro-source-map": "0.83.6", "metro-transform-plugins": "0.83.6", "nullthrows": "^1.1.1" } }, "sha512-G5kDJ/P0ZTIf57t3iyAd5qIXbj2Wb1j7WtIDh82uTFQHe2Mq2SO9aXG9j1wI+kxZlIe58Z22XEXIKMl89z0ibQ=="], - - "@react-native/community-cli-plugin/metro/ws": ["ws@7.5.10", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": "^5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ=="], - - "@react-native/community-cli-plugin/metro-config/metro-cache": ["metro-cache@0.83.6", "", { "dependencies": { "exponential-backoff": "^3.1.1", "flow-enums-runtime": "^0.0.6", "https-proxy-agent": "^7.0.5", "metro-core": "0.83.6" } }, "sha512-DpvZE32feNkqfZkI4Fic7YI/Kw8QP9wdl1rC4YKPrA77wQbI9vXbxjmfkCT/EGwBTFOPKqvIXo+H3BNe93YyiQ=="], - - "@react-native/community-cli-plugin/metro-core/metro-resolver": ["metro-resolver@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-lAwR/FsT1uJ5iCt4AIsN3boKfJ88aN8bjvDT5FwBS0tKeKw4/sbdSTWlFxc7W/MUTN5RekJ3nQkJRIWsvs28tA=="], - - "@react-native/dev-middleware/chrome-launcher/lighthouse-logger": ["lighthouse-logger@1.4.2", "", { "dependencies": { "debug": "^2.6.9", "marky": "^1.2.2" } }, "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g=="], - "@react-native/dev-middleware/serve-static/send": ["send@0.19.2", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "~0.5.2", "http-errors": "~2.0.1", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "~2.4.1", "range-parser": "~1.2.1", "statuses": "~2.0.2" } }, "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg=="], "@sentry/cli/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], @@ -5397,12 +5318,8 @@ "babel-plugin-istanbul/test-exclude/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "chrome-launcher/lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "chrome-launcher/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "chromium-edge-launcher/lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "chromium-edge-launcher/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], @@ -5423,9 +5340,9 @@ "cosmiconfig/js-yaml/argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], - "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], - "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q=="], + "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "eslint-visitor-keys": "^5.0.0" } }, "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg=="], "detective-typescript/@typescript-eslint/typescript-estree/ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], @@ -5481,25 +5398,25 @@ "drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0" } }, "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3" } }, "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "@typescript-eslint/typescript-estree": "8.59.0", "@typescript-eslint/utils": "8.59.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3", "@typescript-eslint/utils": "8.59.3", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/typescript-estree": "8.59.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/utils": ["@typescript-eslint/utils@8.59.3", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/typescript-estree": "8.59.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "eslint-visitor-keys": "^5.0.0" } }, "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg=="], "eslint-config-universe/@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "eslint-config-universe/@typescript-eslint/eslint-plugin/ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="], - "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0" } }, "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg=="], + "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3" } }, "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA=="], - "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], - "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.0", "@typescript-eslint/tsconfig-utils": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw=="], + "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.3", "@typescript-eslint/tsconfig-utils": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg=="], - "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.0", "", { "dependencies": { "@typescript-eslint/types": "8.59.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q=="], + "eslint-config-universe/@typescript-eslint/parser/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.59.3", "", { "dependencies": { "@typescript-eslint/types": "8.59.3", "eslint-visitor-keys": "^5.0.0" } }, "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg=="], "eslint-plugin-import/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], @@ -5551,14 +5468,8 @@ "metro-babel-transformer/hermes-parser/hermes-estree": ["hermes-estree@0.35.0", "", {}, "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg=="], - "metro-symbolicate/metro-source-map/ob1": ["ob1@0.83.7", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg=="], - - "metro-transform-worker/metro-source-map/ob1": ["ob1@0.83.7", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg=="], - "metro/hermes-parser/hermes-estree": ["hermes-estree@0.35.0", "", {}, "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg=="], - "metro/metro-source-map/ob1": ["ob1@0.83.7", "", { "dependencies": { "flow-enums-runtime": "^0.0.6" } }, "sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg=="], - "mimetext/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], @@ -5609,7 +5520,7 @@ "miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250906.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Q8Qjfs8jGVILnZL6vUpQ90q/8MTCYaGR3d1LGxZMBqte8Vr7xF3KFHPEy7tFs0j0mMjnqCYzlofmPNY+9ZaDRg=="], - "next/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "next/postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "ora/chalk/ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="], @@ -5629,8 +5540,6 @@ "tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - "tailwindcss/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - "terminal-link/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "test-exclude/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], @@ -5641,6 +5550,58 @@ "tmp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.0", "", { "os": "aix", "cpu": "ppc64" }, "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA=="], + + "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.0", "", { "os": "android", "cpu": "arm" }, "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ=="], + + "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.0", "", { "os": "android", "cpu": "arm64" }, "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw=="], + + "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.0", "", { "os": "android", "cpu": "x64" }, "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA=="], + + "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q=="], + + "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ=="], + + "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q=="], + + "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw=="], + + "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw=="], + + "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A=="], + + "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ=="], + + "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg=="], + + "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w=="], + + "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg=="], + + "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.0", "", { "os": "linux", "cpu": "none" }, "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ=="], + + "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q=="], + + "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.0", "", { "os": "linux", "cpu": "x64" }, "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ=="], + + "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw=="], + + "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.0", "", { "os": "none", "cpu": "x64" }, "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw=="], + + "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.0", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g=="], + + "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA=="], + + "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.0", "", { "os": "none", "cpu": "arm64" }, "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w=="], + + "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.0", "", { "os": "sunos", "cpu": "x64" }, "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw=="], + + "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA=="], + + "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA=="], + + "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.0", "", { "os": "win32", "cpu": "x64" }, "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw=="], + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], @@ -5693,20 +5654,12 @@ "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - "vite/postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "workers-ai-provider/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "workers-ai-provider/@ai-sdk/provider-utils/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], "wrangler/miniflare/undici": ["undici@7.24.8", "", {}, "sha512-6KQ/+QxK49Z/p3HO6E5ZCZWNnCasyZLa5ExaVYyvPxUwKtbCPMKELJOqh7EqOle0t9cH/7d2TaaTRRa6Nhs4YQ=="], "wrangler/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], - "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], - - "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], - - "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], - "@cloudflare/vitest-pool-workers/wrangler/@cloudflare/kv-asset-handler/mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], "@cloudflare/vitest-pool-workers/wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], @@ -5785,8 +5738,6 @@ "@lhci/cli/express/body-parser/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], - "@lhci/cli/express/body-parser/qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], - "@lhci/cli/express/body-parser/raw-body": ["raw-body@2.5.3", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.4.24", "unpipe": "~1.0.0" } }, "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA=="], "@lhci/cli/express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], @@ -5803,14 +5754,6 @@ "@react-native/codegen/glob/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], - "@react-native/community-cli-plugin/metro/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - - "@react-native/community-cli-plugin/metro/hermes-parser/hermes-estree": ["hermes-estree@0.35.0", "", {}, "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg=="], - - "@react-native/community-cli-plugin/metro/metro-transform-worker/metro-minify-terser": ["metro-minify-terser@0.83.6", "", { "dependencies": { "flow-enums-runtime": "^0.0.6", "terser": "^5.15.0" } }, "sha512-Vx3/Ne9Q+EIEDLfKzZUOtn/rxSNa/QjlYxc42nvK4Mg8mB6XUgd3LXX5ZZVq7lzQgehgEqLrbgShJPGfeF8PnQ=="], - - "@react-native/dev-middleware/chrome-launcher/lighthouse-logger/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], - "@react-native/dev-middleware/serve-static/send/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "@react-native/dev-middleware/serve-static/send/fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], @@ -5829,27 +5772,23 @@ "babel-plugin-istanbul/test-exclude/minimatch/brace-expansion": ["brace-expansion@1.1.14", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g=="], - "chrome-launcher/lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "chrome-launcher/rimraf/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], - "chromium-edge-launcher/lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "chromium-edge-launcher/rimraf/glob/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], "detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/scope-manager/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.0", "@typescript-eslint/tsconfig-utils": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/type-utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.3", "@typescript-eslint/tsconfig-utils": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.0", "@typescript-eslint/tsconfig-utils": "8.59.0", "@typescript-eslint/types": "8.59.0", "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/utils/@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.59.3", "", { "dependencies": { "@typescript-eslint/project-service": "8.59.3", "@typescript-eslint/tsconfig-utils": "8.59.3", "@typescript-eslint/types": "8.59.3", "@typescript-eslint/visitor-keys": "8.59.3", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg=="], - "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.59.0", "", {}, "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A=="], + "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/@typescript-eslint/types": ["@typescript-eslint/types@8.59.3", "", {}, "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg=="], "eslint-config-universe/@typescript-eslint/eslint-plugin/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="], @@ -5903,8 +5842,6 @@ "@react-native/codegen/glob/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "@react-native/dev-middleware/chrome-launcher/lighthouse-logger/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "@react-native/dev-middleware/serve-static/send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "agents/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], diff --git a/docs/plans/2026-05-13-chore-enroll-catalog-candidates-plan.md b/docs/plans/2026-05-13-chore-enroll-catalog-candidates-plan.md new file mode 100644 index 0000000000..d5796bb90d --- /dev/null +++ b/docs/plans/2026-05-13-chore-enroll-catalog-candidates-plan.md @@ -0,0 +1,122 @@ +--- +title: "chore: enroll remaining catalog candidates into Bun workspace catalog" +type: chore +status: active +date: 2026-05-13 +--- + +# chore: enroll remaining catalog candidates into Bun workspace catalog + +The Bun workspace catalog exists at the root `package.json` with 46 entries and a pre-push +enforcement script (`scripts/lint/no-duplicate-deps.ts`). That script currently emits 47 +**CATALOG CANDIDATE** warnings for third-party deps pinned at the same version in 2+ workspaces. +These are non-blocking today but represent unnecessary drift risk — any future version bump in one +workspace would silently create a VERSION MISMATCH (which IS blocking). + +This chore eliminates all warnings by enrolling every third-party candidate into the catalog. + +## Acceptance Criteria + +- [ ] `bun ./scripts/lint/no-duplicate-deps.ts` produces **0 CATALOG CANDIDATES** for third-party packages +- [ ] **0 CATALOG VIOLATIONS** and **0 VERSION MISMATCHES** (must remain at zero) +- [ ] `bun run check-types` exits 0 after changes +- [ ] `bun install` re-run to sync lockfile after package.json edits + +## Pattern + +**Two-step for each candidate:** + +1. Add an entry to the `"catalog"` object in root `package.json`: + ```json + "package-name": "^x.y.z" + ``` +2. In every workspace `package.json` that pins that dep, replace the explicit version with: + ```json + "package-name": "catalog:" + ``` + +**Do NOT catalog:** `workspace:*` references (`@packrat/guards`, `@packrat/env`, etc.) — these use +the workspace protocol, not semver, and should stay as `"workspace:*"`. + +## Packages to Enroll + +All 47 are currently at a **consistent version** across all workspaces (no mismatches), so the +catalog version is simply the existing pinned string. + +### @-scoped + +| Package | Version | Appears in | +|---|---|---| +| `@ai-sdk/openai` | `^3.0.53` | apps/guides, packages/api | +| `@cloudflare/workers-types` | `^4.20250405.0` | packages/mcp, packages/api | +| `@duckdb/node-api` | `1.5.0-r.1` | packages/cli, packages/analytics | +| `@hookform/resolvers` | `^5.2.2` | apps/landing, apps/guides | +| `@lhci/cli` | `^0.14.0` | apps/landing, apps/guides | +| `@neondatabase/serverless` | `^1.0.0` | packages/api, packages/osm-db | +| `@tanstack/react-query` | `^5.70.0` | apps/web, apps/admin, apps/expo, apps/guides, packages/app | +| `@tanstack/react-query-devtools` | `^5.70.0` | apps/web, apps/guides | +| `@types/bun` | `latest` | packages/cli, packages/api, packages/analytics | +| `@types/leaflet` | `^1.9.21` | apps/trails, apps/admin, apps/expo | +| `@types/node` | `^25.6.0` | apps/trails, apps/landing, apps/web, apps/admin, apps/guides | +| `@types/react` | `~19.2.10` | apps/trails, apps/landing, apps/web, apps/admin, apps/expo, apps/guides, packages/web-ui | +| `@types/react-dom` | `^19.1.6` | apps/trails, apps/landing, apps/web, apps/admin, apps/guides | +| `@vitest/coverage-v8` | `~3.1.4` | apps/expo, packages/mcp, packages/api | + +### Unscoped + +| Package | Version | Appears in | +|---|---|---| +| `autoprefixer` | `^10.4.21` | apps/landing, apps/web, apps/guides | +| `better-auth` | `^1.6.9` | apps/expo, packages/api | +| `class-variance-authority` | `^0.7.1` | apps/trails, apps/landing, apps/admin, apps/expo, apps/guides, packages/web-ui | +| `clsx` | `^2.1.1` | apps/trails, apps/landing, apps/admin, apps/expo, apps/guides, packages/web-ui | +| `cmdk` | `1.1.1` | apps/landing, apps/guides, packages/web-ui | +| `consola` | `^3.4.2` | packages/cli, packages/analytics | +| `date-fns` | `^4.1.0` | apps/landing, apps/expo, apps/guides | +| `drizzle-kit` | `^0.31.10` | packages/api, packages/osm-db | +| `drizzle-orm` | `^0.45.2` | packages/api, packages/osm-db | +| `embla-carousel-react` | `8.6.0` | apps/landing, apps/guides, packages/web-ui | +| `google-auth-library` | `^10.1.0` | apps/expo, packages/api | +| `gray-matter` | `^4.0.3` | apps/guides, packages/api | +| `input-otp` | `1.4.1` | apps/trails, apps/landing, apps/guides, packages/web-ui | +| `jotai` | `^2.12.2` | apps/web, apps/expo, packages/app | +| `leaflet` | `^1.9.4` | apps/trails, apps/admin, apps/expo | +| `lucide-react` | `^1.8.0` | apps/trails, apps/landing, apps/web, apps/admin, apps/guides, packages/web-ui | +| `next` | `^15.3.4` | apps/trails, apps/landing, apps/web, apps/admin, apps/guides | +| `next-themes` | `^0.4.6` | apps/landing, apps/web, apps/admin, apps/guides, packages/web-ui | +| `pg` | `^8.16.3` | packages/osm-import, packages/api, packages/osm-db | +| `postcss` | `^8.5.6` | apps/trails, apps/landing, apps/web, apps/admin, apps/guides | +| `postcss-import` | `^16.1.1` | apps/trails, apps/landing, apps/admin, apps/guides | +| `react-day-picker` | `9.14.0` | apps/landing, apps/guides, packages/web-ui | +| `react-hook-form` | `^7.58.1` | apps/landing, apps/guides, packages/web-ui | +| `react-leaflet` | `^5.0.0` | apps/trails, apps/expo | +| `react-resizable-panels` | `^4.10.0` | apps/landing, apps/guides, packages/web-ui | +| `recharts` | `3.8.1` | apps/web, apps/admin, packages/web-ui | +| `sonner` | `^2.0.7` | apps/trails, apps/landing, apps/admin, apps/guides, packages/web-ui | +| `tailwind-merge` | `^3.5.0` | apps/trails, apps/landing, apps/admin, apps/expo, apps/guides, packages/web-ui | +| `tailwindcss-animate` | `^1.0.7` | apps/web, packages/web-ui | +| `vaul` | `^1.1.2` | apps/landing, apps/guides, packages/web-ui | +| `vitest` | `~3.1.4` | apps/expo, packages/overpass, packages/mcp, packages/units, packages/api, packages/analytics | +| `wrangler` | `^4.21.2` | packages/mcp, packages/api | +| `ws` | `^8.18.1` | packages/api, packages/osm-db | + +## Implementation Steps + +- [ ] Add all 47 packages to the `"catalog"` section of root `package.json` (alphabetical order, interleaved with existing entries) +- [ ] For each workspace listed in the table, replace the pinned version with `"catalog:"` +- [ ] Run `bun install` to regenerate the lockfile +- [ ] Run `bun run check-types` — must exit 0 +- [ ] Run `bun ./scripts/lint/no-duplicate-deps.ts` — must show 0 CATALOG CANDIDATES (for third-party; `workspace:*` refs are expected to remain) + +## Context + +- Root catalog: `package.json` lines ~76–123 +- Enforcement script: `scripts/lint/no-duplicate-deps.ts` +- Pre-push hook: `lefthook.yml` (`pre-push.clean-checks`) +- Related PR: #2414 (type system unification — landed on the same branch pattern) + +## Sources + +- Enforcement script: `scripts/lint/no-duplicate-deps.ts` +- Root catalog: `package.json` (catalog section) +- Bun workspace catalog docs: https://bun.sh/docs/install/workspaces diff --git a/package.json b/package.json index 9bb4facf0f..cd935755f1 100644 --- a/package.json +++ b/package.json @@ -76,12 +76,18 @@ "node": ">=24.0.0" }, "catalog": { + "@ai-sdk/openai": "^3.0.53", + "@cloudflare/workers-types": "^4.20250405.0", + "@duckdb/node-api": "1.5.0-r.1", "@elysiajs/cors": "^1.2.0", "@elysiajs/eden": "^1.2.0", "@elysiajs/openapi": "^1.2.0", "@hono/sentry": "^1.2.2", "@hono/zod-openapi": "^1.3.0", "@hono/zod-validator": "^0.7.6", + "@hookform/resolvers": "^5.2.2", + "@lhci/cli": "^0.14.0", + "@neondatabase/serverless": "^1.0.0", "@radix-ui/react-accordion": "^1.2.11", "@radix-ui/react-alert-dialog": "^1.1.14", "@radix-ui/react-aspect-ratio": "^1.1.7", @@ -109,18 +115,59 @@ "@radix-ui/react-toggle": "^1.1.9", "@radix-ui/react-toggle-group": "^1.1.10", "@radix-ui/react-tooltip": "^1.2.7", + "@tanstack/react-query": "^5.70.0", + "@tanstack/react-query-devtools": "^5.70.0", + "@types/bun": "latest", + "@types/leaflet": "^1.9.21", + "@types/node": "^25.6.0", + "@types/react": "~19.2.10", + "@types/react-dom": "^19.1.6", + "@vitest/coverage-v8": "~3.1.4", "ai": "^6.0.168", + "autoprefixer": "^10.4.21", + "better-auth": "^1.6.9", "chalk": "^5.6.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "1.1.1", + "consola": "^3.4.2", + "date-fns": "^4.1.0", + "drizzle-kit": "^0.31.10", + "drizzle-orm": "^0.45.2", "elysia": "^1.4.0", + "embla-carousel-react": "8.6.0", + "google-auth-library": "^10.1.0", + "gray-matter": "^4.0.3", "hono": "^4.10.7", + "input-otp": "1.4.1", + "jotai": "^2.12.2", + "leaflet": "^1.9.4", + "lucide-react": "^1.8.0", "magic-regexp": "^0.11.0", + "next": "^15.3.4", + "next-themes": "^0.4.6", + "pg": "^8.16.3", + "postcss": "^8.5.6", + "postcss-import": "^16.1.1", "radash": "^12.1.1", "react": "19.2.6", + "react-day-picker": "9.14.0", "react-dom": "19.2.6", + "react-hook-form": "^7.58.1", + "react-leaflet": "^5.0.0", + "react-resizable-panels": "^4.10.0", + "recharts": "3.8.1", "semver": "^7.7.4", + "sonner": "^2.0.7", + "tailwind-merge": "^3.5.0", "tailwindcss": "^3.4.17", + "tailwindcss-animate": "^1.0.7", "ts-extras": "^1.0.0", "typescript": "~5.9.2", + "vaul": "^1.1.2", + "vitest": "~3.1.4", + "wrangler": "^4.21.2", + "ws": "^8.18.1", "zod": "^3.24.2" }, "trustedDependencies": [ diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 287442dc11..4571bbf9da 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -9,16 +9,16 @@ "test:watch": "vitest watch" }, "dependencies": { - "@duckdb/node-api": "1.5.0-r.1", + "@duckdb/node-api": "catalog:", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", - "consola": "^3.4.2", + "consola": "catalog:", "magic-regexp": "catalog:", "radash": "catalog:", "zod": "catalog:" }, "devDependencies": { - "@types/bun": "latest", - "vitest": "~3.1.4" + "@types/bun": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/api/package.json b/packages/api/package.json index 0f513afbab..84fa5821e5 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -32,7 +32,7 @@ }, "dependencies": { "@ai-sdk/google": "^3.0.64", - "@ai-sdk/openai": "^3.0.53", + "@ai-sdk/openai": "catalog:", "@ai-sdk/perplexity": "^3.0.29", "@aws-sdk/client-s3": "~3.787.0", "@aws-sdk/s3-request-presigner": "~3.787.0", @@ -41,7 +41,7 @@ "@elysiajs/eden": "catalog:", "@elysiajs/openapi": "catalog:", "@mozilla/readability": "^0.6.0", - "@neondatabase/serverless": "^1.0.0", + "@neondatabase/serverless": "catalog:", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/overpass": "workspace:*", @@ -51,20 +51,20 @@ "ai": "catalog:", "bcryptjs": "^3.0.2", "csv-parse": "^6.2.1", - "drizzle-kit": "^0.31.10", - "drizzle-orm": "^0.45.2", + "drizzle-kit": "catalog:", + "drizzle-orm": "catalog:", "drizzle-zod": "^0.8.3", "elysia": "catalog:", - "google-auth-library": "^10.1.0", - "gray-matter": "^4.0.3", + "google-auth-library": "catalog:", + "gray-matter": "catalog:", "jose": "^5.9.6", "linkedom": "^0.18.11", "nodemailer": "^6.10.0", - "pg": "^8.16.3", + "pg": "catalog:", "radash": "catalog:", "resend": "^6.10.0", "workers-ai-provider": "^0.7.2", - "ws": "^8.18.1", + "ws": "catalog:", "youtube-transcript": "^1.3.0", "zod": "catalog:", "zod-openapi": "^5.4.6" @@ -72,17 +72,17 @@ "devDependencies": { "@better-auth/drizzle-adapter": "^1.6.9", "@cloudflare/vitest-pool-workers": "0.8.71", - "@cloudflare/workers-types": "^4.20250405.0", - "@types/bun": "latest", + "@cloudflare/workers-types": "catalog:", + "@types/bun": "catalog:", "@types/pg": "^8.11.15", "@types/ws": "^8.5.14", - "@vitest/coverage-v8": "~3.1.4", - "better-auth": "^1.6.9", + "@vitest/coverage-v8": "catalog:", + "better-auth": "catalog:", "better-auth-cloudflare": "^0.3.0", "concurrently": "^8.2.2", - "drizzle-orm": "^0.45.2", + "drizzle-orm": "catalog:", "typed-htmx": "^0.3.1", - "vitest": "~3.1.4", - "wrangler": "^4.21.2" + "vitest": "catalog:", + "wrangler": "catalog:" } } diff --git a/packages/app/package.json b/packages/app/package.json index 0a09b434a9..5c840233ac 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -14,8 +14,8 @@ }, "dependencies": { "@packrat/api-client": "workspace:*", - "@tanstack/react-query": "^5.70.0", - "jotai": "^2.12.2", + "@tanstack/react-query": "catalog:", + "jotai": "catalog:", "react": "catalog:", "zod": "catalog:" }, @@ -24,8 +24,8 @@ "steiger": "^0.5.11" }, "peerDependencies": { - "@tanstack/react-query": "^5.70.0", - "jotai": "^2.12.2", + "@tanstack/react-query": "catalog:", + "jotai": "catalog:", "react": "catalog:" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 3b87ca1a2a..c866c3eac6 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -11,17 +11,17 @@ "packrat": "bun run src/index.ts" }, "dependencies": { - "@duckdb/node-api": "1.5.0-r.1", + "@duckdb/node-api": "catalog:", "@packrat/analytics": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "chalk": "catalog:", "citty": "^0.2.1", "cli-table3": "^0.6.5", - "consola": "^3.4.2", + "consola": "catalog:", "zod": "catalog:" }, "devDependencies": { - "@types/bun": "latest" + "@types/bun": "catalog:" } } diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 37024f4fed..3f79d17662 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -20,11 +20,11 @@ "zod": "catalog:" }, "devDependencies": { - "@cloudflare/workers-types": "^4.20250405.0", - "@vitest/coverage-v8": "~3.1.4", + "@cloudflare/workers-types": "catalog:", + "@vitest/coverage-v8": "catalog:", "partyserver": "^0.4.1", "typescript": "catalog:", - "vitest": "~3.1.4", - "wrangler": "^4.21.2" + "vitest": "catalog:", + "wrangler": "catalog:" } } diff --git a/packages/osm-db/package.json b/packages/osm-db/package.json index b334975ccb..46dc95b72f 100644 --- a/packages/osm-db/package.json +++ b/packages/osm-db/package.json @@ -19,13 +19,13 @@ "db:migrate": "bun run ./migrate.ts" }, "dependencies": { - "@neondatabase/serverless": "^1.0.0", - "drizzle-orm": "^0.45.2", - "pg": "^8.16.3", - "ws": "^8.18.1" + "@neondatabase/serverless": "catalog:", + "drizzle-orm": "catalog:", + "pg": "catalog:", + "ws": "catalog:" }, "devDependencies": { - "drizzle-kit": "^0.31.10", + "drizzle-kit": "catalog:", "typescript": "catalog:" } } diff --git a/packages/osm-import/package.json b/packages/osm-import/package.json index 980e5aa684..a4371619e8 100644 --- a/packages/osm-import/package.json +++ b/packages/osm-import/package.json @@ -10,6 +10,6 @@ }, "dependencies": { "@packrat/env": "workspace:*", - "pg": "^8.16.3" + "pg": "catalog:" } } diff --git a/packages/overpass/package.json b/packages/overpass/package.json index 9a3cf91048..f4ad457aef 100644 --- a/packages/overpass/package.json +++ b/packages/overpass/package.json @@ -23,6 +23,6 @@ }, "devDependencies": { "typescript": "catalog:", - "vitest": "~3.1.4" + "vitest": "catalog:" } } diff --git a/packages/units/package.json b/packages/units/package.json index db8c411890..d2436dac73 100644 --- a/packages/units/package.json +++ b/packages/units/package.json @@ -17,6 +17,6 @@ }, "devDependencies": { "convert-units": "3.0.0-beta.8", - "vitest": "~3.1.4" + "vitest": "catalog:" } } diff --git a/packages/web-ui/package.json b/packages/web-ui/package.json index a161968978..6662fa5d0e 100644 --- a/packages/web-ui/package.json +++ b/packages/web-ui/package.json @@ -45,32 +45,32 @@ "@radix-ui/react-toggle": "catalog:", "@radix-ui/react-toggle-group": "catalog:", "@radix-ui/react-tooltip": "catalog:", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.1.1", - "embla-carousel-react": "8.6.0", - "input-otp": "1.4.1", - "lucide-react": "^1.8.0", - "next-themes": "^0.4.6", - "react-day-picker": "9.14.0", - "react-hook-form": "^7.58.1", - "react-resizable-panels": "^4.10.0", - "recharts": "3.8.1", - "sonner": "^2.0.7", - "tailwind-merge": "^3.5.0", - "tailwindcss-animate": "^1.0.7", - "vaul": "^1.1.2" + "class-variance-authority": "catalog:", + "clsx": "catalog:", + "cmdk": "catalog:", + "embla-carousel-react": "catalog:", + "input-otp": "catalog:", + "lucide-react": "catalog:", + "next-themes": "catalog:", + "react-day-picker": "catalog:", + "react-hook-form": "catalog:", + "react-resizable-panels": "catalog:", + "recharts": "catalog:", + "sonner": "catalog:", + "tailwind-merge": "catalog:", + "tailwindcss-animate": "catalog:", + "vaul": "catalog:" }, "devDependencies": { - "@types/react": "~19.2.10", + "@types/react": "catalog:", "react": "catalog:", - "recharts": "3.8.1", + "recharts": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:" }, "peerDependencies": { "react": "catalog:", - "recharts": "3.8.1", + "recharts": "catalog:", "tailwindcss": "catalog:" } } From 858e06827bf05bc0954a6f40a13b1cebfcea3acb Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 13:11:06 -0600 Subject: [PATCH 21/51] fix(guides): remove standalone bun.lock that broke Cloudflare Pages catalog resolution apps/guides/bun.lock was created in isolation ('my-v0-project') and didn't understand catalog: entries from the monorepo root. CF Pages found this file and tried to install against it, failing to resolve the new catalog: deps. Deleting it lets CF Pages use the root bun.lock like all other apps. --- apps/guides/bun.lock | 987 ------------------------------------------- 1 file changed, 987 deletions(-) delete mode 100644 apps/guides/bun.lock diff --git a/apps/guides/bun.lock b/apps/guides/bun.lock deleted file mode 100644 index d605bd76fa..0000000000 --- a/apps/guides/bun.lock +++ /dev/null @@ -1,987 +0,0 @@ -{ - "lockfileVersion": 1, - "workspaces": { - "": { - "name": "my-v0-project", - "dependencies": { - "@ai-sdk/openai": "^1.3.4", - "@hookform/resolvers": "^3.9.1", - "@radix-ui/react-accordion": "^1.2.2", - "@radix-ui/react-alert-dialog": "^1.1.4", - "@radix-ui/react-aspect-ratio": "^1.1.1", - "@radix-ui/react-avatar": "^1.1.2", - "@radix-ui/react-checkbox": "^1.1.3", - "@radix-ui/react-collapsible": "^1.1.2", - "@radix-ui/react-context-menu": "^2.2.4", - "@radix-ui/react-dialog": "^1.1.6", - "@radix-ui/react-dropdown-menu": "^2.1.4", - "@radix-ui/react-hover-card": "^1.1.4", - "@radix-ui/react-label": "^2.1.1", - "@radix-ui/react-menubar": "^1.1.4", - "@radix-ui/react-navigation-menu": "^1.2.3", - "@radix-ui/react-popover": "^1.1.4", - "@radix-ui/react-progress": "^1.1.1", - "@radix-ui/react-radio-group": "^1.2.2", - "@radix-ui/react-scroll-area": "^1.2.2", - "@radix-ui/react-select": "^2.1.4", - "@radix-ui/react-separator": "^1.1.1", - "@radix-ui/react-slider": "^1.2.2", - "@radix-ui/react-slot": "^1.1.1", - "@radix-ui/react-switch": "^1.1.2", - "@radix-ui/react-tabs": "^1.1.2", - "@radix-ui/react-toast": "^1.2.4", - "@radix-ui/react-toggle": "^1.1.1", - "@radix-ui/react-toggle-group": "^1.1.1", - "@radix-ui/react-tooltip": "^1.1.6", - "@tailwindcss/typography": "^0.5.16", - "@tanstack/react-query": "^5.70.0", - "@tanstack/react-query-devtools": "^5.70.0", - "ai": "^4.2.8", - "autoprefixer": "^10.4.20", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "1.0.4", - "date-fns": "^4.1.0", - "embla-carousel-react": "8.5.1", - "fs": "^0.0.1-security", - "gray-matter": "^4.0.3", - "input-otp": "1.4.1", - "lucide-react": "^0.454.0", - "mdx": "^0.3.1", - "next": "15.2.4", - "next-themes": "^0.4.6", - "path": "^0.12.7", - "react": "^19", - "react-day-picker": "8.10.1", - "react-dom": "^19", - "react-hook-form": "^7.54.1", - "react-resizable-panels": "^2.1.7", - "recharts": "2.15.0", - "remark": "^15.0.1", - "remark-html": "^16.0.1", - "slugify": "^1.6.6", - "sonner": "^1.7.1", - "tailwind-merge": "^2.5.5", - "tailwindcss": "^3.4.17", - "tailwindcss-animate": "^1.0.7", - "vaul": "^0.9.6", - "zod": "^3.24.1", - }, - "devDependencies": { - "@types/node": "^22", - "@types/react": "^19", - "@types/react-dom": "^19", - "postcss": "^8.5.3", - "tailwindcss": "^3.4.17", - "typescript": "^5.8.2", - }, - }, - }, - "packages": { - "@ai-sdk/openai": ["@ai-sdk/openai@1.3.4", "", { "dependencies": { "@ai-sdk/provider": "1.1.0", "@ai-sdk/provider-utils": "2.2.1" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-BOw7dQpiTlpaqi1u/NU4Or2+jA6buzl6GOUuYyu/uFI7dxJs1zPkY8IjAp4DQhi+kQGH6GGbEPw0LkIbeK4BVA=="], - - "@ai-sdk/provider": ["@ai-sdk/provider@1.1.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-0M+qjp+clUD0R1E5eWQFhxEvWLNaOtGQRUaBn8CUABnSKredagq92hUS9VjOzGsTm37xLfpaxl97AVtbeOsHew=="], - - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.1", "", { "dependencies": { "@ai-sdk/provider": "1.1.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-BuExLp+NcpwsAVj1F4bgJuQkSqO/+roV9wM7RdIO+NVrcT8RBUTdXzf5arHt5T58VpK7bZyB2V9qigjaPHE+Dg=="], - - "@ai-sdk/react": ["@ai-sdk/react@1.2.3", "", { "dependencies": { "@ai-sdk/provider-utils": "2.2.1", "@ai-sdk/ui-utils": "1.2.2", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["zod"] }, "sha512-EQ6nmmQBBAal1yg72GB/Q7QnmDXMfgYvCo9Gym2mESXUHTqwpXU0JFHtk5Kq3EEkk7CVMf1oBWlNFNvU5ckQBg=="], - - "@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.2", "", { "dependencies": { "@ai-sdk/provider": "1.1.0", "@ai-sdk/provider-utils": "2.2.1", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-6rCx2jSEPuiF6fytfMNscSOinHQZp52aFCHyPVpPPkcWnOur1jPWhol+0TFCUruDl7dCfcSIfTexQUq2ioLwaA=="], - - "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - - "@babel/runtime": ["@babel/runtime@7.27.0", "", { "dependencies": { "regenerator-runtime": "^0.14.0" } }, "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw=="], - - "@emnapi/runtime": ["@emnapi/runtime@1.4.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw=="], - - "@floating-ui/core": ["@floating-ui/core@1.6.9", "", { "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw=="], - - "@floating-ui/dom": ["@floating-ui/dom@1.6.13", "", { "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.9" } }, "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w=="], - - "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A=="], - - "@floating-ui/utils": ["@floating-ui/utils@0.2.9", "", {}, "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg=="], - - "@hookform/resolvers": ["@hookform/resolvers@3.10.0", "", { "peerDependencies": { "react-hook-form": "^7.0.0" } }, "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag=="], - - "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], - - "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], - - "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], - - "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], - - "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], - - "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], - - "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], - - "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], - - "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], - - "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], - - "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], - - "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], - - "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], - - "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], - - "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], - - "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], - - "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], - - "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], - - "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], - - "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], - - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], - - "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - - "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], - - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], - - "@next/env": ["@next/env@15.2.4", "", {}, "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g=="], - - "@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw=="], - - "@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew=="], - - "@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ=="], - - "@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA=="], - - "@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw=="], - - "@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw=="], - - "@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg=="], - - "@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ=="], - - "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], - - "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], - - "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], - - "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - - "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - - "@radix-ui/number": ["@radix-ui/number@1.1.0", "", {}, "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ=="], - - "@radix-ui/primitive": ["@radix-ui/primitive@1.1.1", "", {}, "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA=="], - - "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.2.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collapsible": "1.1.3", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A=="], - - "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dialog": "1.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ=="], - - "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg=="], - - "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TaJxYoCpxJ7vfEkv2PTNox/6zzmpKXT6ewvCuf2tTOIVN45/Jahhlld29Yw4pciOXS2Xq91/rSGEdmEnUWZCqA=="], - - "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.1.3", "", { "dependencies": { "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Paen00T4P8L8gd9bNsRMw7Cbaz85oxiv+hzomsRZgFm2byltPFDtfcoqlWJ8GyZlIBWgLssJlzLCnKU0G0302g=="], - - "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.1.4", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw=="], - - "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-jFSerheto1X03MUC0g6R7LedNW9EEGWdg9W1+MlpkMLwGkgkbUXLPBH/KIuWKXUoeYRVY11llqbTBDzuLg7qrw=="], - - "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw=="], - - "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="], - - "@radix-ui/react-context": ["@radix-ui/react-context@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q=="], - - "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aUP99QZ3VU84NPsHeaFt4cQUNgJqFsLLOt/RbbWXszZ6MP0DpDyjkFZORr4RpAEx3sUBk+Kc8h13yGtC5Qw8dg=="], - - "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw=="], - - "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg=="], - - "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg=="], - - "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA=="], - - "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg=="], - - "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA=="], - - "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ=="], - - "@radix-ui/react-id": ["@radix-ui/react-id@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA=="], - - "@radix-ui/react-label": ["@radix-ui/react-label@2.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw=="], - - "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-callback-ref": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg=="], - - "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-menu": "2.1.6", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FHq7+3DlXwh/7FOM4i0G4bC4vPjiq89VEEvNF4VMLchGnaUuUbE5uKXMUCjdKaOghEEMeiKa5XCa2Pk4kteWmg=="], - - "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.2.5", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-myMHHQUZ3ZLTi8W381/Vu43Ia0NqakkQZ2vzynMmTUtQQ9kNkjzhOwkZC9TAM5R07OZUVIQyHC06f/9JZJpvvA=="], - - "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg=="], - - "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.2", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-rect": "1.1.0", "@radix-ui/react-use-size": "1.1.0", "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA=="], - - "@radix-ui/react-portal": ["@radix-ui/react-portal@1.1.4", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA=="], - - "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg=="], - - "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.0.2", "", { "dependencies": { "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w=="], - - "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.2", "", { "dependencies": { "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA=="], - - "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.2.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA=="], - - "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw=="], - - "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.3", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ=="], - - "@radix-ui/react-select": ["@radix-ui/react-select@2.1.6", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg=="], - - "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ=="], - - "@radix-ui/react-slider": ["@radix-ui/react-slider@1.2.3", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-nNrLAWLjGESnhqBqcCNW4w2nn7LxudyMzeB6VgdyAnFLC6kfQgnAjSL2v6UkQTnDctJBlxrmxfplWS4iYjdUTw=="], - - "@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.2", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="], - - "@radix-ui/react-switch": ["@radix-ui/react-switch@1.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ=="], - - "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng=="], - - "@radix-ui/react-toast": ["@radix-ui/react-toast@1.2.6", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA=="], - - "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lntKchNWx3aCHuWKiDY+8WudiegQvBpDRAYL8dKLRvKEH8VOpl0XX6SSU/bUBqIRJbcTy4+MW06Wv8vgp10rzQ=="], - - "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.1.2", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-roving-focus": "1.1.2", "@radix-ui/react-toggle": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-JBm6s6aVG/nwuY5eadhU2zDi/IwYS0sDM5ZWb4nymv/hn3hZdkw+gENn0LP4iY1yCd7+bgJaCwueMYJIU3vk4A=="], - - "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.1.8", "", { "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA=="], - - "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="], - - "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw=="], - - "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.0", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw=="], - - "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w=="], - - "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og=="], - - "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.0", "", { "dependencies": { "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ=="], - - "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.0", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw=="], - - "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.1.2", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q=="], - - "@radix-ui/rect": ["@radix-ui/rect@1.1.0", "", {}, "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg=="], - - "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], - - "@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="], - - "@tailwindcss/typography": ["@tailwindcss/typography@0.5.16", "", { "dependencies": { "lodash.castarray": "^4.4.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA=="], - - "@tanstack/query-core": ["@tanstack/query-core@5.70.0", "", {}, "sha512-ZkkjQAZjI6nS5OyAmaSQafQXK180Xvp0lZYk4BzrnskkTV8On3zSJUxOIXnh0h/8EgqRkCA9i879DiJovA1kGw=="], - - "@tanstack/query-devtools": ["@tanstack/query-devtools@5.67.2", "", {}, "sha512-O4QXFFd7xqp6EX7sdvc9tsVO8nm4lpWBqwpgjpVLW5g7IeOY6VnS/xvs/YzbRhBVkKTMaJMOUGU7NhSX+YGoNg=="], - - "@tanstack/react-query": ["@tanstack/react-query@5.70.0", "", { "dependencies": { "@tanstack/query-core": "5.70.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-z0tx1zz2CQ6nTm+fCaOp93FqsFjNgXtOy+4mC5ifQ4B+rJiMD0AGfJrYSGh/OuefhrzTYDAbkGUAGw6JzkWy8g=="], - - "@tanstack/react-query-devtools": ["@tanstack/react-query-devtools@5.70.0", "", { "dependencies": { "@tanstack/query-devtools": "5.67.2" }, "peerDependencies": { "@tanstack/react-query": "^5.70.0", "react": "^18 || ^19" } }, "sha512-jFtpA3mnUoVn/ic1EVxmA6qG7z8S19nchsHciMCWOvC1Z2Mt8f0wbl1p8hNvrBpzWywZa+Hl0AxMVs48psUvhg=="], - - "@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="], - - "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="], - - "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="], - - "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="], - - "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="], - - "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="], - - "@types/d3-shape": ["@types/d3-shape@3.1.7", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg=="], - - "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="], - - "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="], - - "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], - - "@types/diff-match-patch": ["@types/diff-match-patch@1.0.36", "", {}, "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg=="], - - "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], - - "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - - "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - - "@types/node": ["@types/node@22.13.14", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w=="], - - "@types/react": ["@types/react@19.0.12", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA=="], - - "@types/react-dom": ["@types/react-dom@19.0.4", "", { "peerDependencies": { "@types/react": "^19.0.0" } }, "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg=="], - - "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], - - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - - "ai": ["ai@4.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.0", "@ai-sdk/provider-utils": "2.2.1", "@ai-sdk/react": "1.2.3", "@ai-sdk/ui-utils": "1.2.2", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-0gwfPZAuuQ+uTfk/GssrfnNTYxliCFKojbSQoEhzpbpSVaPao9NoU3iuE8vwBjWuDKqILRGzYGFE4+vTak0Oxg=="], - - "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], - - "ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], - - "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], - - "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], - - "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], - - "argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - - "aria-hidden": ["aria-hidden@1.2.4", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A=="], - - "array-find-index": ["array-find-index@1.0.2", "", {}, "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw=="], - - "autoprefixer": ["autoprefixer@10.4.21", "", { "dependencies": { "browserslist": "^4.24.4", "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ=="], - - "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], - - "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - - "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], - - "brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], - - "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - - "browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="], - - "busboy": ["busboy@1.6.0", "", { "dependencies": { "streamsearch": "^1.1.0" } }, "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA=="], - - "camelcase": ["camelcase@2.1.1", "", {}, "sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw=="], - - "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], - - "camelcase-keys": ["camelcase-keys@2.1.0", "", { "dependencies": { "camelcase": "^2.0.0", "map-obj": "^1.0.0" } }, "sha512-bA/Z/DERHKqoEOrp+qeGKw1QlvEQkGZSc0XaY6VnTxZr+Kv1G5zFwttpjv8qxZ/sBPT4nthwZaAcsAZTJlSKXQ=="], - - "caniuse-lite": ["caniuse-lite@1.0.30001707", "", {}, "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw=="], - - "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], - - "chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], - - "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], - - "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], - - "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], - - "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - - "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], - - "client-only": ["client-only@0.0.1", "", {}, "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="], - - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], - - "cmdk": ["cmdk@1.0.4", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.0", "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-AnsjfHyHpQ/EFeAnG216WY7A5LiYCoZzCSygiLvfXC3H3LFGCprErteUcszaVluGOhuOTbJS3jWHrSDYPBBygg=="], - - "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], - - "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - - "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], - - "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], - - "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], - - "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - - "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], - - "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], - - "currently-unhandled": ["currently-unhandled@0.4.1", "", { "dependencies": { "array-find-index": "^1.0.1" } }, "sha512-/fITjgjGU50vjQ4FH6eUoYu+iUoUKIXws2hL15JJpIR+BbTxaXQsMuuyjtNh2WqsSBS5nsaZHFsFecyw5CCAng=="], - - "d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], - - "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], - - "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="], - - "d3-format": ["d3-format@3.1.0", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="], - - "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], - - "d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="], - - "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], - - "d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="], - - "d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], - - "d3-time-format": ["d3-time-format@4.1.0", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], - - "d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="], - - "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], - - "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], - - "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="], - - "decode-named-character-reference": ["decode-named-character-reference@1.1.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w=="], - - "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], - - "detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], - - "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], - - "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], - - "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], - - "diff-match-patch": ["diff-match-patch@1.0.5", "", {}, "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="], - - "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], - - "dom-helpers": ["dom-helpers@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA=="], - - "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], - - "electron-to-chromium": ["electron-to-chromium@1.5.128", "", {}, "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ=="], - - "embla-carousel": ["embla-carousel@8.5.1", "", {}, "sha512-JUb5+FOHobSiWQ2EJNaueCNT/cQU9L6XWBbWmorWPQT9bkbk+fhsuLr8wWrzXKagO3oWszBO7MSx+GfaRk4E6A=="], - - "embla-carousel-react": ["embla-carousel-react@8.5.1", "", { "dependencies": { "embla-carousel": "8.5.1", "embla-carousel-reactive-utils": "8.5.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-z9Y0K84BJvhChXgqn2CFYbfEi6AwEr+FFVVKm/MqbTQ2zIzO1VQri6w67LcfpVF0AjbhwVMywDZqY4alYkjW5w=="], - - "embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.5.1", "", { "peerDependencies": { "embla-carousel": "8.5.1" } }, "sha512-n7VSoGIiiDIc4MfXF3ZRTO59KDp820QDuyBDGlt5/65+lumPHxX2JLz0EZ23hZ4eg4vZGUXwMkYv02fw2JVo/A=="], - - "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], - - "error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="], - - "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - - "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], - - "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], - - "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], - - "extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], - - "fast-equals": ["fast-equals@5.2.2", "", {}, "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw=="], - - "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], - - "fastq": ["fastq@1.19.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ=="], - - "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], - - "find-up": ["find-up@1.1.2", "", { "dependencies": { "path-exists": "^2.0.0", "pinkie-promise": "^2.0.0" } }, "sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA=="], - - "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], - - "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], - - "fs": ["fs@0.0.1-security", "", {}, "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - - "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], - - "get-stdin": ["get-stdin@4.0.1", "", {}, "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw=="], - - "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], - - "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - - "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], - - "gray-matter": ["gray-matter@4.0.3", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], - - "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], - - "hast-util-sanitize": ["hast-util-sanitize@5.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "unist-util-position": "^5.0.0" } }, "sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg=="], - - "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], - - "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], - - "hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="], - - "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], - - "indent-string": ["indent-string@2.1.0", "", { "dependencies": { "repeating": "^2.0.0" } }, "sha512-aqwDFWSgSgfRaEwao5lg5KEcVd/2a+D1rvoG7NdilmYz0NwRk6StWpWdz/Hpk34MKPpx7s8XxUqimfcQK6gGlg=="], - - "inherits": ["inherits@2.0.3", "", {}, "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="], - - "input-otp": ["input-otp@1.4.1", "", { "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw=="], - - "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], - - "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], - - "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], - - "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], - - "is-extendable": ["is-extendable@0.1.1", "", {}, "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw=="], - - "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - - "is-finite": ["is-finite@1.1.0", "", {}, "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="], - - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - - "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - - "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - - "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], - - "is-utf8": ["is-utf8@0.2.1", "", {}, "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="], - - "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], - - "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], - - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - - "js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], - - "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], - - "jsondiffpatch": ["jsondiffpatch@0.6.0", "", { "dependencies": { "@types/diff-match-patch": "^1.0.36", "chalk": "^5.3.0", "diff-match-patch": "^1.0.5" }, "bin": { "jsondiffpatch": "bin/jsondiffpatch.js" } }, "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ=="], - - "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], - - "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], - - "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], - - "load-json-file": ["load-json-file@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.2", "parse-json": "^2.2.0", "pify": "^2.0.0", "pinkie-promise": "^2.0.0", "strip-bom": "^2.0.0" } }, "sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A=="], - - "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], - - "lodash.castarray": ["lodash.castarray@4.4.0", "", {}, "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q=="], - - "lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="], - - "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="], - - "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], - - "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], - - "loud-rejection": ["loud-rejection@1.6.0", "", { "dependencies": { "currently-unhandled": "^0.4.1", "signal-exit": "^3.0.0" } }, "sha512-RPNliZOFkqFumDhvYqOaNY4Uz9oJM2K9tC6JWsJJsNdhuONW4LQHRBpb0qf4pJApVffI5N39SwzWZJuEhfd7eQ=="], - - "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], - - "lucide-react": ["lucide-react@0.454.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, "sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ=="], - - "map-obj": ["map-obj@1.0.1", "", {}, "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg=="], - - "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA=="], - - "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], - - "mdast-util-to-hast": ["mdast-util-to-hast@13.2.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA=="], - - "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], - - "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], - - "mdx": ["mdx@0.3.1", "", { "dependencies": { "meow": "3.6.0", "mustache": "2.2.1", "object-assign": "4.0.1", "read-input": "0.3.1" }, "bin": { "mdx": "./bin/mdx" } }, "sha512-i+oUkB4ntcYVYnjiuktpYP77m/ISDg7z1B2pL+alDNFPgRkXlkYaW6zY03103/88A06E3Pn6x/DL9qB8pT9xWA=="], - - "meow": ["meow@3.6.0", "", { "dependencies": { "camelcase-keys": "^2.0.0", "loud-rejection": "^1.0.0", "minimist": "^1.1.3", "normalize-package-data": "^2.3.4", "object-assign": "^4.0.1", "read-pkg-up": "^1.0.1", "redent": "^1.0.0", "trim-newlines": "^1.0.0" } }, "sha512-1zRGO8C/2QD8uBxZbwwKbIQHrHKANzVnlK/3Gj7xro+ks4HLmayvETy+BnCV+wm68PE6dYcfgyTDMVG2mjlQwg=="], - - "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - - "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], - - "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], - - "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], - - "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], - - "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], - - "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], - - "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], - - "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], - - "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], - - "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], - - "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], - - "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], - - "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], - - "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], - - "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], - - "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], - - "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], - - "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], - - "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], - - "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], - - "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], - - "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - - "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], - - "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - - "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "mustache": ["mustache@2.2.1", "", { "bin": { "mustache": "./bin/mustache" } }, "sha512-azYRexmi9y6h2lk2JqfBLh1htlDMjKYyEYOkxoGKa0FRdr5aY4f5q8bH4JIecM181DtUEYLSz8PcRO46mgzMNQ=="], - - "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], - - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "next": ["next@15.2.4", "", { "dependencies": { "@next/env": "15.2.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.2.4", "@next/swc-darwin-x64": "15.2.4", "@next/swc-linux-arm64-gnu": "15.2.4", "@next/swc-linux-arm64-musl": "15.2.4", "@next/swc-linux-x64-gnu": "15.2.4", "@next/swc-linux-x64-musl": "15.2.4", "@next/swc-win32-arm64-msvc": "15.2.4", "@next/swc-win32-x64-msvc": "15.2.4", "sharp": "^0.33.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ=="], - - "next-themes": ["next-themes@0.4.6", "", { "peerDependencies": { "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA=="], - - "node-releases": ["node-releases@2.0.19", "", {}, "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw=="], - - "normalize-package-data": ["normalize-package-data@2.5.0", "", { "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", "semver": "2 || 3 || 4 || 5", "validate-npm-package-license": "^3.0.1" } }, "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA=="], - - "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], - - "normalize-range": ["normalize-range@0.1.2", "", {}, "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA=="], - - "object-assign": ["object-assign@4.0.1", "", {}, "sha512-c6legOHWepAbWnp3j5SRUMpxCXBKI4rD7A5Osn9IzZ8w4O/KccXdW0lqdkQKbpk0eHGjNgKihgzY6WuEq99Tfw=="], - - "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], - - "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], - - "parse-json": ["parse-json@2.2.0", "", { "dependencies": { "error-ex": "^1.2.0" } }, "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ=="], - - "path": ["path@0.12.7", "", { "dependencies": { "process": "^0.11.1", "util": "^0.10.3" } }, "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q=="], - - "path-exists": ["path-exists@2.1.0", "", { "dependencies": { "pinkie-promise": "^2.0.0" } }, "sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ=="], - - "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], - - "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], - - "path-type": ["path-type@1.1.0", "", { "dependencies": { "graceful-fs": "^4.1.2", "pify": "^2.0.0", "pinkie-promise": "^2.0.0" } }, "sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - - "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], - - "pinkie": ["pinkie@2.0.4", "", {}, "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg=="], - - "pinkie-promise": ["pinkie-promise@2.0.1", "", { "dependencies": { "pinkie": "^2.0.0" } }, "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw=="], - - "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], - - "postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], - - "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], - - "postcss-js": ["postcss-js@4.0.1", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw=="], - - "postcss-load-config": ["postcss-load-config@4.0.2", "", { "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, "peerDependencies": { "postcss": ">=8.0.9", "ts-node": ">=9.0.0" }, "optionalPeers": ["postcss", "ts-node"] }, "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ=="], - - "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], - - "postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], - - "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], - - "process": ["process@0.11.10", "", {}, "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A=="], - - "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], - - "property-information": ["property-information@7.0.0", "", {}, "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg=="], - - "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - - "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], - - "react-day-picker": ["react-day-picker@8.10.1", "", { "peerDependencies": { "date-fns": "^2.28.0 || ^3.0.0", "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA=="], - - "react-dom": ["react-dom@19.1.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g=="], - - "react-hook-form": ["react-hook-form@7.55.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-XRnjsH3GVMQz1moZTW53MxfoWN7aDpUg/GpVNc4A3eXRVNdGXfbzJ4vM4aLQ8g6XCUh1nIbx70aaNCl7kxnjog=="], - - "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], - - "react-remove-scroll": ["react-remove-scroll@2.6.3", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="], - - "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], - - "react-resizable-panels": ["react-resizable-panels@2.1.7", "", { "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA=="], - - "react-smooth": ["react-smooth@4.0.4", "", { "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q=="], - - "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], - - "react-transition-group": ["react-transition-group@4.4.5", "", { "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" }, "peerDependencies": { "react": ">=16.6.0", "react-dom": ">=16.6.0" } }, "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g=="], - - "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], - - "read-input": ["read-input@0.3.1", "", {}, "sha512-J1ZkWCnB4altU7RTe+62PSfa21FrEtfKyO9fuqR3yP8kZku3nIwaw2Krj383JC7egAIl5Zyz2w+EOu9uXH5HZw=="], - - "read-pkg": ["read-pkg@1.1.0", "", { "dependencies": { "load-json-file": "^1.0.0", "normalize-package-data": "^2.3.2", "path-type": "^1.0.0" } }, "sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ=="], - - "read-pkg-up": ["read-pkg-up@1.0.1", "", { "dependencies": { "find-up": "^1.0.0", "read-pkg": "^1.0.0" } }, "sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A=="], - - "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], - - "recharts": ["recharts@2.15.0", "", { "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", "react-is": "^18.3.1", "react-smooth": "^4.0.0", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, "peerDependencies": { "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-cIvMxDfpAmqAmVgc4yb7pgm/O1tmmkl/CjrvXuW+62/+7jj/iF9Ykm+hb/UJt42TREHMyd3gb+pkgoa2MxgDIw=="], - - "recharts-scale": ["recharts-scale@0.4.5", "", { "dependencies": { "decimal.js-light": "^2.4.1" } }, "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w=="], - - "redent": ["redent@1.0.0", "", { "dependencies": { "indent-string": "^2.1.0", "strip-indent": "^1.0.1" } }, "sha512-qtW5hKzGQZqKoh6JNSD+4lfitfPKGz42e6QwiRmPM5mmKtR0N41AbJRYu0xJi7nhOJ4WDgRkKvAk6tw4WIwR4g=="], - - "regenerator-runtime": ["regenerator-runtime@0.14.1", "", {}, "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="], - - "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], - - "remark-html": ["remark-html@16.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "hast-util-sanitize": "^5.0.0", "hast-util-to-html": "^9.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0" } }, "sha512-B9JqA5i0qZe0Nsf49q3OXyGvyXuZFDzAP2iOFLEumymuYJITVpiH1IgsTEwTpdptDmZlMDMWeDmSawdaJIGCXQ=="], - - "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], - - "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], - - "repeating": ["repeating@2.0.1", "", { "dependencies": { "is-finite": "^1.0.0" } }, "sha512-ZqtSMuVybkISo2OWvqvm7iHSWngvdaW3IpsT9/uP8v4gMi591LY6h35wdOfvQdWCKFWZWm2Y1Opp4kV7vQKT6A=="], - - "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], - - "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], - - "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - - "scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="], - - "section-matter": ["section-matter@1.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "kind-of": "^6.0.0" } }, "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA=="], - - "secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="], - - "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], - - "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - - "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], - - "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], - - "slugify": ["slugify@1.6.6", "", {}, "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw=="], - - "sonner": ["sonner@1.7.4", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], - - "spdx-correct": ["spdx-correct@3.2.0", "", { "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA=="], - - "spdx-exceptions": ["spdx-exceptions@2.5.0", "", {}, "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w=="], - - "spdx-expression-parse": ["spdx-expression-parse@3.0.1", "", { "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q=="], - - "spdx-license-ids": ["spdx-license-ids@3.0.21", "", {}, "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg=="], - - "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - - "streamsearch": ["streamsearch@1.1.0", "", {}, "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="], - - "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - - "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], - - "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], - - "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "strip-bom": ["strip-bom@2.0.0", "", { "dependencies": { "is-utf8": "^0.2.0" } }, "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g=="], - - "strip-bom-string": ["strip-bom-string@1.0.0", "", {}, "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g=="], - - "strip-indent": ["strip-indent@1.0.1", "", { "dependencies": { "get-stdin": "^4.0.1" }, "bin": { "strip-indent": "cli.js" } }, "sha512-I5iQq6aFMM62fBEAIB/hXzwJD6EEZ0xEGCX2t7oXqaKPIRgt4WruAQ285BISgdkP+HLGWyeGmNJcpIwFeRYRUA=="], - - "styled-jsx": ["styled-jsx@5.1.6", "", { "dependencies": { "client-only": "0.0.1" }, "peerDependencies": { "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" } }, "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA=="], - - "sucrase": ["sucrase@3.35.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA=="], - - "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - - "swr": ["swr@2.3.3", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A=="], - - "tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="], - - "tailwindcss": ["tailwindcss@3.4.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og=="], - - "tailwindcss-animate": ["tailwindcss-animate@1.0.7", "", { "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders" } }, "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA=="], - - "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], - - "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], - - "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], - - "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], - - "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], - - "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], - - "trim-newlines": ["trim-newlines@1.0.0", "", {}, "sha512-Nm4cF79FhSTzrLKGDMi3I4utBtFv8qKy4sq1enftf2gMdpqI8oVQTAfySkTz5r49giVzDj88SVZXP4CeYQwjaw=="], - - "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], - - "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], - - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - - "typescript": ["typescript@5.8.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ=="], - - "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], - - "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], - - "unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], - - "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], - - "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], - - "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], - - "unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], - - "update-browserslist-db": ["update-browserslist-db@1.1.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], - - "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], - - "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], - - "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], - - "util": ["util@0.10.4", "", { "dependencies": { "inherits": "2.0.3" } }, "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A=="], - - "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], - - "validate-npm-package-license": ["validate-npm-package-license@3.0.4", "", { "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" } }, "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew=="], - - "vaul": ["vaul@0.9.9", "", { "dependencies": { "@radix-ui/react-dialog": "^1.1.1" }, "peerDependencies": { "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0" } }, "sha512-7afKg48srluhZwIkaU+lgGtFCUsYBSGOl8vcc8N/M3YQlZFlynHD15AE+pwrYdc826o7nrIND4lL9Y6b9WWZZQ=="], - - "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], - - "vfile-message": ["vfile-message@4.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw=="], - - "victory-vendor": ["victory-vendor@36.9.2", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ=="], - - "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], - - "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], - - "yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], - - "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], - - "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], - - "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], - - "@tailwindcss/typography/postcss-selector-parser": ["postcss-selector-parser@6.0.10", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], - - "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "error-ex/is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], - - "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], - - "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - - "meow/object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - - "mz/object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - - "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], - - "normalize-package-data/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "prop-types/object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], - - "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - - "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - - "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - - "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - - "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - } -} From f06c3685477d7c22b7da3cce066b1795ce2e6f9d Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 00:34:43 -0600 Subject: [PATCH 22/51] fix(ci): add CF Pages-specific error message for missing GitHub Packages token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When packrat-guides builds on Cloudflare Pages and PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN is absent, the preinstall hook now prints the exact CF Pages dashboard path to add it, rather than the generic CI secrets message. Root cause of packrat-guides CF Pages failure since c29d17425: the token was removed or expired in the CF Pages project environment variables. Fix: add PACKRAT_NATIVEWIND_UI_GITHUB_TOKEN (GitHub PAT with read:packages scope) to the packrat-guides CF Pages project settings. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/scripts/configure-deps.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/scripts/configure-deps.ts b/.github/scripts/configure-deps.ts index 72ed22e259..ea4fde83dd 100755 --- a/.github/scripts/configure-deps.ts +++ b/.github/scripts/configure-deps.ts @@ -24,6 +24,10 @@ function isCI(): boolean { ); } +function isCFPages(): boolean { + return process.env.CF_PAGES === '1'; +} + function printLocalFix(): void { console.error(`\n❌ ${TOKEN_VAR} is not exported in your shell.`); console.error('\nBun reads bunfig.toml before the preinstall hook runs, so the token'); @@ -47,8 +51,18 @@ async function configureDeps() { } if (isCI()) { - console.error(`❌ ${TOKEN_VAR} not found in CI environment`); - console.error(`Set ${TOKEN_VAR} in your CI secrets and expose it to this job.`); + if (isCFPages()) { + console.error(`❌ ${TOKEN_VAR} not found in Cloudflare Pages build environment.`); + console.error('Add it as an environment variable in the CF Pages project settings:'); + console.error( + ' dash.cloudflare.com → Pages → packrat-guides → Settings → Environment variables', + ); + console.error(` Variable name: ${TOKEN_VAR}`); + console.error(' Value: a GitHub PAT with read:packages scope'); + } else { + console.error(`❌ ${TOKEN_VAR} not found in CI environment`); + console.error(`Set ${TOKEN_VAR} in your CI secrets and expose it to this job.`); + } process.exit(1); } From b2fe5669349b328562efcafe0955d38b01805712 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 08:25:57 -0600 Subject: [PATCH 23/51] fix(deps): replace vitest version pins with catalog: in landing and guides Pre-push hook caught that apps/landing and apps/guides were pinning vitest@~3.1.4 directly instead of referencing the workspace catalog entry. --- apps/guides/package.json | 2 +- apps/landing/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/guides/package.json b/apps/guides/package.json index a5b4cb7f46..d8e0ffd7af 100644 --- a/apps/guides/package.json +++ b/apps/guides/package.json @@ -99,6 +99,6 @@ "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", - "vitest": "~3.1.4" + "vitest": "catalog:" } } diff --git a/apps/landing/package.json b/apps/landing/package.json index bf8725fd18..63ad6e9d7f 100644 --- a/apps/landing/package.json +++ b/apps/landing/package.json @@ -75,6 +75,6 @@ "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", - "vitest": "~3.1.4" + "vitest": "catalog:" } } From f16805ffd490fb3578ab28d27e6c70ce00d396ec Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 08:30:18 -0600 Subject: [PATCH 24/51] =?UTF-8?q?fix(api):=20lower=20cpu=5Fms=20limit=20to?= =?UTF-8?q?=20300000=20=E2=80=94=20CF=20max=20is=20300=20s=20not=20400=20s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloudflare Workers enforces a hard cap of 300,000 ms (code: 10206). The previous value of 400,000 ms caused every wrangler deploy to fail. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/wrangler.jsonc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/wrangler.jsonc b/packages/api/wrangler.jsonc index 9297ec0c96..a3d7e913c7 100644 --- a/packages/api/wrangler.jsonc +++ b/packages/api/wrangler.jsonc @@ -16,7 +16,7 @@ "head_sampling_rate": 1 }, "limits": { - "cpu_ms": 400000 // 400,000 milliseconds = max allowed by Cloudflare + "cpu_ms": 300000 // 300,000 milliseconds = max allowed by Cloudflare (code: 10206) }, // Environment variables are managed via: // - Production: Cloudflare dashboard From aff5a1444a6ebe9d41f6e33fbb2e40dece5e1746 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Tue, 12 May 2026 23:58:53 -0600 Subject: [PATCH 25/51] feat(api): add response schemas to all Elysia routes for Eden Treaty type inference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add response: { 200: Schema } to all remaining routes so Eden Treaty stops returning unknown for API responses. Convert return status() error paths to throw so handler return types stay clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/expo/features/guides/types.ts | 2 +- packages/api/src/routes/catalog/index.ts | 20 ++++--- packages/api/src/routes/feed/index.ts | 13 +++-- packages/api/src/routes/guides/index.ts | 41 +++++++++------ packages/api/src/routes/packs/index.ts | 38 ++++++++------ packages/api/src/routes/trips/index.ts | 67 ++++++++---------------- packages/api/src/routes/upload.ts | 20 ++++--- packages/api/src/routes/user/index.ts | 31 ++++++----- packages/api/src/routes/weather.ts | 13 +++-- packages/api/src/schemas/guides.ts | 5 ++ packages/api/src/schemas/packs.ts | 1 + packages/api/src/schemas/upload.ts | 4 +- 12 files changed, 140 insertions(+), 115 deletions(-) diff --git a/apps/expo/features/guides/types.ts b/apps/expo/features/guides/types.ts index 7bf619f216..f6ac2a5940 100644 --- a/apps/expo/features/guides/types.ts +++ b/apps/expo/features/guides/types.ts @@ -7,7 +7,7 @@ export interface Guide { description: string; content?: string; author?: string; - readingTime?: string; + readingTime?: number; difficulty?: string; createdAt: string; updatedAt: string; diff --git a/packages/api/src/routes/catalog/index.ts b/packages/api/src/routes/catalog/index.ts index 8e6f31b814..66bdf9dffd 100644 --- a/packages/api/src/routes/catalog/index.ts +++ b/packages/api/src/routes/catalog/index.ts @@ -2,7 +2,10 @@ import { createDb } from '@packrat/api/db'; import { catalogItems, etlJobs, packItems } from '@packrat/api/db/schema'; import { apiKeyAuthPlugin, authPlugin } from '@packrat/api/middleware/auth'; import { + CatalogCategoriesResponseSchema, + CatalogItemSchema, CatalogItemsQuerySchema, + CatalogItemsResponseSchema, CreateCatalogItemRequestSchema, UpdateCatalogItemRequestSchema, VectorSearchQuerySchema, @@ -27,7 +30,7 @@ import { ne, sql, } from 'drizzle-orm'; -import { Elysia, status } from 'elysia'; +import { Elysia, NotFoundError, status } from 'elysia'; import { z } from 'zod'; const catalogETLSchema = z.object({ @@ -68,16 +71,17 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) const totalPages = Math.ceil(result.total / limit); - return { + return CatalogItemsResponseSchema.parse({ items: result.items, totalCount: result.total, page, limit, totalPages, - }; + }); }, { query: CatalogItemsQuerySchema, + response: { 200: CatalogItemsResponseSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], @@ -116,12 +120,13 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) '/categories', async ({ query }) => { const categories = await new CatalogService().getCategories(query.limit); - return categories; + return CatalogCategoriesResponseSchema.parse(categories); }, { query: z.object({ limit: z.coerce.number().int().positive().optional().default(10), }), + response: { 200: CatalogCategoriesResponseSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], @@ -319,7 +324,7 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) itemId <= 0 || itemId > 2147483647 ) { - return status(404, { error: 'Catalog item not found' }); + throw new NotFoundError('Catalog item not found'); } const item = await db.query.catalogItems.findFirst({ @@ -333,15 +338,16 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) }); if (!item) { - return status(404, { error: 'Catalog item not found' }); + throw new NotFoundError('Catalog item not found'); } const usageCount = item.packItems?.length || 0; const { packItems: _packItems, ...itemData } = item; - return { ...itemData, usageCount }; + return CatalogItemSchema.parse({ ...itemData, usageCount }); }, { params: z.object({ id: z.string() }), + response: { 200: CatalogItemSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], diff --git a/packages/api/src/routes/feed/index.ts b/packages/api/src/routes/feed/index.ts index bece1afa17..d232171ca9 100644 --- a/packages/api/src/routes/feed/index.ts +++ b/packages/api/src/routes/feed/index.ts @@ -1,7 +1,11 @@ import { createDb } from '@packrat/api/db'; import { commentLikes, postComments, postLikes, posts, users } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; -import { CreateCommentRequestSchema, CreatePostRequestSchema } from '@packrat/api/schemas/feed'; +import { + CreateCommentRequestSchema, + CreatePostRequestSchema, + FeedResponseSchema, +} from '@packrat/api/schemas/feed'; import { and, count, desc, eq, inArray } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; @@ -44,8 +48,10 @@ export const feedRoutes = new Elysia({ prefix: '/feed' }) const total = totalResult[0]?.count ?? 0; + const totalPages = Math.ceil(total / limit); + if (items.length === 0) { - return { items: [], page, limit, total, totalPages: Math.ceil(total / limit) }; + return FeedResponseSchema.parse({ items: [], page, limit, total, totalPages }); } const postIds = items.map((p) => p.id); @@ -84,13 +90,14 @@ export const feedRoutes = new Elysia({ prefix: '/feed' }) likedByMe: myLikeSet.has(p.id), })); - return { items: result, page, limit, total, totalPages: Math.ceil(total / limit) }; + return FeedResponseSchema.parse({ items: result, page, limit, total, totalPages }); }, { query: z.object({ page: z.coerce.number().int().min(1).optional().default(1), limit: z.coerce.number().int().min(1).max(50).optional().default(20), }), + response: { 200: FeedResponseSchema }, isAuthenticated: true, detail: { tags: ['Feed'], summary: 'List social feed posts', security: [{ bearerAuth: [] }] }, }, diff --git a/packages/api/src/routes/guides/index.ts b/packages/api/src/routes/guides/index.ts index c327787e3d..7c57553507 100644 --- a/packages/api/src/routes/guides/index.ts +++ b/packages/api/src/routes/guides/index.ts @@ -1,5 +1,12 @@ import { authPlugin } from '@packrat/api/middleware/auth'; -import { GuideSearchQuerySchema, GuidesQuerySchema } from '@packrat/api/schemas/guides'; +import { + GuideCategoriesResponseSchema, + GuideDetailSchema, + GuideSearchQuerySchema, + GuideSearchResponseSchema, + GuidesQuerySchema, + GuidesResponseSchema, +} from '@packrat/api/schemas/guides'; import { R2BucketService } from '@packrat/api/services/r2-bucket'; import { getEnv } from '@packrat/api/utils/env-validation'; import { asNumber, asString, isArray } from '@packrat/guards'; @@ -7,7 +14,7 @@ import { asNumber, asString, isArray } from '@packrat/guards'; const MDX_EXT_RE = /\.(mdx?|md)$/; const DASH_RE = /-/g; -import { Elysia, status } from 'elysia'; +import { Elysia, NotFoundError } from 'elysia'; import matter from 'gray-matter'; import { z } from 'zod'; @@ -110,20 +117,21 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) const paginatedGuides = filteredGuides.slice(offset, offset + limit); const totalPages = Math.ceil(total / limit); - return { + return GuidesResponseSchema.parse({ items: paginatedGuides, totalCount: total, page, limit, totalPages, - }; + }); } catch (error) { console.error('Error listing guides:', error); - return status(500, { error: 'Failed to list guides' }); + throw error; } }, { query: GuidesQuerySchema, + response: { 200: GuidesResponseSchema }, isAuthenticated: true, detail: { tags: ['Guides'], @@ -170,13 +178,14 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) } const categories = Array.from(categoriesSet).sort(); - return { categories, count: categories.length }; + return GuideCategoriesResponseSchema.parse({ categories, count: categories.length }); } catch (error) { console.error('Error getting guide categories:', error); - return status(500, { error: 'Failed to get guide categories' }); + throw error; } }, { + response: { 200: GuideCategoriesResponseSchema }, isAuthenticated: true, detail: { tags: ['Guides'], @@ -192,7 +201,7 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) async ({ query }) => { const { q, page, limit, category } = query; if (!q || q.trim() === '') { - return status(400, { error: 'Search query parameter q is required' }); + throw new Error('Search query parameter q is required'); } const searchQuery = q.toLowerCase(); @@ -261,21 +270,22 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) const paginatedGuides = guides.slice(offset, offset + limit); const totalPages = Math.ceil(total / limit); - return { + return GuideSearchResponseSchema.parse({ items: paginatedGuides, totalCount: total, page, limit, totalPages, query: q, - }; + }); } catch (error) { console.error('Error searching guides:', error); - return status(500, { error: 'Failed to search guides' }); + throw error; } }, { query: GuideSearchQuerySchema, + response: { 200: GuideSearchResponseSchema }, isAuthenticated: true, detail: { tags: ['Guides'], @@ -306,7 +316,7 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) } if (!object) { - return status(404, { error: 'Guide not found' }); + throw new NotFoundError('Guide not found'); } const headResult = await bucket.head(key); @@ -314,7 +324,7 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) const rawContent = await object.text(); const { data: frontmatter, content } = matter(rawContent); - return { + return GuideDetailSchema.parse({ id, key, title: frontmatter.title || metadata.title || id.replace(DASH_RE, ' '), @@ -327,14 +337,15 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) content, createdAt: object.uploaded.toISOString(), updatedAt: object.uploaded.toISOString(), - }; + }); } catch (error) { console.error('Error fetching guide:', error); - return status(500, { error: 'Failed to fetch guide' }); + throw error; } }, { params: z.object({ id: z.string() }), + response: { 200: GuideDetailSchema }, isAuthenticated: true, detail: { tags: ['Guides'], diff --git a/packages/api/src/routes/packs/index.ts b/packages/api/src/routes/packs/index.ts index 1bdf287ca5..ec38e7ade3 100644 --- a/packages/api/src/routes/packs/index.ts +++ b/packages/api/src/routes/packs/index.ts @@ -15,6 +15,8 @@ import { CreatePackItemRequestSchema, CreatePackRequestSchema, GapAnalysisRequestSchema, + PackItemSchema, + PackWithWeightsSchema, UpdatePackItemRequestSchema, UpdatePackRequestSchema, } from '@packrat/api/schemas/packs'; @@ -37,7 +39,7 @@ import { or, sql, } from 'drizzle-orm'; -import { Elysia, status } from 'elysia'; +import { Elysia, NotFoundError, status } from 'elysia'; import { z } from 'zod'; const CreatePackBodySchema = CreatePackRequestSchema.extend({ @@ -72,12 +74,13 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) }, }); - return computePacksWeights(result); + return z.array(PackWithWeightsSchema).parse(computePacksWeights(result)); }, { query: z.object({ includePublic: z.coerce.number().int().min(0).max(1).optional().default(0), }), + response: { 200: z.array(PackWithWeightsSchema) }, isAuthenticated: true, detail: { tags: ['Packs'], summary: 'List user packs', security: [{ bearerAuth: [] }] }, }, @@ -91,7 +94,7 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) const data = body; const packId = data.id as string; - if (!packId) return status(400, { error: 'Pack ID is required' }); + if (!packId) throw new Error('Pack ID is required'); // Zod validates all fields at runtime; cast through the Standard Schema // inference gap so drizzle's insert accepts the values. @@ -111,13 +114,14 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) } as typeof packs.$inferInsert) .returning(); - if (!newPack) return status(400, { error: 'Failed to create pack' }); + if (!newPack) throw new Error('Failed to create pack'); const packWithItems: PackWithItems = { ...newPack, items: [] }; - return computePacksWeights([packWithItems])[0]; + return PackWithWeightsSchema.parse(computePacksWeights([packWithItems])[0]); }, { body: CreatePackBodySchema, + response: { 200: PackWithWeightsSchema }, isAuthenticated: true, detail: { tags: ['Packs'], summary: 'Create new pack', security: [{ bearerAuth: [] }] }, }, @@ -233,15 +237,17 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) with: { items: { where: eq(packItems.deleted, false) } }, }); - if (!pack) return status(404, { error: 'Pack not found' }); - return computePackWeights(pack); + if (!pack) throw new NotFoundError('Pack not found'); + return PackWithWeightsSchema.parse(computePackWeights(pack)); } catch (error) { + if (error instanceof NotFoundError) throw error; console.error('Error fetching pack:', error); - return status(500, { error: 'Failed to fetch pack' }); + throw error; } }, { params: z.object({ packId: z.string() }), + response: { 200: PackWithWeightsSchema }, isAuthenticated: true, detail: { tags: ['Packs'], summary: 'Get pack by ID', security: [{ bearerAuth: [] }] }, }, @@ -674,17 +680,18 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s with: { catalogItem: true, pack: true }, }); - if (!item) return status(404, { error: 'Item not found' }); + if (!item) throw new NotFoundError('Item not found'); const isOwner = item.userId === user.userId; const isPublic = item.pack.isPublic; - if (!isOwner && !isPublic) return status(403, { error: 'Unauthorized' }); + if (!isOwner && !isPublic) throw new Error('Unauthorized'); - return item; + return PackItemSchema.parse(item); }, { params: z.object({ itemId: z.string() }), + response: { 200: PackItemSchema }, isAuthenticated: true, detail: { tags: ['Pack Items'], @@ -704,13 +711,13 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); - if (!OPENAI_API_KEY) return status(500, { error: 'OpenAI API key not configured' }); + if (!OPENAI_API_KEY) throw new Error('OpenAI API key not configured'); const existingItem = await db.query.packItems.findFirst({ where: and(eq(packItems.id, itemId), eq(packItems.userId, user.userId)), }); - if (!existingItem) return status(404, { error: 'Pack item not found' }); + if (!existingItem) throw new NotFoundError('Pack item not found'); const newEmbeddingText = getEmbeddingText(data, existingItem); const oldEmbeddingText = getEmbeddingText(existingItem); @@ -763,16 +770,17 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s .where(and(eq(packItems.id, itemId), eq(packItems.userId, user.userId))) .returning(); - if (!updatedItem) return status(404, { error: 'Pack item not found' }); + if (!updatedItem) throw new NotFoundError('Pack item not found'); await db.update(packs).set({ updatedAt: new Date() }).where(eq(packs.id, updatedItem.packId)); updatedItem.embedding = null; - return updatedItem; + return PackItemSchema.parse(updatedItem); }, { params: z.object({ itemId: z.string() }), body: UpdatePackItemRequestSchema, + response: { 200: PackItemSchema }, isAuthenticated: true, detail: { tags: ['Pack Items'], diff --git a/packages/api/src/routes/trips/index.ts b/packages/api/src/routes/trips/index.ts index 8d927714a7..f38f2cc1cc 100644 --- a/packages/api/src/routes/trips/index.ts +++ b/packages/api/src/routes/trips/index.ts @@ -1,34 +1,11 @@ import { createDb } from '@packrat/api/db'; -import { type Trip, trips } from '@packrat/api/db/schema'; +import { trips } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; +import { CreateTripBodySchema, TripSchema, UpdateTripBodySchema } from '@packrat/api/schemas/trips'; import { and, eq } from 'drizzle-orm'; -import { Elysia, status } from 'elysia'; +import { Elysia, NotFoundError } from 'elysia'; import { z } from 'zod'; -const LocationSchema = z - .object({ - latitude: z.number(), - longitude: z.number(), - name: z.string().optional(), - }) - .nullable() - .optional(); - -const CreateTripRequestSchema = z.object({ - id: z.string(), - name: z.string().min(1), - description: z.string().optional().nullable(), - location: LocationSchema, - startDate: z.string().optional().nullable(), - endDate: z.string().optional().nullable(), - notes: z.string().optional().nullable(), - packId: z.string().optional().nullable(), - localCreatedAt: z.string().datetime(), - localUpdatedAt: z.string().datetime(), -}); - -const UpdateTripRequestSchema = CreateTripRequestSchema.partial(); - export const tripsRoutes = new Elysia({ prefix: '/trips' }) .use(authPlugin) @@ -45,13 +22,14 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) orderBy: (t) => t.createdAt, }); - return allTrips; + return z.array(TripSchema).parse(allTrips); } catch (error) { console.error('Error listing trips:', error); - return status(500, { error: 'Failed to list trips' }); + throw error; } }, { + response: { 200: z.array(TripSchema) }, isAuthenticated: true, detail: { tags: ['Trips'], @@ -68,8 +46,6 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) const db = createDb(); const data = body; - if (!data.id) return status(400, { error: 'Trip ID is required' }); - try { const [newTrip] = await db .insert(trips) @@ -89,7 +65,7 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) }) .returning(); - if (!newTrip) return status(400, { error: 'Failed to create trip' }); + if (!newTrip) throw new Error('Failed to create trip'); const tripWithPack = data.packId ? await db.query.trips.findFirst({ @@ -98,14 +74,15 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) }) : newTrip; - return tripWithPack; + return TripSchema.parse(tripWithPack ?? newTrip); } catch (error) { console.error('Error creating trip:', error); - return status(500, { error: 'Failed to create trip' }); + throw error; } }, { - body: CreateTripRequestSchema, + body: CreateTripBodySchema, + response: { 200: TripSchema }, isAuthenticated: true, detail: { tags: ['Trips'], @@ -126,11 +103,12 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) where: and(eq(trips.id, tripId), eq(trips.userId, user.userId)), with: { pack: true }, }); - if (!trip) return status(404, { error: 'Trip not found' }); - return trip; + if (!trip) throw new NotFoundError('Trip not found'); + return TripSchema.parse(trip); }, { params: z.object({ tripId: z.string() }), + response: { 200: TripSchema }, isAuthenticated: true, detail: { tags: ['Trips'], @@ -169,20 +147,21 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) .set(updateData) .where(and(eq(trips.id, tripId), eq(trips.userId, user.userId))); - const updatedTrip: Trip | undefined = await db.query.trips.findFirst({ + const updatedTrip = await db.query.trips.findFirst({ where: and(eq(trips.id, tripId), eq(trips.userId, user.userId)), }); - if (!updatedTrip) return status(404, { error: 'Trip not found' }); - return updatedTrip; + if (!updatedTrip) throw new NotFoundError('Trip not found'); + return TripSchema.parse(updatedTrip); } catch (error) { console.error('Error updating trip:', error); - return status(500, { error: 'Failed to update trip' }); + throw error; } }, { params: z.object({ tripId: z.string() }), - body: UpdateTripRequestSchema, + body: UpdateTripBodySchema, + response: { 200: TripSchema }, isAuthenticated: true, detail: { tags: ['Trips'], @@ -204,15 +183,15 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) where: eq(trips.id, tripId), }); - if (!trip) return status(404, { error: 'Trip not found' }); - if (trip.userId !== user.userId) return status(403, { error: 'Forbidden' }); + if (!trip) throw new NotFoundError('Trip not found'); + if (trip.userId !== user.userId) throw new Error('Forbidden'); await db.delete(trips).where(eq(trips.id, tripId)); return { success: true }; } catch (error) { console.error('Error deleting trip:', error); - return status(500, { error: 'Failed to delete trip' }); + throw error; } }, { diff --git a/packages/api/src/routes/upload.ts b/packages/api/src/routes/upload.ts index 87558dc09c..c2118a5aac 100644 --- a/packages/api/src/routes/upload.ts +++ b/packages/api/src/routes/upload.ts @@ -1,9 +1,12 @@ import { PutObjectCommand } from '@aws-sdk/client-s3'; import { authPlugin } from '@packrat/api/middleware/auth'; -import { PresignedUploadQuerySchema } from '@packrat/api/schemas/upload'; +import { + PresignedUploadQuerySchema, + PresignedUploadResponseSchema, +} from '@packrat/api/schemas/upload'; import { getEnv } from '@packrat/api/utils/env-validation'; import { getPresignedUrl } from '@packrat/api/utils/getPresignedUrl'; -import { Elysia, status } from 'elysia'; +import { Elysia } from 'elysia'; const ALLOWED_IMAGE_TYPES = [ 'image/jpeg', @@ -24,25 +27,25 @@ export const uploadRoutes = new Elysia({ prefix: '/upload' }).use(authPlugin).ge const { fileName, contentType, size } = query; if (!fileName || !contentType) { - return status(400, { error: 'fileName and contentType are required' }); + throw new Error('fileName and contentType are required'); } // Validate content type - only allow images if (!ALLOWED_IMAGE_TYPES.includes(contentType.toLowerCase())) { - return status(400, { error: 'Invalid content type. Only image files are allowed.' }); + throw new Error('Invalid content type. Only image files are allowed.'); } // Validate file size - max 10MB if (size) { const fileSize = Number.parseInt(String(size), 10); if (Number.isNaN(fileSize) || fileSize <= 0 || fileSize > MAX_FILE_SIZE) { - return status(400, { error: 'File size must be greater than 0 and not exceed 10MB' }); + throw new Error('File size must be greater than 0 and not exceed 10MB'); } } // Security check: Ensure the filename starts with the user's ID if (!fileName.startsWith(`${user.userId}-`)) { - return status(403, { error: 'Unauthorized' }); + throw new Error('Unauthorized'); } const command = new PutObjectCommand({ @@ -65,14 +68,15 @@ export const uploadRoutes = new Elysia({ prefix: '/upload' }).use(authPlugin).ge } })(); - return { + return PresignedUploadResponseSchema.parse({ url: presignedUrl, objectKey: fileName, publicUrl, - }; + }); }, { query: PresignedUploadQuerySchema, + response: { 200: PresignedUploadResponseSchema }, isAuthenticated: true, detail: { tags: ['Upload'], diff --git a/packages/api/src/routes/user/index.ts b/packages/api/src/routes/user/index.ts index 8c1d1b9522..af74f6bf8a 100644 --- a/packages/api/src/routes/user/index.ts +++ b/packages/api/src/routes/user/index.ts @@ -1,9 +1,13 @@ import { createDb } from '@packrat/api/db'; import { users } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; -import { UpdateUserRequestSchema } from '@packrat/api/schemas/users'; +import { + UpdateUserRequestSchema, + UpdateUserResponseSchema, + UserProfileSchema, +} from '@packrat/api/schemas/users'; import { eq } from 'drizzle-orm'; -import { Elysia, status } from 'elysia'; +import { Elysia, NotFoundError } from 'elysia'; export const userRoutes = new Elysia({ prefix: '/user' }) .use(authPlugin) @@ -31,23 +35,24 @@ export const userRoutes = new Elysia({ prefix: '/user' }) .limit(1); if (!userRecord) { - return status(404, { error: 'User not found', code: 'USER_NOT_FOUND' }); + throw new NotFoundError('User not found'); } - return { + return UserProfileSchema.parse({ success: true, user: { ...userRecord, createdAt: userRecord.createdAt?.toISOString() || null, updatedAt: userRecord.updatedAt?.toISOString() || null, }, - }; + }); } catch (error) { console.error('Error fetching user profile:', error); - return status(500, { error: 'Failed to fetch user profile', code: 'FETCH_ERROR' }); + throw error; } }, { + response: { 200: UserProfileSchema }, isAuthenticated: true, detail: { tags: ['Users'], summary: 'Get user profile', security: [{ bearerAuth: [] }] }, }, @@ -69,10 +74,7 @@ export const userRoutes = new Elysia({ prefix: '/user' }) .limit(1); if (existingUser && existingUser.id !== user.userId) { - return status(409, { - error: 'Email already in use by another user', - code: 'EMAIL_CONFLICT', - }); + throw new Error('Email already in use by another user'); } } @@ -94,14 +96,14 @@ export const userRoutes = new Elysia({ prefix: '/user' }) .returning(); if (!updatedUser) { - return status(404, { error: 'User not found', code: 'USER_NOT_FOUND' }); + throw new NotFoundError('User not found'); } const message = email ? 'Profile updated successfully. Please verify your new email address.' : 'Profile updated successfully'; - return { + return UpdateUserResponseSchema.parse({ success: true, message, user: { @@ -115,14 +117,15 @@ export const userRoutes = new Elysia({ prefix: '/user' }) createdAt: updatedUser.createdAt?.toISOString() || null, updatedAt: updatedUser.updatedAt?.toISOString() || null, }, - }; + }); } catch (error) { console.error('Error updating user profile:', error); - return status(500, { error: 'Failed to update user profile', code: 'UPDATE_ERROR' }); + throw error; } }, { body: UpdateUserRequestSchema, + response: { 200: UpdateUserResponseSchema }, isAuthenticated: true, detail: { tags: ['Users'], summary: 'Update user profile', security: [{ bearerAuth: [] }] }, }, diff --git a/packages/api/src/routes/weather.ts b/packages/api/src/routes/weather.ts index 1574f24ab8..f615d3181c 100644 --- a/packages/api/src/routes/weather.ts +++ b/packages/api/src/routes/weather.ts @@ -2,6 +2,7 @@ import { authPlugin } from '@packrat/api/middleware/auth'; import { type WeatherAPICurrentResponse, type WeatherAPIForecastResponse, + WeatherAPIForecastResponseSchema, type WeatherAPISearchResponse, WeatherCoordinateQuerySchema, WeatherLocationIdSchema, @@ -133,7 +134,7 @@ export const weatherRoutes = new Elysia({ prefix: '/weather' }) const id = Number(idParam); if (!idParam || Number.isNaN(id)) { - return status(400, { error: 'Valid location ID is required' }); + throw new Error('Valid location ID is required'); } try { @@ -144,23 +145,21 @@ export const weatherRoutes = new Elysia({ prefix: '/weather' }) if (!response.ok) throw new Error(`API error: ${response.status}`); const data = (await response.json()) as WeatherAPIForecastResponse; // safe-cast: WeatherAPI.com response shape matches this type - return { + return WeatherAPIForecastResponseSchema.parse({ ...data, location: { ...data.location, id: Number(id), }, - }; + }); } catch (error) { console.error('Error fetching weather forecast:', error); - return status(500, { - error: 'Internal server error', - code: 'WEATHER_FORECAST_ERROR', - }); + throw error; } }, { query: WeatherLocationIdSchema, + response: { 200: WeatherAPIForecastResponseSchema }, isAuthenticated: true, detail: { tags: ['Weather'], diff --git a/packages/api/src/schemas/guides.ts b/packages/api/src/schemas/guides.ts index 25569e8dc9..3450de133c 100644 --- a/packages/api/src/schemas/guides.ts +++ b/packages/api/src/schemas/guides.ts @@ -59,3 +59,8 @@ export const GuideSearchResponseSchema = z.object({ totalPages: z.number(), query: z.string(), }); + +export const GuideCategoriesResponseSchema = z.object({ + categories: z.array(z.string()), + count: z.number().int(), +}); diff --git a/packages/api/src/schemas/packs.ts b/packages/api/src/schemas/packs.ts index 3414d227da..1ba07c3b72 100644 --- a/packages/api/src/schemas/packs.ts +++ b/packages/api/src/schemas/packs.ts @@ -1,6 +1,7 @@ import { PACK_CATEGORIES, WEIGHT_UNITS } from '@packrat/api/types'; import { z } from 'zod'; +// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. const datetimeString = z.preprocess( (v) => (v instanceof Date ? v.toISOString() : v), z.string().datetime(), diff --git a/packages/api/src/schemas/upload.ts b/packages/api/src/schemas/upload.ts index 04a5b5eb1e..4c11fa8048 100644 --- a/packages/api/src/schemas/upload.ts +++ b/packages/api/src/schemas/upload.ts @@ -12,5 +12,7 @@ export const PresignedUploadQuerySchema = z.object({ }); export const PresignedUploadResponseSchema = z.object({ - url: z.string().url(), + url: z.string(), + objectKey: z.string(), + publicUrl: z.string(), }); From 4464646f4816e5909a9c466ac44543d2b1958cea Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 00:04:45 -0600 Subject: [PATCH 26/51] refactor(api): extract constants to types/constants.ts and slim down types/index.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move enum constants, Zod schemas, and inferred types to a dedicated constants.ts file. Convert types/index.ts to a pure re-export barrel. Update all api-internal importers to use @packrat/api/types/constants directly. Expo app imports continue working via the re-export barrel. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/db/schema.ts | 2 +- packages/api/src/schemas/catalog.ts | 2 +- packages/api/src/schemas/packs.ts | 2 +- packages/api/src/services/packService.ts | 2 +- packages/api/src/types/constants.ts | 183 +++++++++++++++++ packages/api/src/types/index.ts | 188 +----------------- .../utils/__tests__/itemCalculations.test.ts | 2 +- .../api/src/utils/__tests__/weight.test.ts | 2 +- packages/api/src/utils/itemCalculations.ts | 2 +- packages/api/src/utils/weight.ts | 2 +- 10 files changed, 192 insertions(+), 195 deletions(-) create mode 100644 packages/api/src/types/constants.ts diff --git a/packages/api/src/db/schema.ts b/packages/api/src/db/schema.ts index 3fad6511ef..f86d24cb1d 100644 --- a/packages/api/src/db/schema.ts +++ b/packages/api/src/db/schema.ts @@ -1,4 +1,4 @@ -import type { PackCategory, WeightUnit } from '@packrat/api/types'; +import type { PackCategory, WeightUnit } from '@packrat/api/types/constants'; import { type InferInsertModel, type InferSelectModel, relations, sql } from 'drizzle-orm'; import { type AnyPgColumn, diff --git a/packages/api/src/schemas/catalog.ts b/packages/api/src/schemas/catalog.ts index 7fcef4990b..e03c17e01a 100644 --- a/packages/api/src/schemas/catalog.ts +++ b/packages/api/src/schemas/catalog.ts @@ -1,4 +1,4 @@ -import { WEIGHT_UNITS } from '@packrat/api/types'; +import { WEIGHT_UNITS } from '@packrat/api/types/constants'; import { isString } from '@packrat/guards'; import { z } from 'zod'; diff --git a/packages/api/src/schemas/packs.ts b/packages/api/src/schemas/packs.ts index 1ba07c3b72..6f855887f7 100644 --- a/packages/api/src/schemas/packs.ts +++ b/packages/api/src/schemas/packs.ts @@ -1,4 +1,4 @@ -import { PACK_CATEGORIES, WEIGHT_UNITS } from '@packrat/api/types'; +import { PACK_CATEGORIES, WEIGHT_UNITS } from '@packrat/api/types/constants'; import { z } from 'zod'; // Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. diff --git a/packages/api/src/services/packService.ts b/packages/api/src/services/packService.ts index 8d4377882a..3c0ae68b3a 100644 --- a/packages/api/src/services/packService.ts +++ b/packages/api/src/services/packService.ts @@ -7,7 +7,7 @@ import { packItems, packs, } from '@packrat/api/db/schema'; -import { PACK_CATEGORIES } from '@packrat/api/types'; +import { PACK_CATEGORIES } from '@packrat/api/types/constants'; import { DEFAULT_MODELS } from '@packrat/api/utils/ai/models'; import { getEnv } from '@packrat/api/utils/env-validation'; import { generateObject } from 'ai'; diff --git a/packages/api/src/types/constants.ts b/packages/api/src/types/constants.ts new file mode 100644 index 0000000000..79c884d444 --- /dev/null +++ b/packages/api/src/types/constants.ts @@ -0,0 +1,183 @@ +import { z } from 'zod'; + +// --- Pack Category --- +export const PACK_CATEGORIES = Object.freeze([ + 'hiking', + 'backpacking', + 'camping', + 'climbing', + 'winter', + 'desert', + 'custom', + 'water sports', + 'skiing', +] as const); + +export const PackCategorySchema = z.enum(PACK_CATEGORIES); +export type PackCategory = z.infer; + +// --- Item Category --- +export const ITEM_CATEGORIES = Object.freeze([ + 'clothing', + 'shelter', + 'sleep', + 'kitchen', + 'water', + 'electronics', + 'first-aid', + 'navigation', + 'tools', + 'consumables', + 'miscellaneous', +] as const); + +export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); +export type ItemCategory = z.infer; + +// --- Weight Unit --- +export const WEIGHT_UNITS = Object.freeze(['g', 'oz', 'kg', 'lb'] as const); +export const WeightUnitSchema = z.enum(WEIGHT_UNITS); +export type WeightUnit = z.infer; + +// --- Availability --- +export const AVAILABILITY_VALUES = Object.freeze(['in_stock', 'out_of_stock', 'preorder'] as const); +export const AvailabilitySchema = z.enum(AVAILABILITY_VALUES); +export type Availability = z.infer; + +// --- Shared item types used by utility functions --- +export type ItemLink = { + id: string; + title: string; + url: string; + type: 'official' | 'review' | 'guide' | 'purchase' | 'other'; +}; + +export type ItemReview = { + id: string; + userId: string; + userName: string; + userAvatar?: string; + rating: number; + text: string; + date: string; + helpful?: number; + verified?: boolean; +}; + +// --- Lightweight pack/catalog types for utility functions --- +// These are simpler than the full API schemas and are used by +// calculation utilities (weight.ts, itemCalculations.ts). +export const CatalogItemSchema = z.object({ + id: z.number().int().positive(), + name: z.string(), + productUrl: z.string(), + sku: z.string(), + weight: z.number().nonnegative(), + weightUnit: z.string(), + description: z.string().optional(), + categories: z.array(z.string()).optional(), + images: z.array(z.string()).optional(), + brand: z.string().optional(), + model: z.string().optional(), + ratingValue: z.number().optional(), + color: z.string().optional(), + size: z.string().optional(), + price: z.number().optional(), + availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).optional(), + seller: z.string().optional(), + productSku: z.string().optional(), + material: z.string().optional(), + currency: z.string().optional(), + condition: z.string().optional(), + reviewCount: z.number().int().optional(), + variants: z + .array( + z.object({ + attribute: z.string(), + values: z.array(z.string()), + }), + ) + .optional(), + techs: z.record(z.string(), z.string()).optional(), + links: z + .array( + z.object({ + title: z.string(), + url: z.string(), + }), + ) + .optional(), + reviews: z + .array( + z.object({ + user_name: z.string(), + user_avatar: z.string().optional(), + context: z.record(z.string(), z.string()).optional(), + recommends: z.boolean().optional(), + rating: z.number(), + title: z.string(), + text: z.string(), + date: z.string(), + images: z.array(z.string()).optional(), + upvotes: z.number().optional(), + downvotes: z.number().optional(), + verified: z.boolean().optional(), + }), + ) + .optional(), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), +}); + +export type CatalogItem = z.infer; + +export const PackItemSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string().optional(), + weight: z.number().nonnegative(), + weightUnit: WeightUnitSchema, + quantity: z.number().int().positive(), + category: z.string(), + consumable: z.boolean(), + worn: z.boolean(), + image: z.string().url().optional(), + notes: z.string().optional(), + packId: z.string(), + catalogItemId: z.number().int().positive().optional(), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), + userId: z.number().int().positive(), +}); + +export type PackItem = z.infer; + +export const PackSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string().optional(), + category: PackCategorySchema, + baseWeight: z.number().nonnegative().optional(), + totalWeight: z.number().nonnegative().optional(), + items: z.array(PackItemSchema).optional(), + userId: z.number().int().positive(), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), + isPublic: z.boolean(), + image: z.string().url().optional(), + tags: z.array(z.string()).optional(), +}); + +export type Pack = z.infer; + +export const UserSchema = z.object({ + id: z.number().int().positive(), + name: z.string(), + email: z.string().email(), + avatar: z.string().url(), + experience: z.enum(['beginner', 'intermediate', 'expert']), + joinedAt: z.string().datetime(), + bio: z.string().optional(), +}); + +export type User = z.infer; diff --git a/packages/api/src/types/index.ts b/packages/api/src/types/index.ts index bb81848c97..c94f80f843 100644 --- a/packages/api/src/types/index.ts +++ b/packages/api/src/types/index.ts @@ -1,187 +1 @@ -import { z } from 'zod'; - -// --- User Schema --- -export const UserSchema = z.object({ - id: z.number().int().positive(), - name: z.string(), - email: z.string().email(), - avatar: z.string().url(), - experience: z.enum(['beginner', 'intermediate', 'expert']), - joinedAt: z.string().datetime(), - bio: z.string().optional(), -}); - -export type User = z.infer; - -// --- Pack Category Enum --- -export const PACK_CATEGORIES = Object.freeze([ - 'hiking', - 'backpacking', - 'camping', - 'climbing', - 'winter', - 'desert', - 'custom', - 'water sports', - 'skiing', -] as const); - -export const PackCategorySchema = z.enum(PACK_CATEGORIES); -export type PackCategory = z.infer; - -// --- Item Category Enum --- -export const ITEM_CATEGORIES = Object.freeze([ - 'clothing', - 'shelter', - 'sleep', - 'kitchen', - 'water', - 'electronics', - 'first-aid', - 'navigation', - 'tools', - 'consumables', - 'miscellaneous', -] as const); - -export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); -export type ItemCategory = z.infer; - -// --- Weight Unit Enum --- -export const WEIGHT_UNITS = Object.freeze(['g', 'oz', 'kg', 'lb'] as const); -export const WeightUnitSchema = z.enum(WEIGHT_UNITS); -export type WeightUnit = z.infer; - -// --- Availability Enum --- -export const AVAILABILITY_VALUES = Object.freeze(['in_stock', 'out_of_stock', 'preorder'] as const); -export const AvailabilitySchema = z.enum(AVAILABILITY_VALUES); -export type Availability = z.infer; - -export type ItemLink = { - id: string; - title: string; - url: string; - type: 'official' | 'review' | 'guide' | 'purchase' | 'other'; -}; - -export type ItemReview = { - id: string; - userId: string; - userName: string; - userAvatar?: string; - rating: number; - text: string; - date: string; - helpful?: number; - verified?: boolean; -}; - -// --- Catalog Item Schema --- -export const CatalogItemSchema = z.object({ - id: z.number().int().positive(), - name: z.string(), - productUrl: z.string(), - sku: z.string(), - weight: z.number().nonnegative(), - weightUnit: z.string(), - description: z.string().optional(), - categories: z.array(z.string()).optional(), - images: z.array(z.string()).optional(), - brand: z.string().optional(), - model: z.string().optional(), - ratingValue: z.number().optional(), - color: z.string().optional(), - size: z.string().optional(), - price: z.number().optional(), - availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).optional(), - seller: z.string().optional(), - productSku: z.string().optional(), - material: z.string().optional(), - currency: z.string().optional(), - condition: z.string().optional(), - reviewCount: z.number().int().optional(), - variants: z - .array( - z.object({ - attribute: z.string(), - values: z.array(z.string()), - }), - ) - .optional(), - techs: z.record(z.string(), z.string()).optional(), - links: z - .array( - z.object({ - title: z.string(), - url: z.string(), - }), - ) - .optional(), - reviews: z - .array( - z.object({ - user_name: z.string(), - user_avatar: z.string().optional(), - context: z.record(z.string(), z.string()).optional(), - recommends: z.boolean().optional(), - rating: z.number(), - title: z.string(), - text: z.string(), - date: z.string(), - images: z.array(z.string()).optional(), - upvotes: z.number().optional(), - downvotes: z.number().optional(), - verified: z.boolean().optional(), - }), - ) - .optional(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), -}); - -export type CatalogItem = z.infer; -// --- Pack Item Schema --- -export const PackItemSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().optional(), - weight: z.number().nonnegative(), - weightUnit: WeightUnitSchema, - quantity: z.number().int().positive(), - category: z.string(), - consumable: z.boolean(), - worn: z.boolean(), - image: z.string().url().optional(), - notes: z.string().optional(), - packId: z.string(), - catalogItemId: z.number().int().positive().optional(), // Reference to original catalog item - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - userId: z.number().int().positive(), -}); - -export type PackItem = z.infer; - -// --- Pack Schema --- -export const PackSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().optional(), - category: PackCategorySchema, - baseWeight: z.number().nonnegative().optional(), // Weight without consumables (computed) - totalWeight: z.number().nonnegative().optional(), // Total weight including consumables (computed) - items: z.array(PackItemSchema).optional(), - userId: z.number().int().positive(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - isPublic: z.boolean(), - image: z.string().url().optional(), - tags: z.array(z.string()).optional(), -}); - -export type Pack = z.infer; - -// --- Arrays for Mock Data Validation --- -export const UsersArraySchema = z.array(UserSchema); -export const PacksArraySchema = z.array(PackSchema); -export const PackItemsArraySchema = z.array(PackItemSchema); +export * from './constants'; diff --git a/packages/api/src/utils/__tests__/itemCalculations.test.ts b/packages/api/src/utils/__tests__/itemCalculations.test.ts index d9cd91ddb8..e7399c8b5f 100644 --- a/packages/api/src/utils/__tests__/itemCalculations.test.ts +++ b/packages/api/src/utils/__tests__/itemCalculations.test.ts @@ -1,4 +1,4 @@ -import type { CatalogItem, PackItem } from '@packrat/api/types'; +import type { CatalogItem, PackItem } from '@packrat/api/types/constants'; import { describe, expect, it } from 'vitest'; import { calculateTotalWeight, diff --git a/packages/api/src/utils/__tests__/weight.test.ts b/packages/api/src/utils/__tests__/weight.test.ts index 624e055ecd..2fb3a7bb3c 100644 --- a/packages/api/src/utils/__tests__/weight.test.ts +++ b/packages/api/src/utils/__tests__/weight.test.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/api/types'; +import type { PackItem } from '@packrat/api/types/constants'; import { describe, expect, it } from 'vitest'; import { calculateBaseWeight, diff --git a/packages/api/src/utils/itemCalculations.ts b/packages/api/src/utils/itemCalculations.ts index c664b9cf20..7871d1b83a 100644 --- a/packages/api/src/utils/itemCalculations.ts +++ b/packages/api/src/utils/itemCalculations.ts @@ -1,4 +1,4 @@ -import type { CatalogItem, PackItem, WeightUnit } from '@packrat/api/types'; +import type { CatalogItem, PackItem, WeightUnit } from '@packrat/api/types/constants'; /** * Checks if an item is a pack item diff --git a/packages/api/src/utils/weight.ts b/packages/api/src/utils/weight.ts index d3f8543067..223f922733 100644 --- a/packages/api/src/utils/weight.ts +++ b/packages/api/src/utils/weight.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/api/types'; +import type { PackItem } from '@packrat/api/types/constants'; import type { WeightUnit } from '@packrat/units'; import { convert, displayWeight, fromGrams, normalize, parseWeightUnit } from '@packrat/units'; From eaf2fb4fb72ce3a069d7389bfa2137c4a4879f1c Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 00:10:44 -0600 Subject: [PATCH 27/51] refactor(app): consolidate entity schemas to re-export from @packrat/api/schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace duplicated schema definitions in packages/app entities with direct re-exports from the canonical API schemas. Fix CatalogItemSchema to use datetimeString preprocess (consistent with PackItemSchema) so dates serialize to string instead of Date at the API boundary. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- packages/api/src/schemas/catalog.ts | 22 +++---- packages/app/src/entities/catalog/schema.ts | 47 +-------------- packages/app/src/entities/feed/schema.ts | 48 ++-------------- packages/app/src/entities/pack/schema.ts | 63 ++------------------- packages/app/src/entities/trip/schema.ts | 26 +-------- packages/app/src/entities/user/schema.ts | 20 +------ 6 files changed, 23 insertions(+), 203 deletions(-) diff --git a/packages/api/src/schemas/catalog.ts b/packages/api/src/schemas/catalog.ts index e03c17e01a..3b9aff1550 100644 --- a/packages/api/src/schemas/catalog.ts +++ b/packages/api/src/schemas/catalog.ts @@ -2,6 +2,12 @@ import { WEIGHT_UNITS } from '@packrat/api/types/constants'; import { isString } from '@packrat/guards'; import { z } from 'zod'; +// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. +const datetimeString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().datetime(), +); + export const ErrorResponseSchema = z.object({ error: z.string(), code: z.string().optional(), @@ -96,20 +102,8 @@ export const CatalogItemSchema = z.object({ .nullable() .optional(), usageCount: z.number().int().min(0).optional(), - createdAt: z.union([ - z.date(), - z - .string() - .datetime() - .transform((val) => new Date(val)), - ]), - updatedAt: z.union([ - z.date(), - z - .string() - .datetime() - .transform((val) => new Date(val)), - ]), + createdAt: datetimeString, + updatedAt: datetimeString, }); const SortSchema = z.object({ diff --git a/packages/app/src/entities/catalog/schema.ts b/packages/app/src/entities/catalog/schema.ts index c676ca5181..2b5c573e89 100644 --- a/packages/app/src/entities/catalog/schema.ts +++ b/packages/app/src/entities/catalog/schema.ts @@ -1,46 +1 @@ -import { z } from 'zod'; -import { dateField } from '../../shared/lib/date'; - -export const CatalogItemSchema = z.object({ - id: z.number().int().positive(), - name: z.string(), - productUrl: z.string(), - sku: z.string(), - weight: z.number(), - weightUnit: z.string(), - description: z.string().nullable(), - categories: z.array(z.string()).nullable(), - images: z.array(z.string()).nullable(), - brand: z.string().nullable(), - model: z.string().nullable(), - ratingValue: z.number().nullable(), - color: z.string().nullable(), - size: z.string().nullable(), - price: z.number().nullable(), - availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).nullable(), - seller: z.string().nullable(), - productSku: z.string().nullable(), - material: z.string().nullable(), - currency: z.string().nullable(), - condition: z.string().nullable(), - reviewCount: z.number().int().nullable(), - createdAt: dateField.optional(), - updatedAt: dateField.optional(), - variants: z - .array(z.object({ attribute: z.string(), values: z.array(z.string()) })) - .nullable() - .optional(), - techs: z.record(z.string(), z.string()).nullable().optional(), - links: z - .array(z.object({ title: z.string(), url: z.string() })) - .nullable() - .optional(), -}); - -export const CatalogItemsResponseSchema = z.object({ - items: z.array(CatalogItemSchema), - totalCount: z.number(), - page: z.number(), - limit: z.number(), - totalPages: z.number(), -}); +export { CatalogItemSchema, CatalogItemsResponseSchema } from '@packrat/api/schemas/catalog'; diff --git a/packages/app/src/entities/feed/schema.ts b/packages/app/src/entities/feed/schema.ts index 7d14eece86..b7258c5e30 100644 --- a/packages/app/src/entities/feed/schema.ts +++ b/packages/app/src/entities/feed/schema.ts @@ -1,42 +1,6 @@ -import { z } from 'zod'; -import { dateField } from '../../shared/lib/date'; - -export const PostAuthorSchema = z.object({ - id: z.number().int(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), -}); - -export const PostSchema = z.object({ - id: z.number().int(), - userId: z.number().int(), - caption: z.string().nullable(), - images: z.array(z.string()), - createdAt: dateField, - updatedAt: dateField, - author: PostAuthorSchema.optional(), - likeCount: z.number().int(), - commentCount: z.number().int(), - likedByMe: z.boolean(), -}); - -export const FeedResponseSchema = z.object({ - items: z.array(PostSchema), - page: z.number().int(), - limit: z.number().int(), - total: z.number().int(), - totalPages: z.number().int(), -}); - -export const CommentSchema = z.object({ - id: z.number().int(), - postId: z.number().int(), - userId: z.number().int(), - content: z.string(), - parentCommentId: z.number().int().nullable(), - createdAt: dateField, - updatedAt: dateField, - author: PostAuthorSchema.optional(), - likeCount: z.number().int(), - likedByMe: z.boolean(), -}); +export { + CommentSchema, + FeedResponseSchema, + PostAuthorSchema, + PostSchema, +} from '@packrat/api/schemas/feed'; diff --git a/packages/app/src/entities/pack/schema.ts b/packages/app/src/entities/pack/schema.ts index 66351866f4..340b958276 100644 --- a/packages/app/src/entities/pack/schema.ts +++ b/packages/app/src/entities/pack/schema.ts @@ -1,57 +1,6 @@ -import { z } from 'zod'; -import { dateField } from '../../shared/lib/date'; - -export const PackItemSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().nullable(), - weight: z.number(), - weightUnit: z.string(), - quantity: z.number().int().min(1), - category: z.string().nullable(), - consumable: z.boolean(), - worn: z.boolean(), - image: z.string().nullable(), - notes: z.string().nullable(), - packId: z.string(), - catalogItemId: z.number().int().nullable(), - userId: z.number().int(), - deleted: z.boolean(), - isAIGenerated: z.boolean(), - templateItemId: z.string().nullable(), - createdAt: dateField, - updatedAt: dateField, -}); - -export const PackSchema = z.object({ - id: z.string(), - userId: z.number(), - name: z.string(), - description: z.string().nullable(), - category: z.string().nullable(), - isPublic: z.boolean(), - image: z.string().nullable(), - tags: z.array(z.string()).nullable(), - templateId: z.string().nullable().optional(), - deleted: z.boolean(), - isAIGenerated: z.boolean(), - localCreatedAt: dateField.optional(), - localUpdatedAt: dateField.optional(), - createdAt: dateField, - updatedAt: dateField, - items: z.array(PackItemSchema).optional(), -}); - -// totalWeight and baseWeight are computed server-side, not locally derived -export const PackWithWeightsSchema = PackSchema.extend({ - totalWeight: z.number(), - baseWeight: z.number(), -}); - -export const PackListResponseSchema = z.object({ - packs: z.array(PackWithWeightsSchema), - total: z.number(), - page: z.number(), - limit: z.number(), - totalPages: z.number(), -}); +export { + PackItemSchema, + PackListResponseSchema, + PackSchema, + PackWithWeightsSchema, +} from '@packrat/api/schemas/packs'; diff --git a/packages/app/src/entities/trip/schema.ts b/packages/app/src/entities/trip/schema.ts index 33959e68e6..93ede99836 100644 --- a/packages/app/src/entities/trip/schema.ts +++ b/packages/app/src/entities/trip/schema.ts @@ -1,25 +1 @@ -import { z } from 'zod'; -import { dateField } from '../../shared/lib/date'; - -export const TripLocationSchema = z.object({ - latitude: z.number(), - longitude: z.number(), - name: z.string().optional(), -}); - -export const TripSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().nullable().optional(), - notes: z.string().nullable().optional(), - location: TripLocationSchema.nullable().optional(), - startDate: z.string().nullable().optional(), - endDate: z.string().nullable().optional(), - userId: z.number().optional(), - packId: z.string().nullable().optional(), - deleted: z.boolean(), - localCreatedAt: dateField.optional(), - localUpdatedAt: dateField.optional(), - createdAt: dateField.optional(), - updatedAt: dateField.optional(), -}); +export { TripLocationSchema, TripSchema } from '@packrat/api/schemas/trips'; diff --git a/packages/app/src/entities/user/schema.ts b/packages/app/src/entities/user/schema.ts index 75dcc2f724..74fb9b0a46 100644 --- a/packages/app/src/entities/user/schema.ts +++ b/packages/app/src/entities/user/schema.ts @@ -1,19 +1 @@ -import { z } from 'zod'; -import { dateField } from '../../shared/lib/date'; - -export const UserSchema = z.object({ - id: z.number().int().positive(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), - role: z.string().nullable().default('USER'), - emailVerified: z.boolean().nullable(), - createdAt: dateField.nullable(), - updatedAt: dateField.nullable(), - avatarUrl: z.string().nullable().optional(), -}); - -export const UserProfileSchema = z.object({ - success: z.boolean(), - user: UserSchema, -}); +export { UserProfileSchema, UserSchema } from '@packrat/api/schemas/users'; From 95ac90825d5fbe4787a6e9867bbc8d96858ead1f Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 00:16:54 -0600 Subject: [PATCH 28/51] fix(catalog): strip embedding from responses and use admin macro for templates - catalog POST/PUT: parse through CatalogItemSchema before returning, stripping the embedding vector from API responses and adding response: { 200: Schema } for Eden Treaty type inference - catalog POST/PUT: convert return status() error paths to throws so handler return type matches the declared response schema - packTemplates generate-from-online-content: switch from manual role check to adminAuthPlugin macro (isAdmin: true) for consistent access control --- packages/api/src/routes/catalog/index.ts | 20 ++++++++++--------- .../api/src/routes/packTemplates/index.ts | 11 ++++------ 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/api/src/routes/catalog/index.ts b/packages/api/src/routes/catalog/index.ts index 66bdf9dffd..a6f5eb9549 100644 --- a/packages/api/src/routes/catalog/index.ts +++ b/packages/api/src/routes/catalog/index.ts @@ -247,16 +247,16 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) const db = createDb(); const data = body; if (!data.name || data.weight === undefined || data.weight === null || !data.weightUnit) { - return status(400, { error: 'name, weight, and weightUnit are required' }); + throw new Error('name, weight, and weightUnit are required'); } if (data.weight <= 0) { - return status(400, { error: 'weight must be a positive number' }); + throw new Error('weight must be a positive number'); } const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); if (!OPENAI_API_KEY) { - return status(500, { error: 'OpenAI API key not configured' }); + throw new Error('OpenAI API key not configured'); } const embeddingText = getEmbeddingText(data); @@ -299,10 +299,11 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) }) .returning(); - return newItem; + return CatalogItemSchema.parse(newItem); }, { body: CreateCatalogItemRequestSchema, + response: { 200: CatalogItemSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], @@ -435,17 +436,17 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) itemId <= 0 || itemId > 2147483647 ) { - return status(404, { error: 'Catalog item not found' }); + throw new NotFoundError('Catalog item not found'); } const data = body; if (data.weight !== undefined && data.weight !== null && data.weight <= 0) { - return status(400, { error: 'weight must be a positive number' }); + throw new Error('weight must be a positive number'); } const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); if (!OPENAI_API_KEY) { - return status(500, { error: 'OpenAI API key not configured' }); + throw new Error('OpenAI API key not configured'); } const existingItem = await db.query.catalogItems.findFirst({ @@ -453,7 +454,7 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) }); if (!existingItem) { - return status(404, { error: 'Catalog item not found' }); + throw new NotFoundError('Catalog item not found'); } let embedding: number[] | null = null; @@ -481,11 +482,12 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) .where(eq(catalogItems.id, itemId)) .returning(); - return updatedItem; + return CatalogItemSchema.parse(updatedItem); }, { params: z.object({ id: z.string() }), body: UpdateCatalogItemRequestSchema, + response: { 200: CatalogItemSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], diff --git a/packages/api/src/routes/packTemplates/index.ts b/packages/api/src/routes/packTemplates/index.ts index 56fbab9342..a9e1dae0a8 100644 --- a/packages/api/src/routes/packTemplates/index.ts +++ b/packages/api/src/routes/packTemplates/index.ts @@ -2,7 +2,7 @@ import { createGoogleGenerativeAI } from '@ai-sdk/google'; import { getContainer } from '@cloudflare/containers'; import { createDb } from '@packrat/api/db'; import { type PackTemplate, packTemplateItems, packTemplates } from '@packrat/api/db/schema'; -import { authPlugin } from '@packrat/api/middleware/auth'; +import { adminAuthPlugin, authPlugin } from '@packrat/api/middleware/auth'; import { CreatePackTemplateItemRequestSchema, CreatePackTemplateRequestSchema, @@ -140,6 +140,7 @@ const analysisSchema = z.object({ export const packTemplatesRoutes = new Elysia({ prefix: '/pack-templates' }) .use(authPlugin) + .use(adminAuthPlugin) // List all templates .get( @@ -213,10 +214,6 @@ export const packTemplatesRoutes = new Elysia({ prefix: '/pack-templates' }) async ({ body, user }) => { let contentUrl: string | undefined; try { - if (user.role !== 'ADMIN') { - return status(403, { error: 'Forbidden: Admin access required' }); - } - const { isAppTemplate } = body; contentUrl = body.contentUrl; @@ -413,10 +410,10 @@ export const packTemplatesRoutes = new Elysia({ prefix: '/pack-templates' }) }, { body: GenerateFromOnlineContentRequestSchema, - isAuthenticated: true, + isAdmin: true, detail: { tags: ['Pack Templates'], - summary: 'Generate a pack template from an online content URL', + summary: 'Generate a pack template from an online content URL (Admin only)', security: [{ bearerAuth: [] }], }, }, From 0b9404c10f4bdade4cc822b692a5bf4ebdb2be37 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Wed, 13 May 2026 00:20:33 -0600 Subject: [PATCH 29/51] refactor(expo): delete duplicate types/index.ts, redirect imports to API constants Delete apps/expo/types/index.ts which duplicated type definitions already in @packrat/api/types/constants. Redirect all 15 importing files to the canonical source so there is one definition for PackItem, Pack, User, WeightUnit, and PackCategory across the codebase. Also fix constants.ts utility schemas: userId fields were z.number() but better-auth uses string UUIDs; update to z.string() and fix corresponding test mock data. --- apps/expo/app/(app)/current-pack/[id].tsx | 2 +- apps/expo/components/initial/UserAvatar.tsx | 2 +- apps/expo/components/initial/WeightBadge.tsx | 2 +- apps/expo/data/mockData.ts | 2 +- .../pack-templates/packTemplateListAtoms.ts | 2 +- .../screens/CreatePackTemplateItemForm.tsx | 2 +- .../packs/components/TemplateItemsSection.tsx | 2 +- apps/expo/features/packs/input.ts | 2 +- apps/expo/features/packs/packListAtoms.ts | 2 +- .../packs/screens/CreatePackItemForm.tsx | 2 +- apps/expo/features/packs/types.ts | 2 +- .../lib/utils/__tests__/compute-pack.test.ts | 2 +- apps/expo/lib/utils/compute-pack.ts | 2 +- apps/expo/types/index.ts | 73 ------------------- apps/expo/utils/__tests__/weight.test.ts | 2 +- apps/expo/utils/weight.ts | 2 +- packages/api/src/types/constants.ts | 6 +- .../utils/__tests__/itemCalculations.test.ts | 2 +- .../api/src/utils/__tests__/weight.test.ts | 2 +- 19 files changed, 20 insertions(+), 93 deletions(-) delete mode 100644 apps/expo/types/index.ts diff --git a/apps/expo/app/(app)/current-pack/[id].tsx b/apps/expo/app/(app)/current-pack/[id].tsx index db114c3fe7..0aabb315b2 100644 --- a/apps/expo/app/(app)/current-pack/[id].tsx +++ b/apps/expo/app/(app)/current-pack/[id].tsx @@ -1,3 +1,4 @@ +import type { PackItem } from '@packrat/api/types/constants'; import { Avatar, AvatarFallback, @@ -12,7 +13,6 @@ import { cn } from 'expo-app/lib/cn'; import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme'; import { useTranslation } from 'expo-app/lib/hooks/useTranslation'; import { getRelativeTime } from 'expo-app/lib/utils/getRelativeTime'; -import type { PackItem } from 'expo-app/types'; import { useLocalSearchParams } from 'expo-router'; import type React from 'react'; import { ScrollView, View } from 'react-native'; diff --git a/apps/expo/components/initial/UserAvatar.tsx b/apps/expo/components/initial/UserAvatar.tsx index 7ced670e65..29cb3d58d5 100644 --- a/apps/expo/components/initial/UserAvatar.tsx +++ b/apps/expo/components/initial/UserAvatar.tsx @@ -1,4 +1,4 @@ -import type { User } from 'expo-app/types'; +import type { User } from '@packrat/api/types/constants'; import { Image, Text, View } from 'react-native'; type UserAvatarProps = { diff --git a/apps/expo/components/initial/WeightBadge.tsx b/apps/expo/components/initial/WeightBadge.tsx index 83c5326a36..652e290be7 100644 --- a/apps/expo/components/initial/WeightBadge.tsx +++ b/apps/expo/components/initial/WeightBadge.tsx @@ -1,6 +1,6 @@ +import type { WeightUnit } from '@packrat/api/types/constants'; import { isString } from '@packrat/guards'; import { cn } from 'expo-app/lib/cn'; -import type { WeightUnit } from 'expo-app/types'; import { formatWeight } from 'expo-app/utils/weight'; import { Text, View } from 'react-native'; diff --git a/apps/expo/data/mockData.ts b/apps/expo/data/mockData.ts index f39d3619e8..09e8e8ee25 100644 --- a/apps/expo/data/mockData.ts +++ b/apps/expo/data/mockData.ts @@ -1,4 +1,4 @@ -import type { User } from 'expo-app/types'; +import type { User } from '@packrat/api/types/constants'; // --- Users --- export const mockUsers: [User, ...User[]] = [ diff --git a/apps/expo/features/pack-templates/packTemplateListAtoms.ts b/apps/expo/features/pack-templates/packTemplateListAtoms.ts index 293d6d3ce0..c26db60cc5 100644 --- a/apps/expo/features/pack-templates/packTemplateListAtoms.ts +++ b/apps/expo/features/pack-templates/packTemplateListAtoms.ts @@ -1,4 +1,4 @@ -import type { PackCategory } from 'expo-app/types'; +import type { PackCategory } from '@packrat/api/types/constants'; import { atom } from 'jotai'; export const activeTemplateFilterAtom = atom('all'); diff --git a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx b/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx index 615d0d2e98..1b976c7d3b 100644 --- a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx +++ b/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx @@ -1,6 +1,7 @@ // CreatePackTemplateItemForm.tsx import { useActionSheet } from '@expo/react-native-action-sheet'; +import type { WeightUnit } from '@packrat/api/types/constants'; import { safeIndexOf } from '@packrat/guards'; import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui'; import { useForm } from '@tanstack/react-form'; @@ -9,7 +10,6 @@ import { useImagePicker } from 'expo-app/features/packs/hooks/useImagePicker'; import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme'; import { useTranslation } from 'expo-app/lib/hooks/useTranslation'; import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager'; -import type { WeightUnit } from 'expo-app/types'; import { useRouter } from 'expo-router'; import { useMemo, useRef, useState } from 'react'; import { Alert, Image, Pressable, Switch, Text, TouchableOpacity, View } from 'react-native'; diff --git a/apps/expo/features/packs/components/TemplateItemsSection.tsx b/apps/expo/features/packs/components/TemplateItemsSection.tsx index 41823d6e60..312e10e689 100644 --- a/apps/expo/features/packs/components/TemplateItemsSection.tsx +++ b/apps/expo/features/packs/components/TemplateItemsSection.tsx @@ -1,9 +1,9 @@ +import type { WeightUnit } from '@packrat/api/types/constants'; import { Icon } from 'expo-app/components/Icon'; import { cn } from 'expo-app/lib/cn'; import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme'; import { useTranslation } from 'expo-app/lib/hooks/useTranslation'; import { buildPackTemplateItemImageUrl } from 'expo-app/lib/utils/buildPackTemplateItemImageUrl'; -import type { WeightUnit } from 'expo-app/types'; import { Image, ScrollView, Text, View } from 'react-native'; export interface PackTemplateItem { diff --git a/apps/expo/features/packs/input.ts b/apps/expo/features/packs/input.ts index 806351be95..557c53cb18 100644 --- a/apps/expo/features/packs/input.ts +++ b/apps/expo/features/packs/input.ts @@ -1,4 +1,4 @@ -import type { WeightUnit } from 'expo-app/types'; +import type { WeightUnit } from '@packrat/api/types/constants'; export interface PackItemInput { name: string; diff --git a/apps/expo/features/packs/packListAtoms.ts b/apps/expo/features/packs/packListAtoms.ts index daf48bb44b..cd83839fd6 100644 --- a/apps/expo/features/packs/packListAtoms.ts +++ b/apps/expo/features/packs/packListAtoms.ts @@ -1,4 +1,4 @@ -import type { PackCategory } from 'expo-app/types'; +import type { PackCategory } from '@packrat/api/types/constants'; import { atom } from 'jotai'; export const activeFilterAtom = atom('all'); diff --git a/apps/expo/features/packs/screens/CreatePackItemForm.tsx b/apps/expo/features/packs/screens/CreatePackItemForm.tsx index 9f0dff53d7..aeab94e8d0 100644 --- a/apps/expo/features/packs/screens/CreatePackItemForm.tsx +++ b/apps/expo/features/packs/screens/CreatePackItemForm.tsx @@ -1,4 +1,5 @@ import { useActionSheet } from '@expo/react-native-action-sheet'; +import type { WeightUnit } from '@packrat/api/types/constants'; import { safeIndexOf } from '@packrat/guards'; import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui'; import { useForm } from '@tanstack/react-form'; @@ -7,7 +8,6 @@ import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme'; import { useTranslation } from 'expo-app/lib/hooks/useTranslation'; import { testIds } from 'expo-app/lib/testIds'; import ImageCacheManager from 'expo-app/lib/utils/ImageCacheManager'; -import type { WeightUnit } from 'expo-app/types'; import { useRouter } from 'expo-router'; import { useEffect, useMemo, useRef, useState } from 'react'; import { Alert, Image, Pressable, Switch, Text, TouchableOpacity, View } from 'react-native'; diff --git a/apps/expo/features/packs/types.ts b/apps/expo/features/packs/types.ts index 9b6f433895..dc1f4f0129 100644 --- a/apps/expo/features/packs/types.ts +++ b/apps/expo/features/packs/types.ts @@ -1,6 +1,6 @@ +import type { PackCategory, WeightUnit } from '@packrat/api/types/constants'; import type { CatalogItem } from 'expo-app/features/catalog/types'; import type { PackTemplateItem } from 'expo-app/features/pack-templates/types'; -import type { PackCategory, WeightUnit } from 'expo-app/types'; export type { PackCategory, WeightUnit }; diff --git a/apps/expo/lib/utils/__tests__/compute-pack.test.ts b/apps/expo/lib/utils/__tests__/compute-pack.test.ts index 90825eac36..1b97ab0a31 100644 --- a/apps/expo/lib/utils/__tests__/compute-pack.test.ts +++ b/apps/expo/lib/utils/__tests__/compute-pack.test.ts @@ -1,4 +1,4 @@ -import type { Pack, PackItem } from 'expo-app/types'; +import type { Pack, PackItem } from '@packrat/api/types/constants'; import { describe, expect, it } from 'vitest'; import { computePacksWeights, computePackWeights } from '../compute-pack'; diff --git a/apps/expo/lib/utils/compute-pack.ts b/apps/expo/lib/utils/compute-pack.ts index e1e16c324c..ed31547406 100644 --- a/apps/expo/lib/utils/compute-pack.ts +++ b/apps/expo/lib/utils/compute-pack.ts @@ -1,6 +1,6 @@ +import type { Pack } from '@packrat/api/types/constants'; import type { WeightUnit } from '@packrat/units'; import { displayWeight, normalize, parseWeightUnit } from '@packrat/units'; -import type { Pack } from 'expo-app/types'; export const computePackWeights = (pack: Pack, preferredUnit: WeightUnit = 'g'): Pack => { if (!pack.items) { diff --git a/apps/expo/types/index.ts b/apps/expo/types/index.ts deleted file mode 100644 index 7e0084add1..0000000000 --- a/apps/expo/types/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ITEM_CATEGORIES, PACK_CATEGORIES, WEIGHT_UNITS } from '@packrat/api/types'; -import { z } from 'zod'; - -// --- User Schema --- -export const UserSchema = z.object({ - id: z.string(), - name: z.string(), - email: z.string().email(), - avatar: z.string().url(), - experience: z.enum(['beginner', 'intermediate', 'expert']), - joinedAt: z.string().datetime(), - bio: z.string().optional(), -}); - -export type User = z.infer; - -// --- Pack Category Enum --- -export const PackCategorySchema = z.enum(PACK_CATEGORIES); -export type PackCategory = z.infer; - -// --- Item Category Enum --- -export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); -export type ItemCategory = z.infer; - -// --- Weight Unit Enum --- -export const WeightUnitSchema = z.enum(WEIGHT_UNITS); -export type WeightUnit = z.infer; - -// --- Pack Item Schema --- -export const PackItemSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().optional(), - weight: z.number().nonnegative(), - weightUnit: WeightUnitSchema, - quantity: z.number().int().positive(), - category: z.string(), - consumable: z.boolean(), - worn: z.boolean(), - image: z.string().url().optional(), - notes: z.string().optional(), - packId: z.string(), - catalogItemId: z.string().optional(), // Reference to original catalog item - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - userId: z.string(), -}); - -export type PackItem = z.infer; - -// --- Pack Schema --- -export const PackSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().optional(), - category: PackCategorySchema, - baseWeight: z.number().nonnegative().optional(), // Weight without consumables (computed) - totalWeight: z.number().nonnegative().optional(), // Total weight including consumables (computed) - items: z.array(PackItemSchema).optional(), - userId: z.string(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - isPublic: z.boolean(), - image: z.string().url().optional(), - tags: z.array(z.string()).optional(), -}); - -export type Pack = z.infer; - -// --- Arrays for Mock Data Validation --- -export const UsersArraySchema = z.array(UserSchema); -export const PacksArraySchema = z.array(PackSchema); -export const PackItemsArraySchema = z.array(PackItemSchema); diff --git a/apps/expo/utils/__tests__/weight.test.ts b/apps/expo/utils/__tests__/weight.test.ts index ab4af15c27..0311af92c7 100644 --- a/apps/expo/utils/__tests__/weight.test.ts +++ b/apps/expo/utils/__tests__/weight.test.ts @@ -1,4 +1,4 @@ -import type { PackItem } from 'expo-app/types'; +import type { PackItem } from '@packrat/api/types/constants'; import { describe, expect, it } from 'vitest'; import { calculateBaseWeight, calculateTotalWeight, convertWeight, formatWeight } from '../weight'; diff --git a/apps/expo/utils/weight.ts b/apps/expo/utils/weight.ts index a915c0f6b5..33bb474974 100644 --- a/apps/expo/utils/weight.ts +++ b/apps/expo/utils/weight.ts @@ -1,6 +1,6 @@ +import type { PackItem } from '@packrat/api/types/constants'; import type { WeightUnit } from '@packrat/units'; import { convert, displayWeight, normalize, parseWeightUnit } from '@packrat/units'; -import type { PackItem } from 'expo-app/types'; export { convert as convertWeight }; diff --git a/packages/api/src/types/constants.ts b/packages/api/src/types/constants.ts index 79c884d444..7ab332b4f5 100644 --- a/packages/api/src/types/constants.ts +++ b/packages/api/src/types/constants.ts @@ -147,7 +147,7 @@ export const PackItemSchema = z.object({ catalogItemId: z.number().int().positive().optional(), createdAt: z.string().datetime(), updatedAt: z.string().datetime(), - userId: z.number().int().positive(), + userId: z.string(), }); export type PackItem = z.infer; @@ -160,7 +160,7 @@ export const PackSchema = z.object({ baseWeight: z.number().nonnegative().optional(), totalWeight: z.number().nonnegative().optional(), items: z.array(PackItemSchema).optional(), - userId: z.number().int().positive(), + userId: z.string(), createdAt: z.string().datetime(), updatedAt: z.string().datetime(), isPublic: z.boolean(), @@ -171,7 +171,7 @@ export const PackSchema = z.object({ export type Pack = z.infer; export const UserSchema = z.object({ - id: z.number().int().positive(), + id: z.string(), name: z.string(), email: z.string().email(), avatar: z.string().url(), diff --git a/packages/api/src/utils/__tests__/itemCalculations.test.ts b/packages/api/src/utils/__tests__/itemCalculations.test.ts index e7399c8b5f..ebb76cb6e6 100644 --- a/packages/api/src/utils/__tests__/itemCalculations.test.ts +++ b/packages/api/src/utils/__tests__/itemCalculations.test.ts @@ -26,7 +26,7 @@ function makePackItem(overrides: Partial = {}): PackItem { quantity: 1, consumable: false, worn: false, - userId: 1, + userId: 'user-1', category: 'tools', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), diff --git a/packages/api/src/utils/__tests__/weight.test.ts b/packages/api/src/utils/__tests__/weight.test.ts index 2fb3a7bb3c..caab2122ca 100644 --- a/packages/api/src/utils/__tests__/weight.test.ts +++ b/packages/api/src/utils/__tests__/weight.test.ts @@ -23,7 +23,7 @@ function makeItem( consumable: overrides.consumable ?? false, worn: overrides.worn ?? false, packId: 'pack-1', - userId: 1, + userId: 'user-1', category: 'tools', createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), From b59605cc36c9228320daefd252e26604154c6980 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 09:02:18 -0600 Subject: [PATCH 30/51] fix(api): correct HTTP status codes and soft-delete trips - upload.ts: return 400/403 instead of throw Error() for validation/auth - trips/index.ts: soft-delete (deleted=true) scoped by userId; return 404 when no row matched (replaces hard DELETE + separate ownership check) - guides/index.ts: return 400 for missing q param instead of throw Error() - user/index.ts: return 409 for duplicate email instead of throw Error() - weather.ts: return 400 for missing location ID instead of throw Error() - packs/index.ts: return 403 for access denied; use computePackWeights() directly instead of computePacksWeights([])[0] (noUncheckedIndexedAccess); remove redundant try/catch that only re-threw - schemas/upload.ts: restore z.string().url() validation on presigned URLs - routeParams.ts: update stale comment to match .pipe() implementation Routes that needed to return error status codes inline had their response: { 200: Schema } maps removed to satisfy Elysia type constraints (Eden Treaty typing preserved on all other routes). --- packages/api/src/routes/guides/index.ts | 5 ++--- packages/api/src/routes/packs/index.ts | 23 ++++++++--------------- packages/api/src/routes/trips/index.ts | 23 ++++++++--------------- packages/api/src/routes/upload.ts | 11 +++++------ packages/api/src/routes/user/index.ts | 5 ++--- packages/api/src/routes/weather.ts | 3 +-- packages/api/src/schemas/upload.ts | 4 ++-- packages/api/src/utils/routeParams.ts | 2 +- 8 files changed, 29 insertions(+), 47 deletions(-) diff --git a/packages/api/src/routes/guides/index.ts b/packages/api/src/routes/guides/index.ts index 7c57553507..8910ae466d 100644 --- a/packages/api/src/routes/guides/index.ts +++ b/packages/api/src/routes/guides/index.ts @@ -14,7 +14,7 @@ import { asNumber, asString, isArray } from '@packrat/guards'; const MDX_EXT_RE = /\.(mdx?|md)$/; const DASH_RE = /-/g; -import { Elysia, NotFoundError } from 'elysia'; +import { Elysia, NotFoundError, status } from 'elysia'; import matter from 'gray-matter'; import { z } from 'zod'; @@ -201,7 +201,7 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) async ({ query }) => { const { q, page, limit, category } = query; if (!q || q.trim() === '') { - throw new Error('Search query parameter q is required'); + return status(400, { error: 'Search query parameter q is required' }); } const searchQuery = q.toLowerCase(); @@ -285,7 +285,6 @@ export const guidesRoutes = new Elysia({ prefix: '/guides' }) }, { query: GuideSearchQuerySchema, - response: { 200: GuideSearchResponseSchema }, isAuthenticated: true, detail: { tags: ['Guides'], diff --git a/packages/api/src/routes/packs/index.ts b/packages/api/src/routes/packs/index.ts index ec38e7ade3..9f75128a06 100644 --- a/packages/api/src/routes/packs/index.ts +++ b/packages/api/src/routes/packs/index.ts @@ -117,7 +117,7 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) if (!newPack) throw new Error('Failed to create pack'); const packWithItems: PackWithItems = { ...newPack, items: [] }; - return PackWithWeightsSchema.parse(computePacksWeights([packWithItems])[0]); + return PackWithWeightsSchema.parse(computePackWeights(packWithItems)); }, { body: CreatePackBodySchema, @@ -231,19 +231,13 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) '/:packId', async ({ params }) => { const db = createDb(); - try { - const pack = await db.query.packs.findFirst({ - where: eq(packs.id, params.packId), - with: { items: { where: eq(packItems.deleted, false) } }, - }); + const pack = await db.query.packs.findFirst({ + where: eq(packs.id, params.packId), + with: { items: { where: eq(packItems.deleted, false) } }, + }); - if (!pack) throw new NotFoundError('Pack not found'); - return PackWithWeightsSchema.parse(computePackWeights(pack)); - } catch (error) { - if (error instanceof NotFoundError) throw error; - console.error('Error fetching pack:', error); - throw error; - } + if (!pack) throw new NotFoundError('Pack not found'); + return PackWithWeightsSchema.parse(computePackWeights(pack)); }, { params: z.object({ packId: z.string() }), @@ -685,13 +679,12 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s const isOwner = item.userId === user.userId; const isPublic = item.pack.isPublic; - if (!isOwner && !isPublic) throw new Error('Unauthorized'); + if (!isOwner && !isPublic) return status(403, { error: 'Forbidden' }); return PackItemSchema.parse(item); }, { params: z.object({ itemId: z.string() }), - response: { 200: PackItemSchema }, isAuthenticated: true, detail: { tags: ['Pack Items'], diff --git a/packages/api/src/routes/trips/index.ts b/packages/api/src/routes/trips/index.ts index f38f2cc1cc..17fe24bd21 100644 --- a/packages/api/src/routes/trips/index.ts +++ b/packages/api/src/routes/trips/index.ts @@ -3,7 +3,7 @@ import { trips } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; import { CreateTripBodySchema, TripSchema, UpdateTripBodySchema } from '@packrat/api/schemas/trips'; import { and, eq } from 'drizzle-orm'; -import { Elysia, NotFoundError } from 'elysia'; +import { Elysia, NotFoundError, status } from 'elysia'; import { z } from 'zod'; export const tripsRoutes = new Elysia({ prefix: '/trips' }) @@ -178,21 +178,14 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) const db = createDb(); const tripId = params.tripId; - try { - const trip = await db.query.trips.findFirst({ - where: eq(trips.id, tripId), - }); - - if (!trip) throw new NotFoundError('Trip not found'); - if (trip.userId !== user.userId) throw new Error('Forbidden'); - - await db.delete(trips).where(eq(trips.id, tripId)); + const [deleted] = await db + .update(trips) + .set({ deleted: true, updatedAt: new Date() }) + .where(and(eq(trips.id, tripId), eq(trips.userId, user.userId))) + .returning(); - return { success: true }; - } catch (error) { - console.error('Error deleting trip:', error); - throw error; - } + if (!deleted) return status(404, { error: 'Trip not found' }); + return { success: true }; }, { params: z.object({ tripId: z.string() }), diff --git a/packages/api/src/routes/upload.ts b/packages/api/src/routes/upload.ts index c2118a5aac..ea95e15ced 100644 --- a/packages/api/src/routes/upload.ts +++ b/packages/api/src/routes/upload.ts @@ -6,7 +6,7 @@ import { } from '@packrat/api/schemas/upload'; import { getEnv } from '@packrat/api/utils/env-validation'; import { getPresignedUrl } from '@packrat/api/utils/getPresignedUrl'; -import { Elysia } from 'elysia'; +import { Elysia, status } from 'elysia'; const ALLOWED_IMAGE_TYPES = [ 'image/jpeg', @@ -27,25 +27,25 @@ export const uploadRoutes = new Elysia({ prefix: '/upload' }).use(authPlugin).ge const { fileName, contentType, size } = query; if (!fileName || !contentType) { - throw new Error('fileName and contentType are required'); + return status(400, { error: 'fileName and contentType are required' }); } // Validate content type - only allow images if (!ALLOWED_IMAGE_TYPES.includes(contentType.toLowerCase())) { - throw new Error('Invalid content type. Only image files are allowed.'); + return status(400, { error: 'Invalid content type. Only image files are allowed.' }); } // Validate file size - max 10MB if (size) { const fileSize = Number.parseInt(String(size), 10); if (Number.isNaN(fileSize) || fileSize <= 0 || fileSize > MAX_FILE_SIZE) { - throw new Error('File size must be greater than 0 and not exceed 10MB'); + return status(400, { error: 'File size must be greater than 0 and not exceed 10MB' }); } } // Security check: Ensure the filename starts with the user's ID if (!fileName.startsWith(`${user.userId}-`)) { - throw new Error('Unauthorized'); + return status(403, { error: 'Unauthorized' }); } const command = new PutObjectCommand({ @@ -76,7 +76,6 @@ export const uploadRoutes = new Elysia({ prefix: '/upload' }).use(authPlugin).ge }, { query: PresignedUploadQuerySchema, - response: { 200: PresignedUploadResponseSchema }, isAuthenticated: true, detail: { tags: ['Upload'], diff --git a/packages/api/src/routes/user/index.ts b/packages/api/src/routes/user/index.ts index af74f6bf8a..50b778e5f3 100644 --- a/packages/api/src/routes/user/index.ts +++ b/packages/api/src/routes/user/index.ts @@ -7,7 +7,7 @@ import { UserProfileSchema, } from '@packrat/api/schemas/users'; import { eq } from 'drizzle-orm'; -import { Elysia, NotFoundError } from 'elysia'; +import { Elysia, NotFoundError, status } from 'elysia'; export const userRoutes = new Elysia({ prefix: '/user' }) .use(authPlugin) @@ -74,7 +74,7 @@ export const userRoutes = new Elysia({ prefix: '/user' }) .limit(1); if (existingUser && existingUser.id !== user.userId) { - throw new Error('Email already in use by another user'); + return status(409, { error: 'Email already in use by another user' }); } } @@ -125,7 +125,6 @@ export const userRoutes = new Elysia({ prefix: '/user' }) }, { body: UpdateUserRequestSchema, - response: { 200: UpdateUserResponseSchema }, isAuthenticated: true, detail: { tags: ['Users'], summary: 'Update user profile', security: [{ bearerAuth: [] }] }, }, diff --git a/packages/api/src/routes/weather.ts b/packages/api/src/routes/weather.ts index f615d3181c..c6bbd628ed 100644 --- a/packages/api/src/routes/weather.ts +++ b/packages/api/src/routes/weather.ts @@ -134,7 +134,7 @@ export const weatherRoutes = new Elysia({ prefix: '/weather' }) const id = Number(idParam); if (!idParam || Number.isNaN(id)) { - throw new Error('Valid location ID is required'); + return status(400, { error: 'Valid location ID is required' }); } try { @@ -159,7 +159,6 @@ export const weatherRoutes = new Elysia({ prefix: '/weather' }) }, { query: WeatherLocationIdSchema, - response: { 200: WeatherAPIForecastResponseSchema }, isAuthenticated: true, detail: { tags: ['Weather'], diff --git a/packages/api/src/schemas/upload.ts b/packages/api/src/schemas/upload.ts index 4c11fa8048..47b079e33f 100644 --- a/packages/api/src/schemas/upload.ts +++ b/packages/api/src/schemas/upload.ts @@ -12,7 +12,7 @@ export const PresignedUploadQuerySchema = z.object({ }); export const PresignedUploadResponseSchema = z.object({ - url: z.string(), + url: z.string().url(), objectKey: z.string(), - publicUrl: z.string(), + publicUrl: z.string().url(), }); diff --git a/packages/api/src/utils/routeParams.ts b/packages/api/src/utils/routeParams.ts index b69f85c6da..d4a85184b2 100644 --- a/packages/api/src/utils/routeParams.ts +++ b/packages/api/src/utils/routeParams.ts @@ -7,7 +7,7 @@ const PG_INT4_MAX = 2_147_483_647; // Accept only a digits-only string starting with 1-9 so `Number()`-accepted // forms like `0x10`, `1e2`, ` 42 `, `4.0`, and leading-zero `007` are rejected. -// Pipe into z.coerce.number for the int range check. +// Piped into z.coerce.number via .pipe() for the int range check. export const integerIdSchema = z .string() .regex(/^[1-9]\d*$/) From b62846d1ddb6f6b9f0f3450baff4d872c6e7a1a7 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 09:54:33 -0600 Subject: [PATCH 31/51] fix(etl): atomic totalProcessed updates and reset counters on retry Three related bugs that corrupted ETL job counters: 1. totalValid > totalProcessed (successRate > 100%): processValidItemsBatch called updateEtlJobProgress (which incremented totalValid) in one DB call, then processCatalogEtl incremented totalProcessed in a separate DB call. If the CF Worker died between these two calls, the job was permanently stuck with totalValid > totalProcessed. Fix: move totalProcessed increment into updateEtlJobProgress so all three counters (valid, invalid, processed) are updated in a single atomic query. 2. Counter double-counting on CF Queue retry: CF Worker CPU timeouts hard-kill the process, bypassing the catch block, so CF Queue re-delivers the message with the same jobId. The counters were additive, so retried jobs accumulated 2x (or more) the actual row counts. Fix: at the start of processCatalogETL, detect a retry (running status with non-zero totalProcessed) and reset counters to 0 before re-processing. 3. Broken auto-complete in updateEtlJobProgress: the CASE WHEN check compared totalProcessed against totalValid + totalInvalid, but totalProcessed was always updated after updateEtlJobProgress ran, so the check always saw a stale value and never fired. The explicit status='completed' set at the end of processCatalogETL is the real completion mechanism. Fix: remove the dead auto-complete logic from updateEtlJobProgress. Also adds two regression tests: one for the retry counter reset, one to assert totalProcessed == totalValid + totalInvalid after a clean run. --- .../api/src/services/etl/processCatalogEtl.ts | 54 +++++++++---------- .../api/src/services/etl/processLogsBatch.ts | 1 + .../services/etl/processValidItemsBatch.ts | 5 +- .../src/services/etl/updateEtlJobProgress.ts | 14 ++--- packages/api/test/etl.test.ts | 41 ++++++++++++++ 5 files changed, 75 insertions(+), 40 deletions(-) diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 05420aaede..0bff9f6ab3 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -3,7 +3,7 @@ import { etlJobs, type NewCatalogItem, type NewInvalidItemLog } from '@packrat/a import type { Env } from '@packrat/api/types/env'; import { mapCsvRowToItem } from '@packrat/api/utils/csv-utils'; import { parse } from 'csv-parse'; -import { eq, sql } from 'drizzle-orm'; +import { eq } from 'drizzle-orm'; import { R2BucketService } from '../r2-bucket'; import { CatalogItemValidator } from './CatalogItemValidator'; import { processLogsBatch } from './processLogsBatch'; @@ -42,6 +42,22 @@ export async function processCatalogETL({ const chunkDesc = byteStart !== undefined ? ` [bytes ${byteStart}-${byteEnd ?? 'end'}]` : ''; console.log(`🔄 Processing file ${objectKey}${chunkDesc}, job ${jobId}`); + // If this job is already in the DB with non-zero counters, it's a retry after a CF Worker + // timeout (hard-kill bypasses the catch block, so CF Queue re-delivers the message). + // Reset counters so the retry doesn't double-count valid/invalid/processed rows. + const existingJob = await db + .select({ status: etlJobs.status, totalProcessed: etlJobs.totalProcessed }) + .from(etlJobs) + .where(eq(etlJobs.id, jobId)) + .limit(1); + if (existingJob[0]?.status === 'running' && (existingJob[0].totalProcessed ?? 0) > 0) { + console.log(`[ETL] Retry detected for job ${jobId} — resetting counters before re-processing`); + await db + .update(etlJobs) + .set({ totalProcessed: 0, totalValid: 0, totalInvalid: 0 }) + .where(eq(etlJobs.id, jobId)); + } + const r2Service = new R2BucketService({ env, bucketType: 'catalog', @@ -152,51 +168,33 @@ export async function processCatalogETL({ rowIndex++; - // Flush valid batch to DB every BATCH_SIZE rows to avoid Worker OOM on large files + // Flush valid batch to DB every BATCH_SIZE rows to avoid Worker OOM on large files. + // totalProcessed is incremented atomically inside processValidItemsBatch via updateEtlJobProgress. if (validItemsBatch.length >= BATCH_SIZE) { await processValidItemsBatch({ jobId, items: [...validItemsBatch], env }); - await db - .update(etlJobs) - .set({ totalProcessed: sql`COALESCE(${etlJobs.totalProcessed}, 0) + ${BATCH_SIZE}` }) - .where(eq(etlJobs.id, jobId)); validItemsBatch.length = 0; } - // Flush invalid batch to DB every BATCH_SIZE rows + // Flush invalid batch to DB every BATCH_SIZE rows. + // totalProcessed is incremented atomically inside processLogsBatch via updateEtlJobProgress. if (invalidItemsBatch.length >= BATCH_SIZE) { await processLogsBatch({ jobId, logs: [...invalidItemsBatch], env }); - await db - .update(etlJobs) - .set({ totalProcessed: sql`COALESCE(${etlJobs.totalProcessed}, 0) + ${BATCH_SIZE}` }) - .where(eq(etlJobs.id, jobId)); invalidItemsBatch.length = 0; } } console.log(`🔍 [TRACE] Streaming complete - processing remaining batches`); - // Flush remaining items BEFORE updating totalProcessed so that if a flush throws, - // totalProcessed isn't inflated while valid/invalid counts stay null. - const remainingValid = validItemsBatch.length; - const remainingInvalid = invalidItemsBatch.length; - - if (remainingValid > 0) { - console.log(`🔍 [TRACE] Processing valid items batch - size: ${remainingValid}`); + // Flush remaining items. totalProcessed is updated atomically inside each batch function. + if (validItemsBatch.length > 0) { + console.log(`🔍 [TRACE] Processing valid items batch - size: ${validItemsBatch.length}`); await processValidItemsBatch({ jobId, items: validItemsBatch, env }); } - if (remainingInvalid > 0) { - console.log(`🔍 [TRACE] Processing invalid items batch - size: ${remainingInvalid}`); + if (invalidItemsBatch.length > 0) { + console.log(`🔍 [TRACE] Processing invalid items batch - size: ${invalidItemsBatch.length}`); await processLogsBatch({ jobId, logs: invalidItemsBatch, env }); } - const remainingItems = remainingValid + remainingInvalid; - if (remainingItems > 0) { - await db - .update(etlJobs) - .set({ totalProcessed: sql`COALESCE(${etlJobs.totalProcessed}, 0) + ${remainingItems}` }) - .where(eq(etlJobs.id, jobId)); - } - const totalRows = rowIndex; // Mark completed using Drizzle ORM (same as the failed path below) — avoids the diff --git a/packages/api/src/services/etl/processLogsBatch.ts b/packages/api/src/services/etl/processLogsBatch.ts index 661755850b..a9e59792bc 100644 --- a/packages/api/src/services/etl/processLogsBatch.ts +++ b/packages/api/src/services/etl/processLogsBatch.ts @@ -18,6 +18,7 @@ export async function processLogsBatch({ await updateEtlJobProgress(env, { jobId, invalid: logs.length, + processed: logs.length, }); console.log(`📝 Processed and wrote ${logs.length} invalid items for job ${jobId}`); diff --git a/packages/api/src/services/etl/processValidItemsBatch.ts b/packages/api/src/services/etl/processValidItemsBatch.ts index 51a6d36f2e..e84df38828 100644 --- a/packages/api/src/services/etl/processValidItemsBatch.ts +++ b/packages/api/src/services/etl/processValidItemsBatch.ts @@ -42,10 +42,12 @@ export async function processValidItemsBatch({ const upsertedItems = await catalogService.upsertCatalogItems(itemsWithEmbeddings); // Track the ETL job that processed these items await catalogService.trackEtlJob(upsertedItems, jobId); - // Update the ETL job progress + // Update the ETL job progress — processed is incremented atomically with valid to prevent + // totalValid > totalProcessed if the Worker dies between two separate DB updates. await updateEtlJobProgress(env, { jobId, valid: items.length, + processed: items.length, }); } catch (error) { console.error(`Error generating embeddings for batch ${jobId}:`, error); @@ -55,6 +57,7 @@ export async function processValidItemsBatch({ await updateEtlJobProgress(env, { jobId, valid: items.length, + processed: items.length, }); } finally { console.log(`📦 Batch ${jobId}: Processed ${items.length} valid items`); diff --git a/packages/api/src/services/etl/updateEtlJobProgress.ts b/packages/api/src/services/etl/updateEtlJobProgress.ts index 7bc5c623eb..f8e8b469d3 100644 --- a/packages/api/src/services/etl/updateEtlJobProgress.ts +++ b/packages/api/src/services/etl/updateEtlJobProgress.ts @@ -5,28 +5,20 @@ import { eq, sql } from 'drizzle-orm'; export async function updateEtlJobProgress( env: Env, - params: { jobId: string; valid?: number; invalid?: number }, + params: { jobId: string; valid?: number; invalid?: number; processed?: number }, ): Promise { const db = createDbClient(env); const valid = params?.valid ?? 0; const invalid = params?.invalid ?? 0; + const processed = params?.processed ?? 0; await db .update(etlJobs) .set({ totalValid: sql`COALESCE(${etlJobs.totalValid}, 0) + ${valid}`, totalInvalid: sql`COALESCE(${etlJobs.totalInvalid}, 0) + ${invalid}`, - status: sql`CASE - WHEN COALESCE(${etlJobs.totalProcessed}, 0) = COALESCE(${etlJobs.totalValid}, 0) + ${valid} + COALESCE(${etlJobs.totalInvalid}, 0) + ${invalid} - THEN 'completed' - ELSE ${etlJobs.status} - END`, - completedAt: sql`CASE - WHEN COALESCE(${etlJobs.totalProcessed}, 0) = COALESCE(${etlJobs.totalValid}, 0) + ${valid} + COALESCE(${etlJobs.totalInvalid}, 0) + ${invalid} - THEN CURRENT_TIMESTAMP - ELSE ${etlJobs.completedAt} - END`, + totalProcessed: sql`COALESCE(${etlJobs.totalProcessed}, 0) + ${processed}`, }) .where(eq(etlJobs.id, params.jobId)); } diff --git a/packages/api/test/etl.test.ts b/packages/api/test/etl.test.ts index f70c70e917..f41353de58 100644 --- a/packages/api/test/etl.test.ts +++ b/packages/api/test/etl.test.ts @@ -164,6 +164,47 @@ describe('processCatalogETL', () => { expect(job?.totalProcessed).toBe(250); }); + it('resets counters and re-processes correctly on CF Queue retry (same jobId)', async () => { + const jobId = crypto.randomUUID(); + const db = createDbClient({} as any); + // Simulate a prior partial run: job is still 'running' with non-zero counters + // (as would happen after a CF Worker CPU-timeout hard-kill) + await db.insert(etlJobs).values({ + id: jobId, + status: 'running', + source: 'test', + filename: 'test.csv', + scraperRevision: 'abc123', + startedAt: new Date(Date.now() - 10 * 60 * 1000), // started 10 min ago + totalProcessed: 50, + totalValid: 60, // intentionally > totalProcessed to simulate the corruption + totalInvalid: 0, + }); + mockR2WithCsv(makeCsv(5)); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const job = await getJob(jobId); + expect(job?.status).toBe('completed'); + // Counters should reflect the fresh run only, not the accumulated stale values + expect(job?.totalProcessed, 'retry must reset counters — no double-counting').toBe(5); + expect(job?.totalValid).toBe(5); + }); + + it('totalProcessed never exceeds totalValid + totalInvalid after completion', async () => { + const jobId = crypto.randomUUID(); + await insertJob(jobId); + mockR2WithCsv(makeCsv(10)); + + await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); + + const job = await getJob(jobId); + const processed = job?.totalProcessed ?? 0; + const valid = job?.totalValid ?? 0; + const invalid = job?.totalInvalid ?? 0; + expect(processed).toBe(valid + invalid); + }); + it('marks job as completed even when items have no weight', async () => { const jobId = crypto.randomUUID(); await insertJob(jobId); From c27ea322460d3ff3529440585ec42cb151b3d657 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 10:10:43 -0600 Subject: [PATCH 32/51] =?UTF-8?q?fixup:=20remove=20retry=20counter=20reset?= =?UTF-8?q?=20=E2=80=94=20incompatible=20with=20byte-range=20chunk=20proce?= =?UTF-8?q?ssing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With chunked ETL (multiple queue messages per file, each covering a byte range), chunk 2..N arrive with totalProcessed already > 0 from earlier chunks. The retry-reset guard would have fired on every non-first chunk, wiping the counters legitimately set by previous chunks. The core atomic-counter fix (totalProcessed updated inside updateEtlJobProgress alongside totalValid/totalInvalid) is the primary guard against successRate > 100%. Stale running jobs are already handled by the existing POST /etl/reset-stuck endpoint. --- .../api/src/services/etl/processCatalogEtl.ts | 16 ----------- packages/api/test/etl.test.ts | 27 ------------------- 2 files changed, 43 deletions(-) diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 0bff9f6ab3..b9a1d0220e 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -42,22 +42,6 @@ export async function processCatalogETL({ const chunkDesc = byteStart !== undefined ? ` [bytes ${byteStart}-${byteEnd ?? 'end'}]` : ''; console.log(`🔄 Processing file ${objectKey}${chunkDesc}, job ${jobId}`); - // If this job is already in the DB with non-zero counters, it's a retry after a CF Worker - // timeout (hard-kill bypasses the catch block, so CF Queue re-delivers the message). - // Reset counters so the retry doesn't double-count valid/invalid/processed rows. - const existingJob = await db - .select({ status: etlJobs.status, totalProcessed: etlJobs.totalProcessed }) - .from(etlJobs) - .where(eq(etlJobs.id, jobId)) - .limit(1); - if (existingJob[0]?.status === 'running' && (existingJob[0].totalProcessed ?? 0) > 0) { - console.log(`[ETL] Retry detected for job ${jobId} — resetting counters before re-processing`); - await db - .update(etlJobs) - .set({ totalProcessed: 0, totalValid: 0, totalInvalid: 0 }) - .where(eq(etlJobs.id, jobId)); - } - const r2Service = new R2BucketService({ env, bucketType: 'catalog', diff --git a/packages/api/test/etl.test.ts b/packages/api/test/etl.test.ts index f41353de58..c70019ffbf 100644 --- a/packages/api/test/etl.test.ts +++ b/packages/api/test/etl.test.ts @@ -164,33 +164,6 @@ describe('processCatalogETL', () => { expect(job?.totalProcessed).toBe(250); }); - it('resets counters and re-processes correctly on CF Queue retry (same jobId)', async () => { - const jobId = crypto.randomUUID(); - const db = createDbClient({} as any); - // Simulate a prior partial run: job is still 'running' with non-zero counters - // (as would happen after a CF Worker CPU-timeout hard-kill) - await db.insert(etlJobs).values({ - id: jobId, - status: 'running', - source: 'test', - filename: 'test.csv', - scraperRevision: 'abc123', - startedAt: new Date(Date.now() - 10 * 60 * 1000), // started 10 min ago - totalProcessed: 50, - totalValid: 60, // intentionally > totalProcessed to simulate the corruption - totalInvalid: 0, - }); - mockR2WithCsv(makeCsv(5)); - - await processCatalogETL({ message: makeMessage(jobId) as any, env: TEST_ENV }); - - const job = await getJob(jobId); - expect(job?.status).toBe('completed'); - // Counters should reflect the fresh run only, not the accumulated stale values - expect(job?.totalProcessed, 'retry must reset counters — no double-counting').toBe(5); - expect(job?.totalValid).toBe(5); - }); - it('totalProcessed never exceeds totalValid + totalInvalid after completion', async () => { const jobId = crypto.randomUUID(); await insertJob(jobId); From ad8167ae415a8fd3c1045efd2c232d5d722d1eb5 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 10:24:33 -0600 Subject: [PATCH 33/51] refactor: extract @packrat/db and @packrat/schemas from @packrat/api MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create two new workspace packages: - @packrat/db: Drizzle table definitions, raw constants (no Zod), ValidationError interface, drizzle-zod generated schemas - @packrat/schemas: All route-level Zod schemas, depends on @packrat/db packages/api keeps 100% backward-compatible re-export shims so all existing import paths continue to work unchanged. drizzle.config.ts and migration infra are untouched. @packrat/app entities now import from @packrat/schemas directly. Resolves duplicate ErrorResponseSchema/SuccessResponseSchema across schema files (consolidated into @packrat/schemas/shared). Fixes pre-existing type mismatches surfaced by stricter Drizzle-inferred types: User.avatar→image/avatarUrl, Pack→PackWithItems for compute-pack utility, test factories updated to provide all required Drizzle model fields. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/expo/components/initial/UserAvatar.tsx | 16 +- apps/expo/data/mockData.ts | 12 +- .../lib/utils/__tests__/compute-pack.test.ts | 33 +- apps/expo/lib/utils/compute-pack.ts | 18 +- apps/expo/utils/__tests__/weight.test.ts | 16 +- bun.lock | 35 +- ...or-extract-schemas-and-db-packages-plan.md | 313 ++++++++ package.json | 1 + packages/api/package.json | 2 + packages/api/src/db/schema.ts | 737 +----------------- packages/api/src/db/zod-schemas.ts | 47 +- packages/api/src/schemas/ai.ts | 35 +- packages/api/src/schemas/auth.ts | 139 +--- packages/api/src/schemas/catalog.ts | 339 +------- packages/api/src/schemas/chat.ts | 64 +- packages/api/src/schemas/feed.ts | 65 +- packages/api/src/schemas/guides.ts | 67 +- packages/api/src/schemas/imageDetection.ts | 26 +- packages/api/src/schemas/packTemplates.ts | 117 +-- packages/api/src/schemas/packs.ts | 173 +--- packages/api/src/schemas/seasonSuggestions.ts | 39 +- packages/api/src/schemas/trailConditions.ts | 33 +- packages/api/src/schemas/trips.ts | 62 +- packages/api/src/schemas/upload.ts | 19 +- packages/api/src/schemas/users.ts | 91 +-- packages/api/src/schemas/weather.ts | 260 +----- packages/api/src/types/constants.ts | 185 +---- packages/api/src/types/validation.ts | 16 +- .../utils/__tests__/itemCalculations.test.ts | 49 +- .../api/src/utils/__tests__/weight.test.ts | 19 +- packages/api/src/utils/itemCalculations.ts | 4 +- packages/app/package.json | 1 + packages/app/src/entities/catalog/schema.ts | 2 +- packages/app/src/entities/feed/schema.ts | 2 +- packages/app/src/entities/pack/schema.ts | 2 +- packages/app/src/entities/trip/schema.ts | 2 +- packages/app/src/entities/user/schema.ts | 2 +- packages/db/package.json | 26 + packages/db/src/constants.ts | 55 ++ packages/db/src/index.ts | 4 + packages/db/src/schema.ts | 636 +++++++++++++++ packages/db/src/validation.ts | 5 + packages/db/src/zod-schemas.ts | 41 + packages/db/tsconfig.json | 11 + packages/schemas/package.json | 27 + packages/schemas/src/ai.ts | 30 + packages/schemas/src/auth.ts | 133 ++++ packages/schemas/src/catalog.ts | 333 ++++++++ packages/schemas/src/chat.ts | 54 ++ packages/schemas/src/constants.ts | 26 + packages/schemas/src/feed.ts | 64 ++ packages/schemas/src/guides.ts | 61 ++ packages/schemas/src/imageDetection.ts | 25 + packages/schemas/src/index.ts | 18 + packages/schemas/src/packTemplates.ts | 112 +++ packages/schemas/src/packs.ts | 172 ++++ packages/schemas/src/seasonSuggestions.ts | 34 + packages/schemas/src/shared.ts | 13 + packages/schemas/src/trailConditions.ts | 32 + packages/schemas/src/trips.ts | 61 ++ packages/schemas/src/upload.ts | 13 + packages/schemas/src/users.ts | 90 +++ packages/schemas/src/validation.ts | 11 + packages/schemas/src/weather.ts | 254 ++++++ packages/schemas/tsconfig.json | 17 + tsconfig.json | 6 +- 66 files changed, 2855 insertions(+), 2552 deletions(-) create mode 100644 docs/plans/2026-05-14-refactor-extract-schemas-and-db-packages-plan.md create mode 100644 packages/db/package.json create mode 100644 packages/db/src/constants.ts create mode 100644 packages/db/src/index.ts create mode 100644 packages/db/src/schema.ts create mode 100644 packages/db/src/validation.ts create mode 100644 packages/db/src/zod-schemas.ts create mode 100644 packages/db/tsconfig.json create mode 100644 packages/schemas/package.json create mode 100644 packages/schemas/src/ai.ts create mode 100644 packages/schemas/src/auth.ts create mode 100644 packages/schemas/src/catalog.ts create mode 100644 packages/schemas/src/chat.ts create mode 100644 packages/schemas/src/constants.ts create mode 100644 packages/schemas/src/feed.ts create mode 100644 packages/schemas/src/guides.ts create mode 100644 packages/schemas/src/imageDetection.ts create mode 100644 packages/schemas/src/index.ts create mode 100644 packages/schemas/src/packTemplates.ts create mode 100644 packages/schemas/src/packs.ts create mode 100644 packages/schemas/src/seasonSuggestions.ts create mode 100644 packages/schemas/src/shared.ts create mode 100644 packages/schemas/src/trailConditions.ts create mode 100644 packages/schemas/src/trips.ts create mode 100644 packages/schemas/src/upload.ts create mode 100644 packages/schemas/src/users.ts create mode 100644 packages/schemas/src/validation.ts create mode 100644 packages/schemas/src/weather.ts create mode 100644 packages/schemas/tsconfig.json diff --git a/apps/expo/components/initial/UserAvatar.tsx b/apps/expo/components/initial/UserAvatar.tsx index 29cb3d58d5..dd070a664f 100644 --- a/apps/expo/components/initial/UserAvatar.tsx +++ b/apps/expo/components/initial/UserAvatar.tsx @@ -1,8 +1,14 @@ -import type { User } from '@packrat/api/types/constants'; import { Image, Text, View } from 'react-native'; +type UserLike = { + name: string; + avatar?: string | null; + image?: string | null; + avatarUrl?: string | null; +}; + type UserAvatarProps = { - user: User; + user: UserLike; size?: 'sm' | 'md' | 'lg'; showName?: boolean; }; @@ -20,11 +26,13 @@ export function UserAvatar({ user, size = 'md', showName = false }: UserAvatarPr lg: 'text-base', }[size]; + const avatarUri = user.avatar ?? user.image ?? user.avatarUrl; + return ( - {user.avatar ? ( - + {avatarUri ? ( + ) : ( {user.name.substring(0, 2).toUpperCase()} diff --git a/apps/expo/data/mockData.ts b/apps/expo/data/mockData.ts index 09e8e8ee25..8f8be24b68 100644 --- a/apps/expo/data/mockData.ts +++ b/apps/expo/data/mockData.ts @@ -1,7 +1,15 @@ -import type { User } from '@packrat/api/types/constants'; +export type MockUser = { + id: string; + name: string; + email: string; + avatar: string; + experience: string; + joinedAt: string; + bio: string; +}; // --- Users --- -export const mockUsers: [User, ...User[]] = [ +export const mockUsers: [MockUser, ...MockUser[]] = [ { id: '1', name: 'Alex Hiker', diff --git a/apps/expo/lib/utils/__tests__/compute-pack.test.ts b/apps/expo/lib/utils/__tests__/compute-pack.test.ts index 1b97ab0a31..322d8ae2bd 100644 --- a/apps/expo/lib/utils/__tests__/compute-pack.test.ts +++ b/apps/expo/lib/utils/__tests__/compute-pack.test.ts @@ -1,12 +1,11 @@ -import type { Pack, PackItem } from '@packrat/api/types/constants'; +import type { PackItem, PackWithItems } from '@packrat/api/types/constants'; import { describe, expect, it } from 'vitest'; import { computePacksWeights, computePackWeights } from '../compute-pack'; // --------------------------------------------------------------------------- // Minimal factory helpers // --------------------------------------------------------------------------- -// Arbitrary fixed timestamp used only as a required field value, not asserted on -const NOW = new Date().toISOString(); +const NOW = new Date(); function makePackItem( overrides: Partial & Pick, @@ -14,28 +13,44 @@ function makePackItem( return { id: 'item-1', name: 'Test Item', + description: null, quantity: overrides.quantity ?? 1, + category: null, consumable: overrides.consumable ?? false, worn: overrides.worn ?? false, - category: 'tools', + image: null, + notes: null, packId: 'pack-1', + catalogItemId: null, userId: 'user-1', + deleted: false, + isAIGenerated: false, + templateItemId: null, + embedding: null, createdAt: NOW, updatedAt: NOW, ...overrides, - }; + } as PackItem; } -function makePack(items: PackItem[] = [], overrides: Partial = {}): Pack { +function makePack(items: PackItem[] = [], overrides: Partial = {}): PackWithItems { return { id: 'pack-1', name: 'Test Pack', + description: null, category: 'hiking', - items, userId: 'user-1', + templateId: null, + isPublic: false, + image: null, + tags: null, + deleted: false, + isAIGenerated: false, + localCreatedAt: NOW, + localUpdatedAt: NOW, createdAt: NOW, updatedAt: NOW, - isPublic: false, + items, ...overrides, }; } @@ -53,7 +68,7 @@ describe('computePackWeights', () => { it('throws when items property is null/undefined', () => { const pack = makePack(); // Force missing items - (pack as Pack & { items: undefined }).items = undefined; + (pack as any).items = undefined; expect(() => computePackWeights(pack)).toThrow('Pack with ID pack-1 has no items'); }); diff --git a/apps/expo/lib/utils/compute-pack.ts b/apps/expo/lib/utils/compute-pack.ts index ed31547406..99bbf900a4 100644 --- a/apps/expo/lib/utils/compute-pack.ts +++ b/apps/expo/lib/utils/compute-pack.ts @@ -1,8 +1,16 @@ -import type { Pack } from '@packrat/api/types/constants'; +import type { PackWithItems } from '@packrat/api/types/constants'; import type { WeightUnit } from '@packrat/units'; import { displayWeight, normalize, parseWeightUnit } from '@packrat/units'; -export const computePackWeights = (pack: Pack, preferredUnit: WeightUnit = 'g'): Pack => { +export type ComputedPack = PackWithItems & { + baseWeight: number; + totalWeight: number; +}; + +export const computePackWeights = ( + pack: PackWithItems, + preferredUnit: WeightUnit = 'g', +): ComputedPack => { if (!pack.items) { throw new Error(`Pack with ID ${pack.id} has no items`); } @@ -26,5 +34,7 @@ export const computePackWeights = (pack: Pack, preferredUnit: WeightUnit = 'g'): }; }; -export const computePacksWeights = (packs: Pack[], preferredUnit: WeightUnit = 'g'): Pack[] => - packs.map((pack) => computePackWeights(pack, preferredUnit)); +export const computePacksWeights = ( + packs: PackWithItems[], + preferredUnit: WeightUnit = 'g', +): ComputedPack[] => packs.map((pack) => computePackWeights(pack, preferredUnit)); diff --git a/apps/expo/utils/__tests__/weight.test.ts b/apps/expo/utils/__tests__/weight.test.ts index 0311af92c7..c8080690f4 100644 --- a/apps/expo/utils/__tests__/weight.test.ts +++ b/apps/expo/utils/__tests__/weight.test.ts @@ -11,16 +11,24 @@ function makeItem( return { id: 'item-1', name: 'Test Item', + description: null, quantity: overrides.quantity ?? 1, + category: null, consumable: overrides.consumable ?? false, worn: overrides.worn ?? false, + image: null, + notes: null, packId: 'pack-1', + catalogItemId: null, userId: 'user-1', - category: 'tools', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + deleted: false, + isAIGenerated: false, + templateItemId: null, + embedding: null, + createdAt: new Date(), + updatedAt: new Date(), ...overrides, - }; + } as PackItem; } // --------------------------------------------------------------------------- diff --git a/bun.lock b/bun.lock index d2ca53ff96..7a204cbed1 100644 --- a/bun.lock +++ b/bun.lock @@ -279,7 +279,7 @@ "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", - "vitest": "~3.1.4", + "vitest": "catalog:", }, }, "apps/landing": { @@ -347,7 +347,7 @@ "postcss-import": "catalog:", "tailwindcss": "catalog:", "typescript": "catalog:", - "vitest": "~3.1.4", + "vitest": "catalog:", }, }, "apps/trails": { @@ -452,9 +452,11 @@ "@elysiajs/openapi": "catalog:", "@mozilla/readability": "^0.6.0", "@neondatabase/serverless": "catalog:", + "@packrat/db": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/overpass": "workspace:*", + "@packrat/schemas": "workspace:*", "@packrat/units": "workspace:*", "@sinclair/typebox": "^0.34.15", "@types/nodemailer": "^6.4.17", @@ -519,6 +521,7 @@ "version": "2.0.25", "dependencies": { "@packrat/api-client": "workspace:*", + "@packrat/schemas": "workspace:*", "@tanstack/react-query": "catalog:", "jotai": "catalog:", "react": "catalog:", @@ -566,6 +569,17 @@ "@packrat/guards": "workspace:*", }, }, + "packages/db": { + "name": "@packrat/db", + "version": "0.0.0", + "dependencies": { + "drizzle-orm": "catalog:", + "drizzle-zod": "catalog:", + }, + "devDependencies": { + "typescript": "catalog:", + }, + }, "packages/env": { "name": "@packrat/env", "version": "2.0.25", @@ -636,6 +650,18 @@ "vitest": "catalog:", }, }, + "packages/schemas": { + "name": "@packrat/schemas", + "version": "0.0.0", + "dependencies": { + "@packrat/db": "workspace:*", + "@packrat/guards": "workspace:*", + "zod": "catalog:", + }, + "devDependencies": { + "typescript": "catalog:", + }, + }, "packages/ui": { "name": "@packrat/ui", "version": "2.0.25", @@ -785,6 +811,7 @@ "date-fns": "^4.1.0", "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.2", + "drizzle-zod": "^0.8.3", "elysia": "^1.4.0", "embla-carousel-react": "8.6.0", "google-auth-library": "^10.1.0", @@ -1540,6 +1567,8 @@ "@packrat/config": ["@packrat/config@workspace:packages/config"], + "@packrat/db": ["@packrat/db@workspace:packages/db"], + "@packrat/env": ["@packrat/env@workspace:packages/env"], "@packrat/guards": ["@packrat/guards@workspace:packages/guards"], @@ -1552,6 +1581,8 @@ "@packrat/overpass": ["@packrat/overpass@workspace:packages/overpass"], + "@packrat/schemas": ["@packrat/schemas@workspace:packages/schemas"], + "@packrat/ui": ["@packrat/ui@workspace:packages/ui"], "@packrat/units": ["@packrat/units@workspace:packages/units"], diff --git a/docs/plans/2026-05-14-refactor-extract-schemas-and-db-packages-plan.md b/docs/plans/2026-05-14-refactor-extract-schemas-and-db-packages-plan.md new file mode 100644 index 0000000000..a690cb1d01 --- /dev/null +++ b/docs/plans/2026-05-14-refactor-extract-schemas-and-db-packages-plan.md @@ -0,0 +1,313 @@ +--- +title: "refactor: extract @packrat/db and @packrat/schemas from packages/api" +type: refactor +status: active +date: 2026-05-14 +--- + +# refactor: extract @packrat/db and @packrat/schemas from packages/api + +## Overview + +`packages/api` currently serves two incompatible roles: a Cloudflare Worker (deployed binary, CF-specific deps) and a shared schema/type library imported by every consumer in the monorepo. This plan extracts the Drizzle schema + drizzle-zod generated schemas into `@packrat/db`, and the route-level Zod schemas into `@packrat/schemas`. Frontend apps get a clean import path with no CF Workers leakage. Migration infra is unchanged. + +## Problem Statement + +**Leaky abstraction.** `apps/expo`, `packages/app`, `apps/admin`, and `apps/guides` import from `@packrat/api`. That package has `compatibility_flags: ["nodejs_compat"]`, `wrangler.jsonc`, and CF Workers-specific deps. Frontend toolchains encounter server-only types — fragile and semantically wrong. + +**Schema duplication.** Hand-written Zod schemas in `packages/api/src/schemas/` duplicate the shape of Drizzle tables. `drizzle-zod` can generate the base layer from the table definitions, making the DB the single source of truth. This is only possible if the Drizzle schema lives in a package that `@packrat/schemas` can import from without a circular dep. + +**`packages/app` entity re-exports are vestigial.** After PR #2414, `packages/app/src/entities/*/schema.ts` are 3-line re-export passthroughs to `@packrat/api/schemas/*`. Once `@packrat/schemas` exists, they have a real home. + +## Proposed Solution + +Two new packages, created in order: + +### `packages/db` → `@packrat/db` (foundation) + +Pure Drizzle + drizzle-zod. No Zod dep. No CF Workers bindings. + +Contains: +- `src/schema.ts` — Drizzle table definitions (moved from `packages/api/src/db/schema.ts`) +- `src/zod-schemas.ts` — drizzle-zod generated base schemas (`createSelectSchema(users)`, `createInsertSchema(packs)`, etc.) — moved from `packages/api/src/db/zod-schemas.ts` +- `src/constants.ts` — raw `as const` arrays + TypeScript types only (`PACK_CATEGORIES`, `PackCategory`, `WEIGHT_UNITS`, `WeightUnit`, etc.) — no Zod dep needed +- `src/validation.ts` — `ValidationError` interface only (plain TypeScript, no Zod) — used by `schema.ts` as `.$type()` +- `src/index.ts` — re-exports all of the above + +Does **not** contain: +- DB client factories (`createDb`, `createReadOnlyDb`) — stay in `packages/api/src/db/index.ts` +- `drizzle.config.ts`, `drizzle/` migrations — stay in `packages/api` (see Migration Infra below) +- Any Zod schemas — those belong in `@packrat/schemas` + +**`package.json` for `@packrat/db`:** +```json +{ + "name": "@packrat/db", + "version": "0.0.0", + "private": true, + "exports": { + ".": { "types": "./src/index.ts", "default": "./src/index.ts" }, + "./*": { "types": "./src/*", "default": "./src/*" } + }, + "dependencies": { + "drizzle-orm": "catalog:", + "drizzle-zod": "catalog:" + } +} +``` + +Note: `drizzle-zod` v0.8.3 explicitly supports `"zod": "^3.25.0 || ^4.0.0"` — no compatibility issue. + +### `packages/schemas` → `@packrat/schemas` (depends on db) + +Route-level Zod schemas. Depends on `@packrat/db`. No Elysia. No CF Workers bindings. + +The drizzle-zod chain: `@packrat/db` generates base schemas from tables → `@packrat/schemas` extends/picks/transforms them for routes: +```typescript +// single source of truth +import { selectUserSchema } from '@packrat/db/zod-schemas'; +export const UserProfileSchema = selectUserSchema + .pick({ id: true, email: true, firstName: true, ... }) + .extend({ createdAt: z.string().datetime() }); +``` + +Contains: +- Route-level Zod schemas extending drizzle-zod output (packs, catalog, trips, feed, guides, users, etc.) +- Hand-written schemas for non-DB types (weather, upload, chat, AI, guides search, etc.) +- Zod enum wrappers around raw constants from `@packrat/db`: `PackCategorySchema = z.enum(PACK_CATEGORIES)` +- `ValidationErrorSchema` (Zod schema wrapping `ValidationError` interface from `@packrat/db`) +- Inferred TypeScript types + +Does **not** contain: +- `admin.ts` — uses Elysia TypeBox `t`; stays in `packages/api/src/schemas/` +- Raw Drizzle table definitions or DB connection code + +**`package.json` for `@packrat/schemas`:** +```json +{ + "name": "@packrat/schemas", + "version": "0.0.0", + "private": true, + "exports": { + ".": { "types": "./src/index.ts", "default": "./src/index.ts" }, + "./*": { "types": "./src/*", "default": "./src/*" } + }, + "dependencies": { + "zod": "catalog:", + "@packrat/db": "workspace:*", + "@packrat/guards": "workspace:*" + } +} +``` + +### `packages/api` — 3 re-export shims + 1 dep, nothing else + +The only changes to `packages/api`: + +1. `src/db/schema.ts` → `export * from '@packrat/db/schema';` +2. `src/types/constants.ts` → `export * from '@packrat/schemas/constants';` (which itself re-exports raw arrays from `@packrat/db/constants` — single shim covers both layers) +3. `src/types/validation.ts` → `export * from '@packrat/schemas/validation';` +4. `package.json` → add `"@packrat/db": "workspace:*"` and `"@packrat/schemas": "workspace:*"` + +Everything else in `packages/api` is **unchanged**: routes, services, middleware, db client factories, env-validation, `drizzle.config.ts`, `drizzle/` migrations, `db:generate` script. + +## Dependency Graph + +``` +@packrat/guards + +@packrat/db (drizzle-orm, drizzle-zod — no Zod) + │ Drizzle tables, raw enum arrays/types, drizzle-zod generated schemas + ↓ +@packrat/schemas (zod, @packrat/db, @packrat/guards) + │ Zod enum wrappers, route schemas extending drizzle-zod output + ↓ +@packrat/api (elysia, CF Workers, @packrat/db, @packrat/schemas, ...) + │ Routes, middleware, DB client factories, migrations + ↓ +@packrat/api-client (eden treaty, @packrat/api type App) + ↓ +apps/expo, apps/web, apps/admin, apps/guides, packages/app +``` + +## Migration Infra — Zero Changes + +`drizzle.config.ts` stays in `packages/api` pointing at `./src/db/schema.ts`. That file is now a 1-line re-export shim. drizzle-kit v0.31 loads schema files via `require()` + `Object.values(exports)` — it follows re-exports transparently (verified from source at `node_modules/drizzle-kit/bin.cjs` lines 15893–15920). The `drizzle/` migrations folder, `db:generate` script, `db:migrate` script — all unchanged. + +## Implementation Phases + +### Phase 1: Create `@packrat/db` + +**Deliverables:** +- New `packages/db/` with `package.json`, `tsconfig.json`, `src/index.ts` +- Move `packages/api/src/db/schema.ts` → `packages/db/src/schema.ts` + - Update import `from '@packrat/api/types/constants'` → `from './constants'` + - Update import `from '../types/validation'` → `from './validation'` +- Move `packages/api/src/db/zod-schemas.ts` → `packages/db/src/zod-schemas.ts` + - Update import `from './schema'` — stays the same (local) +- Extract to `packages/db/src/constants.ts` (raw arrays + TS types only, no Zod): + - `PACK_CATEGORIES`, `PackCategory` + - `ITEM_CATEGORIES`, `ItemCategory` + - `WEIGHT_UNITS`, `WeightUnit` + - `AVAILABILITY_VALUES`, `Availability` + - `ItemLink`, `ItemReview` (plain TS interfaces) +- Extract to `packages/db/src/validation.ts`: `ValidationError` interface only (no Zod) +- Add re-export shim `packages/api/src/db/schema.ts` → `export * from '@packrat/db/schema'` +- Add re-export shim `packages/api/src/db/zod-schemas.ts` → `export * from '@packrat/db/zod-schemas'` +- Add `@packrat/db` to root `tsconfig.json` path aliases and `package.json` workspaces +- Add `"@packrat/db": "workspace:*"` to `packages/api/package.json` + +**`tsconfig.json` for `packages/db`:** +```json +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noUncheckedIndexedAccess": true + }, + "include": ["src"] +} +``` + +**Success criteria:** +- `bun check-types` exits 0 +- `bun run --cwd packages/api db:generate` still works (drizzle-kit follows re-export shim) +- `packages/api` routes still resolve Drizzle tables via `@packrat/api/db/schema` (through shim) + +### Phase 2: Create `@packrat/schemas` + +**Deliverables:** +- New `packages/schemas/` with `package.json`, `tsconfig.json`, `src/index.ts` +- Move (copy → delete) from `packages/api/src/schemas/` → `packages/schemas/src/`: + - `ai.ts`, `auth.ts`, `catalog.ts`, `chat.ts`, `feed.ts`, `guides.ts` + - `imageDetection.ts`, `packTemplates.ts`, `packs.ts`, `seasonSuggestions.ts` + - `trailConditions.ts`, `trips.ts`, `upload.ts`, `users.ts`, `weather.ts` +- Create `packages/schemas/src/constants.ts`: + ```typescript + export * from '@packrat/db/constants'; // re-export raw arrays + TS types + import { PACK_CATEGORIES, ITEM_CATEGORIES, WEIGHT_UNITS, AVAILABILITY_VALUES } from '@packrat/db/constants'; + export const PackCategorySchema = z.enum(PACK_CATEGORIES); + export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); + export const WeightUnitSchema = z.enum(WEIGHT_UNITS); + export const AvailabilitySchema = z.enum(AVAILABILITY_VALUES); + // ... inferred types + ``` +- Create `packages/schemas/src/validation.ts`: + ```typescript + export type { ValidationError } from '@packrat/db/validation'; + export const ValidationErrorSchema = z.object({ ... }); + export const ValidationErrorsSchema = z.array(ValidationErrorSchema); + ``` +- Add 1-line re-export shims in `packages/api/src/schemas/*.ts` (except `admin.ts`) → `@packrat/schemas/*` +- Replace `packages/api/src/types/constants.ts` with: `export * from '@packrat/schemas/constants'` +- Replace `packages/api/src/types/validation.ts` with: `export * from '@packrat/schemas/validation'` +- Cleanup: `CatalogItemSchema`, `PackItemSchema`, `PackSchema`, `UserSchema` currently live in `constants.ts` but belong in their respective schema files (`catalog.ts`, `packs.ts`, `users.ts`). Remove the duplicates from `constants.ts` during this move. +- Add `@packrat/schemas` to root `tsconfig.json` path aliases and `package.json` workspaces +- Add `"@packrat/schemas": "workspace:*"` to `packages/api/package.json` + +**`tsconfig.json` for `packages/schemas`:** +```json +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noUncheckedIndexedAccess": true, + "paths": { + "@packrat/db": ["../db/src/index.ts"], + "@packrat/db/*": ["../db/src/*"], + "@packrat/guards": ["../guards/src/index.ts"], + "@packrat/guards/*": ["../guards/src/*"] + } + }, + "include": ["src"] +} +``` + +**Root `tsconfig.json` additions (both phases):** +```json +"@packrat/db": ["./packages/db/src/index.ts"], +"@packrat/db/*": ["./packages/db/src/*"], +"@packrat/schemas": ["./packages/schemas/src/index.ts"], +"@packrat/schemas/*": ["./packages/schemas/src/*"] +``` + +**Success criteria:** +- `bun check-types` exits 0 +- `packages/api/src/schemas/catalog.ts` is a 1-line re-export +- `apps/expo` imports still resolve unchanged (re-export shims preserve all existing paths) +- `bun ./scripts/lint/no-duplicate-deps.ts` exits 0 + +### Phase 3: Migrate `packages/app` entities + +Update 5 files in `packages/app/src/entities/`: +- `catalog/schema.ts` → `from '@packrat/schemas/catalog'` +- `pack/schema.ts` → `from '@packrat/schemas/packs'` +- `feed/schema.ts` → `from '@packrat/schemas/feed'` +- `trip/schema.ts` → `from '@packrat/schemas/trips'` +- `user/schema.ts` → `from '@packrat/schemas/users'` + +Add `"@packrat/schemas": "workspace:*"` to `packages/app/package.json`. + +**Success criteria:** `packages/app` has no `@packrat/api` imports. + +### Phase 4: Direct consumer migration (optional) + +Update `apps/expo` (35+ files), `apps/admin`, `apps/guides` to import from `@packrat/schemas` directly. The re-export shims make this fully deferrable — things work correctly without it. + +```bash +grep -r "from '@packrat/api/schemas" apps/ packages/app/ --include="*.ts" --include="*.tsx" -l +grep -r "from '@packrat/api/types" apps/ packages/app/ --include="*.ts" --include="*.tsx" -l +``` + +### Phase 5: Incremental drizzle-zod migration (ongoing) + +Replace hand-written Zod schemas that duplicate DB-backed shapes with drizzle-zod extensions: +```typescript +// before +export const TripSchema = z.object({ id: z.string(), name: z.string(), ... }); + +// after +import { selectTripSchema } from '@packrat/db/zod-schemas'; +export const TripSchema = selectTripSchema.extend({ startDate: z.string().datetime().nullable(), ... }); +``` + +This is incremental — schemas can be migrated one at a time, `bun check-types` after each. + +## Acceptance Criteria + +- [ ] `packages/db/` exists; `package.json` deps are only `drizzle-orm` and `drizzle-zod` (no Zod) +- [ ] `packages/schemas/` exists; `package.json` deps are `zod`, `@packrat/db`, `@packrat/guards` +- [ ] `bun check-types` exits 0 after each phase +- [ ] `bun ./scripts/lint/no-duplicate-deps.ts` exits 0 (catalog entries used correctly) +- [ ] `bun run --cwd packages/api db:generate` still works after Phase 1 +- [ ] DB client factories, `drizzle.config.ts`, `drizzle/` migrations all stay in `packages/api` +- [ ] `packages/api/src/schemas/admin.ts` stays in `packages/api` +- [ ] Re-export shims mean zero changes to `apps/expo` through Phases 1–2 +- [ ] Phase 3: `packages/app` has no `@packrat/api` imports +- [ ] Phase 4 (optional): no file outside `packages/api` imports from `@packrat/api/schemas/*` + +## Risks — All Low + +| Risk | Assessment | Mitigation | +|---|---|---| +| drizzle-kit can't follow re-export shim | **None** — verified from source (v0.31 uses `require()` + `Object.values(exports)`) | N/A | +| drizzle-zod incompatible with Zod v4 | **None** — v0.8.3 peer dep is `"zod": "^3.25.0 \|\| ^4.0.0"` | N/A | +| `ValidationError` circular dep | **None** — it's a plain TS interface; lives in `@packrat/db` with no Zod dep | N/A | +| Routes in `packages/api` break | **None** — all import via `@packrat/api/db/schema` which becomes a re-export shim | N/A | +| `better-auth` CLI needs stable schema path | **None** — `packages/api/src/db/schema.ts` path is preserved as a re-export shim | N/A | +| Phase 4 touches 35+ files in `apps/expo` | Medium — but fully deferrable; shims work indefinitely | Do with find-replace when ready | + +## Sources & References + +- `packages/api/src/db/schema.ts` — Drizzle table definitions (735 lines); moves to `@packrat/db` +- `packages/api/src/db/zod-schemas.ts` — drizzle-zod generated schemas; moves to `@packrat/db` +- `packages/api/src/types/constants.ts` — raw arrays + Zod enums + misplaced schemas; splits across `@packrat/db` and `@packrat/schemas` +- `packages/api/src/types/validation.ts` — `ValidationError` interface + Zod schema; splits across `@packrat/db` and `@packrat/schemas` +- `packages/api/src/schemas/` — 16 files; 15 move, `admin.ts` stays +- `packages/osm-db/` — existing monorepo template for a standalone Drizzle schema package +- PR #2414 — prerequisite; establishes clean schema boundaries and `app/entities/*` re-exports +- `docs/plans/2026-05-13-chore-enroll-catalog-candidates-plan.md` — `zod`, `drizzle-orm`, `drizzle-zod` already enrolled in catalog diff --git a/package.json b/package.json index cd935755f1..b0760b42fa 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,7 @@ "date-fns": "^4.1.0", "drizzle-kit": "^0.31.10", "drizzle-orm": "^0.45.2", + "drizzle-zod": "^0.8.3", "elysia": "^1.4.0", "embla-carousel-react": "8.6.0", "google-auth-library": "^10.1.0", diff --git a/packages/api/package.json b/packages/api/package.json index 84fa5821e5..865c55c436 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -42,9 +42,11 @@ "@elysiajs/openapi": "catalog:", "@mozilla/readability": "^0.6.0", "@neondatabase/serverless": "catalog:", + "@packrat/db": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/overpass": "workspace:*", + "@packrat/schemas": "workspace:*", "@packrat/units": "workspace:*", "@sinclair/typebox": "^0.34.15", "@types/nodemailer": "^6.4.17", diff --git a/packages/api/src/db/schema.ts b/packages/api/src/db/schema.ts index f86d24cb1d..48af2ccbc6 100644 --- a/packages/api/src/db/schema.ts +++ b/packages/api/src/db/schema.ts @@ -1,736 +1 @@ -import type { PackCategory, WeightUnit } from '@packrat/api/types/constants'; -import { type InferInsertModel, type InferSelectModel, relations, sql } from 'drizzle-orm'; -import { - type AnyPgColumn, - bigint, - boolean, - index, - integer, - jsonb, - pgEnum, - pgTable, - real, - serial, - text, - timestamp, - unique, - vector, -} from 'drizzle-orm/pg-core'; -import type { ValidationError } from '../types/validation'; - -const availabilityEnum = pgEnum('availability', ['in_stock', 'out_of_stock', 'preorder']); - -// User table -export const users = pgTable('users', { - id: text('id').primaryKey(), - name: text('name').notNull(), - email: text('email').unique().notNull(), - emailVerified: boolean('email_verified').default(false).notNull(), - image: text('image'), - role: text('role').default('USER').notNull(), - banned: boolean('banned').default(false), - banReason: text('ban_reason'), - banExpires: timestamp('ban_expires'), - firstName: text('first_name'), - lastName: text('last_name'), - avatarUrl: text('avatar_url'), - passwordHash: text('password_hash'), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); - -// Better Auth — session table -export const session = pgTable( - 'session', - { - id: text('id').primaryKey(), - expiresAt: timestamp('expires_at').notNull(), - token: text('token').notNull().unique(), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), - ipAddress: text('ip_address'), - userAgent: text('user_agent'), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - impersonatedBy: text('impersonated_by'), - }, - (table) => [index('session_userId_idx').on(table.userId)], -); - -// Better Auth — account table (OAuth + credential provider) -export const account = pgTable( - 'account', - { - id: text('id').primaryKey(), - accountId: text('account_id').notNull(), - providerId: text('provider_id').notNull(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - accessToken: text('access_token'), - refreshToken: text('refresh_token'), - idToken: text('id_token'), - accessTokenExpiresAt: timestamp('access_token_expires_at'), - refreshTokenExpiresAt: timestamp('refresh_token_expires_at'), - scope: text('scope'), - password: text('password'), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), - }, - (t) => [ - unique('account_provider_account_idx').on(t.providerId, t.accountId), - index('account_userId_idx').on(t.userId), - ], -); - -// Better Auth — verification table (email/OTP verification tokens) -export const verification = pgTable( - 'verification', - { - id: text('id').primaryKey(), - identifier: text('identifier').notNull(), - value: text('value').notNull(), - expiresAt: timestamp('expires_at').notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), - }, - (table) => [index('verification_identifier_idx').on(table.identifier)], -); - -// Better Auth — jwks table (asymmetric JWT key pairs for jwt() plugin) -export const jwks = pgTable('jwks', { - id: text('id').primaryKey(), - publicKey: text('public_key').notNull(), - privateKey: text('private_key').notNull(), - createdAt: timestamp('created_at').notNull(), -}); - -// Packs table -export const packs = pgTable('packs', { - id: text('id').primaryKey(), - name: text('name').notNull(), - description: text('description'), - category: text('category').notNull().$type(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - templateId: text('template_id').references(() => packTemplates.id), - - isPublic: boolean('is_public').notNull().default(false), - image: text('image'), - tags: jsonb('tags').$type(), - deleted: boolean('deleted').notNull().default(false), - isAIGenerated: boolean('is_ai_generated').notNull().default(false), - localCreatedAt: timestamp('local_created_at').notNull(), - localUpdatedAt: timestamp('local_updated_at').notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), // for controlling sync. controlled by server. - updatedAt: timestamp('updated_at').defaultNow().notNull(), // for controlling sync. controlled by server. -}); - -// Catalog items table -export const catalogItems = pgTable( - 'catalog_items', - { - id: serial('id').primaryKey(), - name: text('name').notNull(), - productUrl: text('product_url').notNull(), - sku: text('sku').unique().notNull(), - weight: real('weight'), - weightUnit: text('weight_unit').$type(), - description: text('description'), - categories: jsonb('categories').$type(), - images: jsonb('images').$type(), - brand: text('brand'), - model: text('model'), - ratingValue: real('rating_value'), - color: text('color'), - size: text('size'), - price: real('price'), - availability: availabilityEnum('availability'), - seller: text('seller'), - productSku: text('product_sku'), - material: text('material'), - currency: text('currency'), - condition: text('condition'), - reviewCount: integer('review_count'), - - variants: - jsonb('variants').$type< - Array<{ - attribute: string; - values: string[]; - }> - >(), - - techs: jsonb('techs').$type>(), - - links: - jsonb('links').$type< - Array<{ - title: string; - url: string; - }> - >(), - - reviews: - jsonb('reviews').$type< - Array<{ - user_name: string; - user_avatar?: string | null; - context?: Record | null; - recommends?: boolean | null; - rating: number; - title: string; - text: string; - date: string; - images?: string[] | null; - upvotes?: number | null; - downvotes?: number | null; - verified?: boolean | null; - }> - >(), - - qas: jsonb('qas').$type< - Array<{ - question: string; - user?: string | null; - date: string; - answers: Array<{ - a: string; - date: string; - user?: string | null; - upvotes?: number | null; - }>; - }> - >(), - - faqs: jsonb('faqs').$type< - Array<{ - question: string; - answer: string; - }> - >(), - embedding: vector('embedding', { dimensions: 1536 }), - - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), - }, - (table) => ({ - embeddingIndex: index('embedding_idx').using('hnsw', table.embedding.op('vector_cosine_ops')), - }), -); - -// Pack items table -export const packItems = pgTable( - 'pack_items', - { - id: text('id').primaryKey(), - name: text('name').notNull(), - description: text('description'), - weight: real('weight').notNull(), - weightUnit: text('weight_unit').notNull().$type(), - quantity: integer('quantity').default(1).notNull(), - category: text('category'), - consumable: boolean('consumable').notNull().default(false), - worn: boolean('worn').notNull().default(false), - image: text('image'), - notes: text('notes'), - packId: text('pack_id') - .references(() => packs.id, { onDelete: 'cascade' }) - .notNull(), - catalogItemId: integer('catalog_item_id').references(() => catalogItems.id), - userId: text('user_id') - .references(() => users.id) - .notNull(), - deleted: boolean('deleted').notNull().default(false), - isAIGenerated: boolean('is_ai_generated').notNull().default(false), - templateItemId: text('template_item_id').references(() => packTemplateItems.id), - embedding: vector('embedding', { dimensions: 1536 }), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), - }, - (table) => ({ - embeddingIndex: index('pack_items_embedding_idx').using( - 'hnsw', - table.embedding.op('vector_cosine_ops'), - ), - }), -); - -export const packWeightHistory = pgTable('weight_history', { - id: text('id').primaryKey(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - packId: text('pack_id') - .references(() => packs.id, { onDelete: 'cascade' }) - .notNull(), - weight: real('weight').notNull(), - localCreatedAt: timestamp('local_created_at').notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), -}); - -//Pack Template table -export const packTemplates = pgTable('pack_templates', { - id: text('id').primaryKey(), - name: text('name').notNull(), - description: text('description'), - category: text('category').notNull(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - image: text('image'), - tags: jsonb('tags').$type(), - isAppTemplate: boolean('is_app_template').notNull().default(false), - deleted: boolean('deleted').notNull().default(false), - contentSource: text('content_source'), - contentId: text('content_id'), - - localCreatedAt: timestamp('local_created_at').notNull(), - localUpdatedAt: timestamp('local_updated_at').notNull(), - - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); - -//Pack Template Item table -export const packTemplateItems = pgTable('pack_template_items', { - id: text('id').primaryKey(), - name: text('name').notNull(), - description: text('description'), - weight: real('weight').notNull(), - weightUnit: text('weight_unit').notNull().$type(), - quantity: integer('quantity').default(1).notNull(), - category: text('category'), - consumable: boolean('consumable').notNull().default(false), - worn: boolean('worn').notNull().default(false), - image: text('image'), - notes: text('notes'), - packTemplateId: text('pack_template_id') - .references(() => packTemplates.id, { onDelete: 'cascade' }) - .notNull(), - catalogItemId: integer('catalog_item_id').references(() => catalogItems.id), - userId: text('user_id') - .references(() => users.id) - .notNull(), - deleted: boolean('deleted').notNull().default(false), - - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); - -// Trail condition reports table -export const trailConditionReports = pgTable( - 'trail_condition_reports', - { - id: text('id').primaryKey(), - trailName: text('trail_name').notNull(), - trailRegion: text('trail_region'), - surface: text('surface').notNull(), // paved | gravel | dirt | rocky | snow | mud - overallCondition: text('overall_condition').notNull(), // excellent | good | fair | poor - hazards: jsonb('hazards').$type().notNull().default([]), - waterCrossings: integer('water_crossings').notNull().default(0), - waterCrossingDifficulty: text('water_crossing_difficulty'), // easy | moderate | difficult - notes: text('notes'), - photos: jsonb('photos').$type().notNull().default([]), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - tripId: text('trip_id').references(() => trips.id, { onDelete: 'set null' }), - deleted: boolean('deleted').notNull().default(false), - localCreatedAt: timestamp('local_created_at').notNull(), - localUpdatedAt: timestamp('local_updated_at').notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), - }, - (table) => ({ - userIdIdx: index('trail_condition_reports_user_id_idx').on(table.userId), - activeCreatedIdx: index('trail_condition_reports_active_created_idx').on( - table.deleted, - table.createdAt.desc(), - ), - trailNameIdx: index('trail_condition_reports_trail_name_idx').on(table.trailName), - // Partial index used to keep trip deletes (ON DELETE SET NULL) fast by - // avoiding a sequential scan on trail_condition_reports. - tripIdIdx: index('trail_condition_reports_trip_id_idx') - .on(table.tripId) - .where(sql`${table.tripId} IS NOT NULL`), - }), -); - -export const trips = pgTable('trips', { - id: text('id').primaryKey(), - name: text('name').notNull(), - description: text('description'), - startDate: timestamp('start_date'), - endDate: timestamp('end_date'), - location: jsonb('location').$type<{ latitude: number; longitude: number; name?: string }>(), - notes: text('notes'), - userId: text('user_id') - .references(() => users.id) - .notNull(), - packId: text('pack_id').references(() => packs.id, { onDelete: 'set null' }), - trailOsmId: bigint('trail_osm_id', { mode: 'bigint' }), - localCreatedAt: timestamp('local_created_at').notNull(), - localUpdatedAt: timestamp('local_updated_at').notNull(), - deleted: boolean('deleted').notNull().default(false), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); - -// Define relations - -export const packsRelations = relations(packs, ({ one, many }) => ({ - user: one(users, { - fields: [packs.userId], - references: [users.id], - }), - items: many(packItems), -})); - -export const packItemsRelations = relations(packItems, ({ one }) => ({ - pack: one(packs, { - fields: [packItems.packId], - references: [packs.id], - }), - user: one(users, { - fields: [packItems.userId], - references: [users.id], - }), - catalogItem: one(catalogItems, { - fields: [packItems.catalogItemId], - references: [catalogItems.id], - }), -})); - -export const catalogItemsRelations = relations(catalogItems, ({ many }) => ({ - packItems: many(packItems), - etlJobs: many(catalogItemEtlJobs), -})); - -export const packWeightHistoryRelations = relations(packWeightHistory, ({ one }) => ({ - pack: one(packs, { - fields: [packWeightHistory.packId], - references: [packs.id], - }), -})); -// Trips relations -export const tripsRelations = relations(trips, ({ one }) => ({ - user: one(users, { - fields: [trips.userId], - references: [users.id], - }), - pack: one(packs, { - fields: [trips.packId], - references: [packs.id], - }), -})); - -// Reported content table -export const reportedContent = pgTable('reported_content', { - id: serial('id').primaryKey(), - userId: text('user_id') - .references(() => users.id) - .notNull(), - userQuery: text('user_query').notNull(), - aiResponse: text('ai_response').notNull(), - reason: text('reason').notNull(), - userComment: text('user_comment'), - status: text('status').default('pending').notNull(), // pending, reviewed, dismissed - reviewed: boolean('reviewed').default(false), - reviewedBy: text('reviewed_by').references(() => users.id), - reviewedAt: timestamp('reviewed_at'), - createdAt: timestamp('created_at').defaultNow().notNull(), -}); - -export const reportedContentRelations = relations(reportedContent, ({ one }) => ({ - user: one(users, { - fields: [reportedContent.userId], - references: [users.id], - }), - reviewer: one(users, { - fields: [reportedContent.reviewedBy], - references: [users.id], - }), -})); - -export const packTemplatesRelations = relations(packTemplates, ({ one, many }) => ({ - user: one(users, { - fields: [packTemplates.userId], - references: [users.id], - }), - items: many(packTemplateItems), -})); - -export const packTemplateItemsRelations = relations(packTemplateItems, ({ one }) => ({ - template: one(packTemplates, { - fields: [packTemplateItems.packTemplateId], - references: [packTemplates.id], - }), - user: one(users, { - fields: [packTemplateItems.userId], - references: [users.id], - }), - catalogItem: one(catalogItems, { - fields: [packTemplateItems.catalogItemId], - references: [catalogItems.id], - }), -})); - -export const invalidItemLogs = pgTable('invalid_item_logs', { - id: serial('id').primaryKey(), - jobId: text('job_id') - .references(() => etlJobs.id) - .notNull(), - errors: jsonb('errors').notNull().$type(), - rawData: jsonb('raw_data').notNull(), - rowIndex: integer('row_index').notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), -}); - -export type InvalidItemLog = typeof invalidItemLogs.$inferSelect; -export type NewInvalidItemLog = typeof invalidItemLogs.$inferInsert; - -const etlJobStatusEnum = pgEnum('etl_job_status', ['running', 'completed', 'failed']); - -export const etlJobs = pgTable( - 'etl_jobs', - { - id: text('id').primaryKey(), - status: etlJobStatusEnum('status').notNull(), - source: text('source').notNull(), - filename: text('filename').notNull(), - startedAt: timestamp('started_at').notNull(), - completedAt: timestamp('completed_at'), - totalProcessed: integer('total_processed'), - totalValid: integer('total_valid'), - totalInvalid: integer('total_invalid'), - scraperRevision: text('scraper_revision').notNull(), // Git commit SHA or tag - }, - (table) => ({ - scraperRevisionIdx: index('etl_jobs_scraper_revision_idx').on(table.scraperRevision), - }), -); - -export type ETLJob = typeof etlJobs.$inferSelect; -export type NewETLJob = typeof etlJobs.$inferInsert; - -export const etlJobsRelations = relations(etlJobs, ({ many }) => ({ - logs: many(invalidItemLogs), - catalogItems: many(catalogItemEtlJobs), -})); - -export const invalidItemLogsRelations = relations(invalidItemLogs, ({ one }) => ({ - job: one(etlJobs, { - fields: [invalidItemLogs.jobId], - references: [etlJobs.id], - }), -})); - -export const catalogItemEtlJobs = pgTable('catalog_item_etl_jobs', { - id: serial('id').primaryKey(), - catalogItemId: integer('catalog_item_id') - .references(() => catalogItems.id, { onDelete: 'cascade' }) - .notNull(), - etlJobId: text('etl_job_id') - .references(() => etlJobs.id, { onDelete: 'cascade' }) - .notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), -}); - -export const catalogItemEtlJobsRelations = relations(catalogItemEtlJobs, ({ one }) => ({ - catalogItem: one(catalogItems, { - fields: [catalogItemEtlJobs.catalogItemId], - references: [catalogItems.id], - }), - etlJob: one(etlJobs, { - fields: [catalogItemEtlJobs.etlJobId], - references: [etlJobs.id], - }), -})); - -// Infer models from tables -export type User = InferSelectModel; -export type NewUser = InferInsertModel; - -export type Session = InferSelectModel; -export type NewSession = InferInsertModel; - -export type Account = InferSelectModel; -export type NewAccount = InferInsertModel; - -export type Verification = InferSelectModel; -export type NewVerification = InferInsertModel; - -export type Jwks = InferSelectModel; -export type NewJwks = InferInsertModel; - -export type Pack = InferSelectModel; -export type PackWithItems = Pack & { - items: PackItem[]; -}; -export type NewPack = InferInsertModel; - -export type CatalogItem = InferSelectModel; -export type NewCatalogItem = InferInsertModel; - -export type PackItem = InferSelectModel; -export type NewPackItem = InferInsertModel; - -export type ReportedContent = InferSelectModel; -export type NewReportedContent = InferInsertModel; - -export type PackTemplate = InferSelectModel; -export type NewPackTemplate = InferInsertModel; - -export type PackTemplateItem = InferSelectModel; -export type NewPackTemplateItem = InferInsertModel; - -export const trailConditionReportsRelations = relations(trailConditionReports, ({ one }) => ({ - user: one(users, { - fields: [trailConditionReports.userId], - references: [users.id], - }), - trip: one(trips, { - fields: [trailConditionReports.tripId], - references: [trips.id], - }), -})); - -export type TrailConditionReport = InferSelectModel; -export type NewTrailConditionReport = InferInsertModel; - -export type Trip = InferSelectModel; -export type NewTrip = InferInsertModel; - -export type PackTemplateWithItems = PackTemplate & { - items: PackTemplateItem[]; -}; - -// Social Feed tables - -// Posts table -export const posts = pgTable('posts', { - id: serial('id').primaryKey(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - caption: text('caption'), - images: jsonb('images').$type().notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); - -// Post likes table -export const postLikes = pgTable( - 'post_likes', - { - id: serial('id').primaryKey(), - postId: integer('post_id') - .references(() => posts.id, { onDelete: 'cascade' }) - .notNull(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), - }, - (table) => ({ - uniquePostUser: unique('post_likes_post_id_user_id_unique').on(table.postId, table.userId), - }), -); - -// Post comments table -export const postComments = pgTable('post_comments', { - id: serial('id').primaryKey(), - postId: integer('post_id') - .references(() => posts.id, { onDelete: 'cascade' }) - .notNull(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - content: text('content').notNull(), - parentCommentId: integer('parent_comment_id').references((): AnyPgColumn => postComments.id, { - onDelete: 'cascade', - }), - createdAt: timestamp('created_at').defaultNow().notNull(), - updatedAt: timestamp('updated_at').defaultNow().notNull(), -}); - -// Comment likes table -export const commentLikes = pgTable( - 'comment_likes', - { - id: serial('id').primaryKey(), - commentId: integer('comment_id') - .references(() => postComments.id, { onDelete: 'cascade' }) - .notNull(), - userId: text('user_id') - .references(() => users.id, { onDelete: 'cascade' }) - .notNull(), - createdAt: timestamp('created_at').defaultNow().notNull(), - }, - (table) => ({ - uniqueCommentUser: unique('comment_likes_comment_id_user_id_unique').on( - table.commentId, - table.userId, - ), - }), -); - -// Relations -export const postsRelations = relations(posts, ({ one, many }) => ({ - user: one(users, { - fields: [posts.userId], - references: [users.id], - }), - likes: many(postLikes), - comments: many(postComments), -})); - -export const postLikesRelations = relations(postLikes, ({ one }) => ({ - post: one(posts, { - fields: [postLikes.postId], - references: [posts.id], - }), - user: one(users, { - fields: [postLikes.userId], - references: [users.id], - }), -})); - -export const postCommentsRelations = relations(postComments, ({ one, many }) => ({ - post: one(posts, { - fields: [postComments.postId], - references: [posts.id], - }), - user: one(users, { - fields: [postComments.userId], - references: [users.id], - }), - likes: many(commentLikes), -})); - -export const commentLikesRelations = relations(commentLikes, ({ one }) => ({ - comment: one(postComments, { - fields: [commentLikes.commentId], - references: [postComments.id], - }), - user: one(users, { - fields: [commentLikes.userId], - references: [users.id], - }), -})); - -// Infer types for social feed -export type Post = InferSelectModel; -export type NewPost = InferInsertModel; - -export type PostLike = InferSelectModel; -export type NewPostLike = InferInsertModel; - -export type PostComment = InferSelectModel; -export type NewPostComment = InferInsertModel; - -export type CommentLike = InferSelectModel; -export type NewCommentLike = InferInsertModel; +export * from '@packrat/db/schema'; diff --git a/packages/api/src/db/zod-schemas.ts b/packages/api/src/db/zod-schemas.ts index fcf00731bf..a49150789f 100644 --- a/packages/api/src/db/zod-schemas.ts +++ b/packages/api/src/db/zod-schemas.ts @@ -1,46 +1 @@ -import { createInsertSchema, createSelectSchema } from 'drizzle-zod'; -import { - catalogItemEtlJobs, - catalogItems, - etlJobs, - invalidItemLogs, - packItems, - packs, - packTemplateItems, - packTemplates, - packWeightHistory, - reportedContent, - users, -} from './schema'; - -// User schemas -export const selectUserSchema = createSelectSchema(users); -export const insertUserSchema = createInsertSchema(users); - -// Pack schemas -export const selectPackSchema = createSelectSchema(packs); -export const insertPackSchema = createInsertSchema(packs); -export const selectPackItemSchema = createSelectSchema(packItems); -export const insertPackItemSchema = createInsertSchema(packItems); -export const selectPackWeightHistorySchema = createSelectSchema(packWeightHistory); -export const insertPackWeightHistorySchema = createInsertSchema(packWeightHistory); - -// Catalog schemas -export const selectCatalogItemSchema = createSelectSchema(catalogItems); -export const insertCatalogItemSchema = createInsertSchema(catalogItems); - -// Pack template schemas -export const selectPackTemplateSchema = createSelectSchema(packTemplates); -export const insertPackTemplateSchema = createInsertSchema(packTemplates); -export const selectPackTemplateItemSchema = createSelectSchema(packTemplateItems); -export const insertPackTemplateItemSchema = createInsertSchema(packTemplateItems); - -// ETL and reporting schemas -export const selectReportedContentSchema = createSelectSchema(reportedContent); -export const insertReportedContentSchema = createInsertSchema(reportedContent); -export const selectInvalidItemLogSchema = createSelectSchema(invalidItemLogs); -export const insertInvalidItemLogSchema = createInsertSchema(invalidItemLogs); -export const selectEtlJobSchema = createSelectSchema(etlJobs); -export const insertEtlJobSchema = createInsertSchema(etlJobs); -export const selectCatalogItemEtlJobSchema = createSelectSchema(catalogItemEtlJobs); -export const insertCatalogItemEtlJobSchema = createInsertSchema(catalogItemEtlJobs); +export * from '@packrat/db/zod-schemas'; diff --git a/packages/api/src/schemas/ai.ts b/packages/api/src/schemas/ai.ts index 1b63c5ca22..713838febe 100644 --- a/packages/api/src/schemas/ai.ts +++ b/packages/api/src/schemas/ai.ts @@ -1,34 +1 @@ -import { z } from 'zod'; - -export const RagSearchQuerySchema = z.object({ - q: z.string().min(1), - limit: z.coerce.number().int().min(1).max(100).optional().default(5), -}); - -export const WebSearchQuerySchema = z.object({ - q: z.string().min(1), -}); - -export const RagSearchResponseSchema = z.object({ - object: z.string(), - search_query: z.string(), - has_more: z.boolean(), - next_page: z.string().nullable(), - data: z.array( - z.object({ - filename: z.string(), - url: z.string(), - score: z.number().optional(), - content: z.array(z.unknown()).optional(), - }), - ), -}); - -export const WebSearchResponseSchema = z.object({ - answer: z.string(), - sources: z.array(z.unknown()), -}); - -export const ErrorResponseSchema = z.object({ - error: z.string(), -}); +export * from '@packrat/schemas/ai'; diff --git a/packages/api/src/schemas/auth.ts b/packages/api/src/schemas/auth.ts index b6784b07fb..a270614446 100644 --- a/packages/api/src/schemas/auth.ts +++ b/packages/api/src/schemas/auth.ts @@ -1,138 +1 @@ -import { z } from 'zod'; - -export const LoginRequestSchema = z.object({ - email: z.string().email(), - password: z.string().min(8), -}); - -export const LoginResponseSchema = z.object({ - success: z.boolean(), - accessToken: z.string(), - refreshToken: z.string(), - user: z.object({ - id: z.string(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), - emailVerified: z.boolean().nullable(), - }), -}); - -export const RegisterRequestSchema = z.object({ - email: z.string().email(), - password: z.string().min(8), - firstName: z.string().optional(), - lastName: z.string().optional(), -}); - -export const RegisterResponseSchema = z.object({ - success: z.boolean(), - message: z.string(), - userId: z.string(), -}); - -export const VerifyEmailRequestSchema = z.object({ - email: z.string().email(), - code: z.string().length(5), -}); - -export const VerifyEmailResponseSchema = z.object({ - success: z.boolean(), - message: z.string(), - accessToken: z.string(), - refreshToken: z.string(), - user: z.object({ - id: z.string(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), - emailVerified: z.boolean().nullable(), - }), -}); - -export const RefreshTokenRequestSchema = z.object({ - refreshToken: z.string(), -}); - -export const RefreshTokenResponseSchema = z.object({ - success: z.boolean(), - accessToken: z.string(), - refreshToken: z.string(), - user: z.object({ - id: z.string(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), - emailVerified: z.boolean().nullable(), - role: z.string().nullable(), - }), -}); - -export const ForgotPasswordRequestSchema = z.object({ - email: z.string().email(), -}); - -export const ForgotPasswordResponseSchema = z.object({ - success: z.boolean(), - message: z.string(), -}); - -export const ResetPasswordRequestSchema = z.object({ - email: z.string().email(), - code: z.string().length(5), - newPassword: z.string().min(8), -}); - -export const ResetPasswordResponseSchema = z.object({ - success: z.boolean(), - message: z.string(), -}); - -export const GoogleAuthRequestSchema = z.object({ - idToken: z.string(), -}); - -export const AppleAuthRequestSchema = z.object({ - identityToken: z.string(), - authorizationCode: z.string(), -}); - -export const SocialAuthResponseSchema = z.object({ - success: z.boolean(), - accessToken: z.string(), - refreshToken: z.string(), - user: z.object({ - id: z.string(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), - emailVerified: z.boolean().nullable(), - role: z.string().nullable(), - }), - isNewUser: z.boolean().optional(), -}); - -export const LogoutRequestSchema = z.object({ - refreshToken: z.string(), -}); - -export const LogoutResponseSchema = z.object({ - success: z.boolean(), - message: z.string(), -}); - -export const MeResponseSchema = z.object({ - success: z.boolean(), - user: z.object({ - id: z.string(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), - emailVerified: z.boolean().nullable(), - }), -}); - -export const ErrorResponseSchema = z.object({ - error: z.string(), - code: z.string().optional(), -}); +export * from '@packrat/schemas/auth'; diff --git a/packages/api/src/schemas/catalog.ts b/packages/api/src/schemas/catalog.ts index 3b9aff1550..6d7c49a83a 100644 --- a/packages/api/src/schemas/catalog.ts +++ b/packages/api/src/schemas/catalog.ts @@ -1,338 +1 @@ -import { WEIGHT_UNITS } from '@packrat/api/types/constants'; -import { isString } from '@packrat/guards'; -import { z } from 'zod'; - -// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); - -export const ErrorResponseSchema = z.object({ - error: z.string(), - code: z.string().optional(), -}); - -export const CatalogItemSchema = z.object({ - id: z.number().int().positive(), - name: z.string(), - productUrl: z.string(), - sku: z.string(), - weight: z.number(), - weightUnit: z.enum(WEIGHT_UNITS), - description: z.string().nullable(), - categories: z.array(z.string()).nullable(), - images: z.array(z.string()).nullable(), - brand: z.string().nullable(), - model: z.string().nullable(), - ratingValue: z.number().nullable(), - color: z.string().nullable(), - size: z.string().nullable(), - price: z.number().nullable(), - availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).nullable(), - seller: z.string().nullable(), - productSku: z.string().nullable(), - material: z.string().nullable(), - currency: z.string().nullable(), - condition: z.string().nullable(), - reviewCount: z.number().int().nullable(), - variants: z - .array( - z.object({ - attribute: z.string(), - values: z.array(z.string()), - }), - ) - .nullable() - .optional(), - techs: z.record(z.string(), z.string()).nullable().optional(), - links: z - .array( - z.object({ - title: z.string(), - url: z.string(), - }), - ) - .nullable() - .optional(), - reviews: z - .array( - z.object({ - user_name: z.string().nullable().optional(), - user_avatar: z.string().nullable().optional(), - context: z.record(z.string(), z.string()).nullable().optional(), - recommends: z.boolean().nullable().optional(), - rating: z.number(), - title: z.string().nullable().optional(), - text: z.string().nullable().optional(), - date: z.string().nullable().optional(), - images: z.array(z.string()).nullable().optional(), - upvotes: z.number().nullable().optional(), - downvotes: z.number().nullable().optional(), - verified: z.boolean().nullable().optional(), - }), - ) - .nullable() - .optional(), - qas: z - .array( - z.object({ - question: z.string(), - user: z.string().nullable().optional(), - date: z.string(), - answers: z.array( - z.object({ - a: z.string(), - date: z.string(), - user: z.string().nullable().optional(), - upvotes: z.number().nullable().optional(), - }), - ), - }), - ) - .nullable() - .optional(), - faqs: z - .array( - z.object({ - question: z.string(), - answer: z.string(), - }), - ) - .nullable() - .optional(), - usageCount: z.number().int().min(0).optional(), - createdAt: datetimeString, - updatedAt: datetimeString, -}); - -const SortSchema = z.object({ - field: z.enum([ - 'name', - 'brand', - 'category', - 'price', - 'ratingValue', - 'createdAt', - 'updatedAt', - 'usage', - ]), - order: z.enum(['asc', 'desc']), -}); - -export const CatalogItemsQuerySchema = z.object({ - page: z.coerce.number().int().positive().optional().default(1), - limit: z.coerce.number().int().min(1).max(100).optional().default(20), - q: z.string().optional(), - category: z.string().optional(), - // Eden Treaty serializes nested objects as JSON strings in query params. - // z.preprocess parses the JSON string before Zod validates the shape. - sort: z - .preprocess((val) => { - if (isString(val)) { - try { - return JSON.parse(val); - } catch { - return undefined; - } - } - return val; - }, SortSchema.optional()) - .optional(), -}); - -export const CatalogItemsResponseSchema = z.object({ - items: z.array(CatalogItemSchema), - totalCount: z.number(), - page: z.number(), - limit: z.number(), - totalPages: z.number(), -}); - -export const CreateCatalogItemRequestSchema = z.object({ - name: z.string().min(1).max(255), - productUrl: z.string().url(), - sku: z.string(), - weight: z.number().positive(), - weightUnit: z.enum(WEIGHT_UNITS), - description: z.string().optional(), - categories: z.array(z.string()).optional(), - images: z.array(z.string()).optional(), - brand: z.string().optional(), - model: z.string().optional(), - ratingValue: z.number().min(0).max(5).optional(), - color: z.string().optional(), - size: z.string().optional(), - price: z.number().optional(), - availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).optional(), - seller: z.string().optional(), - productSku: z.string().optional(), - material: z.string().optional(), - currency: z.string().optional(), - condition: z.string().optional(), - reviewCount: z.number().min(0).optional(), - variants: z - .array( - z.object({ - attribute: z.string(), - values: z.array(z.string()), - }), - ) - .optional(), - techs: z.record(z.string(), z.string()).optional(), - links: z - .array( - z.object({ - title: z.string(), - url: z.string(), - }), - ) - .optional(), - reviews: z - .array( - z.object({ - user_name: z.string(), - user_avatar: z.string().optional(), - context: z.record(z.string(), z.string()).optional(), - recommends: z.boolean().optional(), - rating: z.number(), - title: z.string(), - text: z.string(), - date: z.string(), - images: z.array(z.string()).optional(), - upvotes: z.number().optional(), - downvotes: z.number().optional(), - verified: z.boolean().optional(), - }), - ) - .optional(), - qas: z - .array( - z.object({ - question: z.string(), - user: z.string().optional(), - date: z.string(), - answers: z.array( - z.object({ - a: z.string(), - date: z.string(), - user: z.string().optional(), - upvotes: z.number().optional(), - }), - ), - }), - ) - .optional(), - faqs: z - .array( - z.object({ - question: z.string(), - answer: z.string(), - }), - ) - .optional(), -}); - -export const UpdateCatalogItemRequestSchema = z.object({ - name: z.string().min(1).max(255).optional(), - productUrl: z.string().url().optional(), - sku: z.string().optional(), - weight: z.number().optional(), - weightUnit: z.enum(WEIGHT_UNITS).optional(), - description: z.string().optional(), - categories: z.array(z.string()).optional(), - images: z.array(z.string()).optional(), - brand: z.string().optional(), - model: z.string().optional(), - ratingValue: z.number().min(0).max(5).optional(), - color: z.string().optional(), - size: z.string().optional(), - price: z.number().optional(), - availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).optional(), - seller: z.string().optional(), - productSku: z.string().optional(), - material: z.string().optional(), - currency: z.string().optional(), - condition: z.string().optional(), - reviewCount: z.number().min(0).optional(), - variants: z - .array( - z.object({ - attribute: z.string(), - values: z.array(z.string()), - }), - ) - .optional(), - techs: z.record(z.string(), z.string()).optional(), - links: z - .array( - z.object({ - title: z.string(), - url: z.string(), - }), - ) - .optional(), - reviews: z - .array( - z.object({ - user_name: z.string(), - user_avatar: z.string().optional(), - context: z.record(z.string(), z.string()).optional(), - recommends: z.boolean().optional(), - rating: z.number(), - title: z.string(), - text: z.string(), - date: z.string(), - images: z.array(z.string()).optional(), - upvotes: z.number().optional(), - downvotes: z.number().optional(), - verified: z.boolean().optional(), - }), - ) - .optional(), - qas: z - .array( - z.object({ - question: z.string(), - user: z.string().optional(), - date: z.string(), - answers: z.array( - z.object({ - a: z.string(), - date: z.string(), - user: z.string().optional(), - upvotes: z.number().optional(), - }), - ), - }), - ) - .optional(), - faqs: z - .array( - z.object({ - question: z.string(), - answer: z.string(), - }), - ) - .optional(), -}); - -export const CatalogCategoriesResponseSchema = z.array(z.string()); - -export const VectorSearchQuerySchema = z.object({ - q: z.string().min(1), - limit: z.coerce.number().int().min(1).max(50).optional().default(10), - offset: z.coerce.number().int().min(0).optional().default(0), -}); - -export const SimilarItemSchema = CatalogItemSchema.extend({ - similarity: z.number().min(0).max(1), -}); - -export const VectorSearchResponseSchema = z.object({ - items: z.array(SimilarItemSchema), - total: z.number(), - limit: z.number(), - offset: z.number(), - nextOffset: z.number(), -}); +export * from '@packrat/schemas/catalog'; diff --git a/packages/api/src/schemas/chat.ts b/packages/api/src/schemas/chat.ts index 9c408b969f..1cfa4ba65c 100644 --- a/packages/api/src/schemas/chat.ts +++ b/packages/api/src/schemas/chat.ts @@ -1,63 +1 @@ -import { z } from 'zod'; - -export const ErrorResponseSchema = z.object({ - error: z.string(), - code: z.string().optional(), -}); - -export const SuccessResponseSchema = z.object({ - success: z.boolean(), -}); - -export const ChatMessageSchema = z.object({ - role: z.enum(['user', 'assistant', 'system']), - content: z.string(), -}); - -export const ChatRequestSchema = z.any(); -// .oRbject({ -// messages: z.array(ChatMessageSchema), -// contextType: z.string().optional(), -// itemId: z.string().optional(), -// packId: z.string().optional(), -// location: z.string().optional(), -// }) -// ; - -export const ReportedContentUserSchema = z.object({ - id: z.string(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), -}); - -export const ReportedContentSchema = z.object({ - id: z.number(), - userId: z.string(), - userQuery: z.string(), - aiResponse: z.string(), - reason: z.string(), - userComment: z.string().nullable(), - status: z.string(), - reviewed: z.boolean().nullable(), - reviewedBy: z.string().nullable(), - reviewedAt: z.string().datetime().nullable(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - user: ReportedContentUserSchema, -}); - -export const CreateReportRequestSchema = z.object({ - userQuery: z.string(), - aiResponse: z.string(), - reason: z.string(), - userComment: z.string().optional(), -}); - -export const ReportsResponseSchema = z.object({ - reportedItems: z.array(ReportedContentSchema), -}); - -export const UpdateReportStatusRequestSchema = z.object({ - status: z.string(), -}); +export * from '@packrat/schemas/chat'; diff --git a/packages/api/src/schemas/feed.ts b/packages/api/src/schemas/feed.ts index 4b4b5f31a7..f2d91373fe 100644 --- a/packages/api/src/schemas/feed.ts +++ b/packages/api/src/schemas/feed.ts @@ -1,64 +1 @@ -import { z } from 'zod'; - -export const PostAuthorSchema = z.object({ - id: z.string(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), -}); - -export const PostSchema = z.object({ - id: z.number().int(), - userId: z.string(), - caption: z.string().nullable(), - images: z.array(z.string()), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - author: PostAuthorSchema.optional(), - likeCount: z.number().int(), - commentCount: z.number().int(), - likedByMe: z.boolean(), -}); - -export const CreatePostRequestSchema = z.object({ - caption: z.string().max(2000).optional(), - images: z.array(z.string()).min(1).max(10), -}); - -export const FeedResponseSchema = z.object({ - items: z.array(PostSchema), - page: z.number().int(), - limit: z.number().int(), - total: z.number().int(), - totalPages: z.number().int(), -}); - -export const CommentSchema = z.object({ - id: z.number().int(), - postId: z.number().int(), - userId: z.string(), - content: z.string(), - parentCommentId: z.number().int().nullable(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - author: PostAuthorSchema.optional(), - likeCount: z.number().int(), - likedByMe: z.boolean(), -}); - -export const CreateCommentRequestSchema = z.object({ - content: z.string().min(1).max(1000), - parentCommentId: z.number().int().optional(), -}); - -export const CommentsResponseSchema = z.object({ - items: z.array(CommentSchema), - page: z.number().int(), - limit: z.number().int(), - total: z.number().int(), - totalPages: z.number().int(), -}); - -export const LikeToggleResponseSchema = z.object({ - liked: z.boolean(), - likeCount: z.number().int(), -}); +export * from '@packrat/schemas/feed'; diff --git a/packages/api/src/schemas/guides.ts b/packages/api/src/schemas/guides.ts index 3450de133c..de9168b02d 100644 --- a/packages/api/src/schemas/guides.ts +++ b/packages/api/src/schemas/guides.ts @@ -1,66 +1 @@ -import { z } from 'zod'; - -export const ErrorResponseSchema = z.object({ - error: z.string(), - code: z.string().optional(), -}); - -export const GuideSchema = z.object({ - id: z.string(), - key: z.string(), - title: z.string(), - category: z.string(), - categories: z.array(z.string()).optional(), - description: z.string(), - author: z.string().optional(), - readingTime: z.number().optional(), - difficulty: z.string().optional(), - content: z.string().optional(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), -}); - -export const GuideDetailSchema = GuideSchema.extend({ - content: z.string(), -}); - -export const GuidesQuerySchema = z.object({ - page: z.coerce.number().int().positive().optional().default(1), - limit: z.coerce.number().int().positive().optional().default(20), - category: z.string().optional(), - sort: z - .object({ - field: z.enum(['title', 'category', 'createdAt', 'updatedAt']), - order: z.enum(['asc', 'desc']), - }) - .optional(), -}); - -export const GuidesResponseSchema = z.object({ - items: z.array(GuideSchema), - totalCount: z.number(), - page: z.number(), - limit: z.number(), - totalPages: z.number(), -}); - -export const GuideSearchQuerySchema = z.object({ - q: z.string().min(1), - page: z.coerce.number().int().positive().optional().default(1), - limit: z.coerce.number().int().positive().optional().default(20), - category: z.string().optional(), -}); - -export const GuideSearchResponseSchema = z.object({ - items: z.array(GuideSchema), - totalCount: z.number(), - page: z.number(), - limit: z.number(), - totalPages: z.number(), - query: z.string(), -}); - -export const GuideCategoriesResponseSchema = z.object({ - categories: z.array(z.string()), - count: z.number().int(), -}); +export * from '@packrat/schemas/guides'; diff --git a/packages/api/src/schemas/imageDetection.ts b/packages/api/src/schemas/imageDetection.ts index 4d4b399256..edbb760fb8 100644 --- a/packages/api/src/schemas/imageDetection.ts +++ b/packages/api/src/schemas/imageDetection.ts @@ -1,25 +1 @@ -import { z } from 'zod'; -import { CatalogItemSchema } from './catalog'; - -export const DetectedItemSchema = z.object({ - name: z.string(), - description: z.string(), - quantity: z.number().int().positive(), - category: z.string(), - consumable: z.boolean().default(false).describe('Whether the item is consumable'), - worn: z.boolean().default(false).describe('Whether the item is worn'), - notes: z.string().nullable().optional(), - confidence: z.number().min(0).max(1), -}); - -export const DetectedItemWithMatchesSchema = z.object({ - detected: DetectedItemSchema, - catalogMatches: z.array(CatalogItemSchema), -}); - -export const AnalyzeImageRequestSchema = z.object({ - image: z.string(), - matchLimit: z.number().int().min(1).max(10).optional().default(3), -}); - -export const AnalyzeImageResponseSchema = z.array(DetectedItemWithMatchesSchema); +export * from '@packrat/schemas/imageDetection'; diff --git a/packages/api/src/schemas/packTemplates.ts b/packages/api/src/schemas/packTemplates.ts index 6ffe2de886..9fe338b3a9 100644 --- a/packages/api/src/schemas/packTemplates.ts +++ b/packages/api/src/schemas/packTemplates.ts @@ -1,116 +1 @@ -import { z } from 'zod'; - -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); - -export const ErrorResponseSchema = z.object({ - error: z.string(), - code: z.string().optional(), - existingTemplateId: z.string().optional(), -}); - -export const PackTemplateSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().nullable(), - category: z.string(), - userId: z.string(), - image: z.string().nullable(), - tags: z.array(z.string()).nullable(), - isAppTemplate: z.boolean(), - deleted: z.boolean(), - localCreatedAt: datetimeString, - localUpdatedAt: datetimeString, - createdAt: datetimeString, - updatedAt: datetimeString, - contentSource: z.string().nullable(), - contentId: z.string().nullable(), -}); - -export const PackTemplateItemSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().nullable(), - weight: z.number(), - weightUnit: z.string(), - quantity: z.number(), - category: z.string().nullable(), - consumable: z.boolean(), - worn: z.boolean(), - image: z.string().nullable(), - notes: z.string().nullable(), - packTemplateId: z.string(), - catalogItemId: z.number().nullable(), - userId: z.string(), - deleted: z.boolean(), - createdAt: datetimeString, - updatedAt: datetimeString, -}); - -export const PackTemplateWithItemsSchema = PackTemplateSchema.extend({ - items: z.array(PackTemplateItemSchema), -}); - -export const CreatePackTemplateRequestSchema = z.object({ - id: z.string(), - name: z.string().min(1).max(255), - description: z.string().optional(), - category: z.string().min(1), - image: z.string().url().optional(), - tags: z.array(z.string()).optional(), - isAppTemplate: z.boolean().optional(), - localCreatedAt: z.string().datetime(), - localUpdatedAt: z.string().datetime(), -}); - -export const UpdatePackTemplateRequestSchema = z.object({ - name: z.string().min(1).max(255).optional(), - description: z.string().nullable(), - category: z.string().min(1).optional(), - image: z.string().url().nullable(), - tags: z.array(z.string()).nullable(), - isAppTemplate: z.boolean().optional(), - deleted: z.boolean().optional(), - localUpdatedAt: z.string().datetime().optional(), -}); - -export const CreatePackTemplateItemRequestSchema = z.object({ - id: z.string(), - name: z.string().min(1).max(255), - description: z.string().optional(), - weight: z.number().min(0), - weightUnit: z.enum(['g', 'kg', 'lb', 'oz']), - quantity: z.number().int().min(1).optional().default(1), - category: z.string().optional(), - consumable: z.boolean().optional().default(false), - worn: z.boolean().optional().default(false), - image: z.string().nullish(), - notes: z.string().optional(), -}); - -export const UpdatePackTemplateItemRequestSchema = z.object({ - name: z.string().min(1).max(255).optional(), - description: z.string().optional(), - weight: z.number().min(0).optional(), - weightUnit: z.enum(['g', 'kg', 'lb', 'oz']).optional(), - quantity: z.number().int().min(1).optional(), - category: z.string().optional(), - consumable: z.boolean().optional(), - worn: z.boolean().optional(), - image: z.string().url().optional(), - notes: z.string().optional(), - deleted: z.boolean().optional(), -}); - -export const SuccessResponseSchema = z.object({ - success: z.boolean(), -}); - -export const GenerateFromOnlineContentRequestSchema = z.object({ - contentUrl: z.string().url(), - isAppTemplate: z.boolean().optional().default(true), -}); - -export const GenerateFromOnlineContentResponseSchema = PackTemplateWithItemsSchema; +export * from '@packrat/schemas/packTemplates'; diff --git a/packages/api/src/schemas/packs.ts b/packages/api/src/schemas/packs.ts index 6f855887f7..5a0e2f87f9 100644 --- a/packages/api/src/schemas/packs.ts +++ b/packages/api/src/schemas/packs.ts @@ -1,172 +1 @@ -import { PACK_CATEGORIES, WEIGHT_UNITS } from '@packrat/api/types/constants'; -import { z } from 'zod'; - -// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); - -export const PackItemSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().nullable(), - weight: z.number(), - weightUnit: z.enum(WEIGHT_UNITS), - quantity: z.number().int().min(1), - category: z.string().nullable(), - consumable: z.boolean(), - worn: z.boolean(), - image: z.string().nullable(), - notes: z.string().nullable(), - packId: z.string(), - catalogItemId: z.number().int().nullable(), - userId: z.string(), - deleted: z.boolean(), - isAIGenerated: z.boolean(), - templateItemId: z.string().nullable(), - createdAt: datetimeString, - updatedAt: datetimeString, -}); - -export const PackSchema = z.object({ - id: z.string(), - userId: z.string(), - name: z.string(), - description: z.string().nullable(), - category: z.enum(PACK_CATEGORIES).nullable(), - isPublic: z.boolean(), - image: z.string().nullable(), - tags: z.array(z.string()).nullable(), - templateId: z.string().nullable().optional(), - deleted: z.boolean(), - isAIGenerated: z.boolean(), - localCreatedAt: datetimeString.optional(), - localUpdatedAt: datetimeString.optional(), - createdAt: datetimeString, - updatedAt: datetimeString, - items: z.array(PackItemSchema).optional(), -}); - -export const PackWithWeightsSchema = PackSchema.extend({ - totalWeight: z.number(), - baseWeight: z.number(), -}); - -export const CreatePackRequestSchema = z.object({ - name: z.string().min(1).max(255), - description: z.string().optional(), - category: z.string().optional(), - isPublic: z.boolean().optional().default(false), - image: z.string().nullish(), - tags: z.array(z.string()).optional(), -}); - -export const UpdatePackRequestSchema = z.object({ - name: z.string().min(1).max(255).optional(), - description: z.string().optional(), - category: z.string().optional(), - isPublic: z.boolean().optional(), - image: z.string().nullish(), - tags: z.array(z.string()).optional(), - deleted: z.boolean().optional(), -}); - -export const CreatePackItemRequestSchema = z.object({ - name: z.string().min(1).max(255), - description: z.string().optional(), - weight: z.number(), - weightUnit: z.enum(WEIGHT_UNITS).default('g'), - quantity: z.number().int().min(1).default(1), - category: z.string().optional(), - consumable: z.boolean().optional().default(false), - worn: z.boolean().optional().default(false), - image: z.string().nullish(), - notes: z.string().nullish(), - catalogItemId: z.number().int().nullish(), -}); - -export const UpdatePackItemRequestSchema = z.object({ - name: z.string().min(1).max(255).optional(), - description: z.string().optional(), - weight: z.number().optional(), - weightUnit: z.enum(WEIGHT_UNITS).optional(), - quantity: z.number().int().min(1).optional(), - category: z.string().optional(), - consumable: z.boolean().optional(), - worn: z.boolean().optional(), - image: z.string().nullish(), - notes: z.string().nullish(), - catalogItemId: z.number().int().nullish(), - deleted: z.boolean().optional(), -}); - -export const PackListResponseSchema = z.object({ - packs: z.array(PackWithWeightsSchema), - total: z.number(), - page: z.number(), - limit: z.number(), - totalPages: z.number(), -}); - -export const ItemSuggestionsResponseSchema = z.object({ - suggestions: z.array( - z.object({ - name: z.string(), - category: z.string(), - weight: z.number().optional(), - description: z.string().optional(), - confidence: z.number().min(0).max(1), - }), - ), -}); - -export const GapAnalysisRequestSchema = z.object({ - destination: z.string().optional(), - tripType: z.string().optional(), - duration: z.string().optional(), - startDate: z.string().optional(), - endDate: z.string().optional(), -}); - -export const GapAnalysisItemSchema = z.object({ - suggestion: z.string(), - reason: z.string(), - consumable: z.boolean(), - worn: z.boolean(), - category: z.string().optional(), - priority: z.enum(['must-have', 'nice-to-have', 'optional']).optional(), -}); - -export const GapAnalysisResponseSchema = z.object({ - gaps: z.array(GapAnalysisItemSchema), - summary: z.string().optional(), -}); - -// Body schemas mirroring the inline route schemas (exported so stores/clients -// can use ApiBody<> or direct z.infer<> without importing from route files). -export const CreatePackBodySchema = CreatePackRequestSchema.extend({ - id: z.string(), - localCreatedAt: z.string().datetime(), - localUpdatedAt: z.string().datetime(), -}); - -export const UpdatePackBodySchema = UpdatePackRequestSchema.extend({ - localUpdatedAt: z.string().datetime().optional(), -}); - -export const PackWeightHistoryResponseSchema = z.object({ - id: z.string(), - packId: z.string(), - userId: z.string(), - weight: z.number(), - localCreatedAt: datetimeString.optional(), - createdAt: datetimeString, - updatedAt: datetimeString, -}); - -export const CreatePackWeightHistoryBodySchema = z.object({ - id: z.string(), - weight: z.number(), - localCreatedAt: z.string().datetime(), -}); +export * from '@packrat/schemas/packs'; diff --git a/packages/api/src/schemas/seasonSuggestions.ts b/packages/api/src/schemas/seasonSuggestions.ts index 9d7813e494..5ab283b2dc 100644 --- a/packages/api/src/schemas/seasonSuggestions.ts +++ b/packages/api/src/schemas/seasonSuggestions.ts @@ -1,38 +1 @@ -import { z } from 'zod'; - -export const SeasonSuggestionsRequestSchema = z.object({ - location: z.string(), - date: z.string(), -}); - -export const PackSuggestionSchema = z.object({ - name: z.string(), - description: z.string(), - category: z.string().nullable(), - items: z.array( - z.object({ - name: z.string(), - description: z.string().nullable(), - weight: z.number().int(), - weightUnit: z.string(), - quantity: z.number().int().min(1), - category: z.string().nullable(), - consumable: z.boolean(), - worn: z.boolean(), - image: z.string().nullable().optional(), - notes: z.string().nullable(), - catalogItemId: z.number().int().nullable(), - }), - ), -}); - -export const SeasonSuggestionsResponseSchema = z.object({ - suggestions: z.array(PackSuggestionSchema), - totalInventoryItems: z.number(), - location: z.string(), - season: z.string(), -}); - -export const ErrorResponseSchema = z.object({ - error: z.string(), -}); +export * from '@packrat/schemas/seasonSuggestions'; diff --git a/packages/api/src/schemas/trailConditions.ts b/packages/api/src/schemas/trailConditions.ts index 1dc4254775..002ae17c8c 100644 --- a/packages/api/src/schemas/trailConditions.ts +++ b/packages/api/src/schemas/trailConditions.ts @@ -1,32 +1 @@ -import { z } from 'zod'; - -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); - -export const TrailSurfaceSchema = z.enum(['paved', 'gravel', 'dirt', 'rocky', 'snow', 'mud']); -export const OverallConditionSchema = z.enum(['excellent', 'good', 'fair', 'poor']); -export const WaterCrossingDifficultySchema = z.enum(['easy', 'moderate', 'difficult']); - -export const TrailConditionReportSchema = z.object({ - id: z.string(), - trailName: z.string(), - trailRegion: z.string().nullable().optional(), - surface: TrailSurfaceSchema, - overallCondition: OverallConditionSchema, - hazards: z.array(z.string()), - waterCrossings: z.number(), - waterCrossingDifficulty: WaterCrossingDifficultySchema.nullable().optional(), - notes: z.string().nullable().optional(), - photos: z.array(z.string()), - userId: z.string().optional(), - tripId: z.string().nullable().optional(), - deleted: z.boolean(), - localCreatedAt: datetimeString.optional(), - localUpdatedAt: datetimeString.optional(), - createdAt: datetimeString.optional(), - updatedAt: datetimeString.optional(), -}); - -export type TrailConditionReport = z.infer; +export * from '@packrat/schemas/trailConditions'; diff --git a/packages/api/src/schemas/trips.ts b/packages/api/src/schemas/trips.ts index 9e4e301dc1..56fc66d575 100644 --- a/packages/api/src/schemas/trips.ts +++ b/packages/api/src/schemas/trips.ts @@ -1,61 +1 @@ -import { z } from 'zod'; - -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); - -const nullableDateString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().nullable(), -); - -export const TripLocationSchema = z.object({ - latitude: z.number(), - longitude: z.number(), - name: z.string().optional(), -}); - -export const TripSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().nullable().optional(), - notes: z.string().nullable().optional(), - location: TripLocationSchema.nullable().optional(), - startDate: nullableDateString.optional(), - endDate: nullableDateString.optional(), - userId: z.string().optional(), - packId: z.string().nullable().optional(), - deleted: z.boolean(), - localCreatedAt: datetimeString.optional(), - localUpdatedAt: datetimeString.optional(), - createdAt: datetimeString.optional(), - updatedAt: datetimeString.optional(), -}); - -export const CreateTripBodySchema = z.object({ - id: z.string(), - name: z.string().min(1).max(255), - description: z.string().nullable().optional(), - notes: z.string().nullable().optional(), - location: TripLocationSchema.nullable().optional(), - startDate: z.string().nullable().optional(), - endDate: z.string().nullable().optional(), - packId: z.string().nullable().optional(), - localCreatedAt: z.string().datetime(), - localUpdatedAt: z.string().datetime(), -}); - -export const UpdateTripBodySchema = z.object({ - name: z.string().min(1).max(255).optional(), - description: z.string().nullable().optional(), - notes: z.string().nullable().optional(), - location: TripLocationSchema.nullable().optional(), - startDate: z.string().nullable().optional(), - endDate: z.string().nullable().optional(), - packId: z.string().nullable().optional(), - localUpdatedAt: z.string().datetime().optional(), -}); - -export type TripLocation = z.infer; -export type Trip = z.infer; +export * from '@packrat/schemas/trips'; diff --git a/packages/api/src/schemas/upload.ts b/packages/api/src/schemas/upload.ts index 47b079e33f..fd8925518a 100644 --- a/packages/api/src/schemas/upload.ts +++ b/packages/api/src/schemas/upload.ts @@ -1,18 +1 @@ -import { z } from 'zod'; - -export const ErrorResponseSchema = z.object({ - error: z.string(), - code: z.string().optional(), -}); - -export const PresignedUploadQuerySchema = z.object({ - fileName: z.string().optional(), - contentType: z.string().optional(), - size: z.string().optional(), -}); - -export const PresignedUploadResponseSchema = z.object({ - url: z.string().url(), - objectKey: z.string(), - publicUrl: z.string().url(), -}); +export * from '@packrat/schemas/upload'; diff --git a/packages/api/src/schemas/users.ts b/packages/api/src/schemas/users.ts index a1c1f15bba..2b6a6d1d6b 100644 --- a/packages/api/src/schemas/users.ts +++ b/packages/api/src/schemas/users.ts @@ -1,90 +1 @@ -import { z } from 'zod'; - -// Base user schema -export const UserSchema = z.object({ - id: z.string(), - email: z.string().email(), - firstName: z.string().nullable(), - lastName: z.string().nullable(), - role: z.string().nullable().default('USER'), - emailVerified: z.boolean().nullable(), - createdAt: z.string().nullable(), - updatedAt: z.string().nullable(), - avatarUrl: z.string().nullable().optional(), -}); - -// User profile response schema -export const UserProfileSchema = z.object({ - success: z.boolean(), - user: UserSchema, -}); - -// Update user request schema -export const UpdateUserRequestSchema = z.object({ - firstName: z.string().optional(), - lastName: z.string().optional(), - email: z.string().email().optional(), - avatarUrl: z.string().nullable().optional(), -}); - -// Update user response schema -export const UpdateUserResponseSchema = z.object({ - success: z.boolean(), - message: z.string(), - user: UserSchema, -}); - -// User search query schema -export const UserSearchQuerySchema = z.object({ - q: z.string().optional(), - limit: z.number().int().positive().max(100).default(20), - offset: z.number().int().min(0).default(0), -}); - -// User list response schema -export const UserListResponseSchema = z.object({ - success: z.boolean(), - users: z.array(UserSchema), - pagination: z.object({ - total: z.number().int().min(0), - limit: z.number().int().positive(), - offset: z.number().int().min(0), - hasMore: z.boolean(), - }), -}); - -// User items response schema (for pack items belonging to user) -export const UserItemsResponseSchema = z.array( - z.object({ - id: z.string(), - name: z.string(), - quantity: z.number().int().positive().default(1), - weight: z.number().positive().nullable(), - weightUnit: z.string().default('g'), - category: z.string().nullable(), - packId: z.string(), - catalogItem: z - .object({ - id: z.number(), - name: z.string(), - brand: z.string().nullable(), - categories: z.array(z.string()).nullable(), - description: z.string().nullable(), - price: z.number().nullable(), - weight: z.number().nullable(), - images: z.array(z.string()).nullable(), - }) - .nullable(), - createdAt: z.string(), - updatedAt: z.string(), - }), -); - -// Admin user stats schema -export const AdminUserStatsSchema = z.object({ - totalUsers: z.number().int().min(0), - verifiedUsers: z.number().int().min(0), - unverifiedUsers: z.number().int().min(0), - adminUsers: z.number().int().min(0), - recentSignups: z.number().int().min(0), -}); +export * from '@packrat/schemas/users'; diff --git a/packages/api/src/schemas/weather.ts b/packages/api/src/schemas/weather.ts index 64446deef0..661cd72efd 100644 --- a/packages/api/src/schemas/weather.ts +++ b/packages/api/src/schemas/weather.ts @@ -1,259 +1 @@ -import { z } from 'zod'; - -export const ErrorResponseSchema = z.object({ - error: z.string(), - code: z.string().optional(), -}); - -export const LocationSchema = z.object({ - id: z.number(), - name: z.string(), - region: z.string(), - country: z.string(), - lat: z.number(), - lon: z.number(), -}); - -// Extended location schema for API responses -export const WeatherAPILocationSchema = z.object({ - id: z.number(), - name: z.string(), - region: z.string(), - country: z.string(), - lat: z.number(), - lon: z.number(), - tz_id: z.string().optional(), - localtime_epoch: z.number().optional(), - localtime: z.string().optional(), -}); - -export const WeatherSearchQuerySchema = z.object({ - q: z.string().optional(), -}); - -export const WeatherCoordinateQuerySchema = z.object({ - lat: z.string(), - lon: z.string(), -}); - -export const WeatherLocationIdSchema = z.object({ - id: z.string(), -}); - -export const WeatherConditionSchema = z.object({ - text: z.string(), - icon: z.string(), - code: z.number(), -}); - -// Air quality schema based on actual API response -export const AirQualitySchema = z.object({ - co: z.number(), - no2: z.number(), - o3: z.number(), - so2: z.number(), - pm2_5: z.number(), - pm10: z.number(), - 'us-epa-index': z.number(), - 'gb-defra-index': z.number(), -}); - -export const WeatherCurrentSchema = z.object({ - last_updated: z.string(), - temp_c: z.number(), - temp_f: z.number(), - condition: WeatherConditionSchema, - wind_mph: z.number(), - wind_kph: z.number(), - wind_degree: z.number(), - wind_dir: z.string(), - pressure_mb: z.number(), - pressure_in: z.number(), - precip_mm: z.number(), - precip_in: z.number(), - humidity: z.number(), - cloud: z.number(), - feelslike_c: z.number(), - feelslike_f: z.number(), - vis_km: z.number(), - vis_miles: z.number(), - uv: z.number(), - gust_mph: z.number().optional(), - gust_kph: z.number().optional(), - // Additional fields from actual API response - is_day: z.number(), - windchill_c: z.number().optional(), - windchill_f: z.number().optional(), - heatindex_c: z.number().optional(), - heatindex_f: z.number().optional(), - dewpoint_c: z.number().optional(), - dewpoint_f: z.number().optional(), - will_it_rain: z.number().optional(), - chance_of_rain: z.number().optional(), - will_it_snow: z.number().optional(), - chance_of_snow: z.number().optional(), - snow_cm: z.number().optional(), - air_quality: AirQualitySchema.optional(), - short_rad: z.number().optional(), - diff_rad: z.number().optional(), - dni: z.number().optional(), - gti: z.number().optional(), -}); - -export const WeatherDaySchema = z.object({ - maxtemp_c: z.number(), - maxtemp_f: z.number(), - mintemp_c: z.number(), - mintemp_f: z.number(), - avgtemp_c: z.number(), - avgtemp_f: z.number(), - maxwind_mph: z.number(), - maxwind_kph: z.number(), - totalprecip_mm: z.number(), - totalprecip_in: z.number(), - totalsnow_cm: z.number(), - avghumidity: z.number(), - avgvis_km: z.number(), - avgvis_miles: z.number(), - uv: z.number(), - condition: WeatherConditionSchema, - daily_chance_of_rain: z.number().optional(), - daily_chance_of_snow: z.number().optional(), -}); - -export const WeatherHourSchema = z.object({ - time_epoch: z.number(), - time: z.string(), - temp_c: z.number(), - temp_f: z.number(), - condition: WeatherConditionSchema, - wind_mph: z.number(), - wind_kph: z.number(), - wind_degree: z.number(), - wind_dir: z.string(), - pressure_mb: z.number(), - pressure_in: z.number(), - precip_mm: z.number(), - precip_in: z.number(), - humidity: z.number(), - cloud: z.number(), - feelslike_c: z.number(), - feelslike_f: z.number(), - vis_km: z.number(), - vis_miles: z.number(), - uv: z.number(), - gust_mph: z.number().optional(), - gust_kph: z.number().optional(), - chance_of_rain: z.number().optional(), - chance_of_snow: z.number().optional(), - // Additional fields from actual API response - is_day: z.number(), - windchill_c: z.number().optional(), - windchill_f: z.number().optional(), - heatindex_c: z.number().optional(), - heatindex_f: z.number().optional(), - dewpoint_c: z.number().optional(), - dewpoint_f: z.number().optional(), - will_it_rain: z.number().optional(), - will_it_snow: z.number().optional(), - snow_cm: z.number().optional(), - air_quality: AirQualitySchema.optional(), - short_rad: z.number().optional(), - diff_rad: z.number().optional(), - dni: z.number().optional(), - gti: z.number().optional(), -}); - -export const WeatherForecastDaySchema = z.object({ - date: z.string(), - date_epoch: z.number(), - day: WeatherDaySchema, - astro: z - .object({ - sunrise: z.string(), - sunset: z.string(), - moonrise: z.string(), - moonset: z.string(), - moon_phase: z.string(), - moon_illumination: z.number(), - }) - .optional(), - hour: z.array(WeatherHourSchema), -}); - -export const WeatherAlertSchema = z - .object({ - alert: z - .array( - z.object({ - headline: z.string(), - msgtype: z.string(), - severity: z.string(), - urgency: z.string(), - areas: z.string(), - category: z.string(), - certainty: z.string(), - event: z.string(), - note: z.string().optional(), - effective: z.string(), - expires: z.string(), - desc: z.string(), - instruction: z.string().optional(), - }), - ) - .optional(), - }) - .optional(); - -export const WeatherForecastSchema = z.object({ - location: WeatherAPILocationSchema, - current: WeatherCurrentSchema, - forecast: z.object({ - forecastday: z.array(WeatherForecastDaySchema), - }), -}); - -// Raw WeatherAPI.com response schemas for internal use -export const WeatherAPISearchResponseSchema = z.array( - z.object({ - id: z.number(), - name: z.string(), - region: z.string(), - country: z.string(), - lat: z.number(), - lon: z.number(), - url: z.string().optional(), - }), -); - -export const WeatherAPICurrentResponseSchema = z.object({ - location: WeatherAPILocationSchema, - current: WeatherCurrentSchema, -}); - -export const WeatherAPIForecastResponseSchema = z.object({ - location: WeatherAPILocationSchema, - current: WeatherCurrentSchema, - forecast: z.object({ - forecastday: z.array(WeatherForecastDaySchema), - }), - alerts: WeatherAlertSchema.optional(), -}); - -export const LocationSearchResponseSchema = z.array(LocationSchema); - -// Export types for use in the application -export type Location = z.infer; -export type WeatherAPILocation = z.infer; -export type WeatherCondition = z.infer; -export type AirQuality = z.infer; -export type WeatherCurrent = z.infer; -export type WeatherDay = z.infer; -export type WeatherHour = z.infer; -export type WeatherForecastDay = z.infer; -export type WeatherAlert = z.infer; -export type WeatherForecast = z.infer; -export type WeatherAPISearchResponse = z.infer; -export type WeatherAPICurrentResponse = z.infer; -export type WeatherAPIForecastResponse = z.infer; -export type LocationSearchResponse = z.infer; +export * from '@packrat/schemas/weather'; diff --git a/packages/api/src/types/constants.ts b/packages/api/src/types/constants.ts index 7ab332b4f5..8643efaba1 100644 --- a/packages/api/src/types/constants.ts +++ b/packages/api/src/types/constants.ts @@ -1,183 +1,2 @@ -import { z } from 'zod'; - -// --- Pack Category --- -export const PACK_CATEGORIES = Object.freeze([ - 'hiking', - 'backpacking', - 'camping', - 'climbing', - 'winter', - 'desert', - 'custom', - 'water sports', - 'skiing', -] as const); - -export const PackCategorySchema = z.enum(PACK_CATEGORIES); -export type PackCategory = z.infer; - -// --- Item Category --- -export const ITEM_CATEGORIES = Object.freeze([ - 'clothing', - 'shelter', - 'sleep', - 'kitchen', - 'water', - 'electronics', - 'first-aid', - 'navigation', - 'tools', - 'consumables', - 'miscellaneous', -] as const); - -export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); -export type ItemCategory = z.infer; - -// --- Weight Unit --- -export const WEIGHT_UNITS = Object.freeze(['g', 'oz', 'kg', 'lb'] as const); -export const WeightUnitSchema = z.enum(WEIGHT_UNITS); -export type WeightUnit = z.infer; - -// --- Availability --- -export const AVAILABILITY_VALUES = Object.freeze(['in_stock', 'out_of_stock', 'preorder'] as const); -export const AvailabilitySchema = z.enum(AVAILABILITY_VALUES); -export type Availability = z.infer; - -// --- Shared item types used by utility functions --- -export type ItemLink = { - id: string; - title: string; - url: string; - type: 'official' | 'review' | 'guide' | 'purchase' | 'other'; -}; - -export type ItemReview = { - id: string; - userId: string; - userName: string; - userAvatar?: string; - rating: number; - text: string; - date: string; - helpful?: number; - verified?: boolean; -}; - -// --- Lightweight pack/catalog types for utility functions --- -// These are simpler than the full API schemas and are used by -// calculation utilities (weight.ts, itemCalculations.ts). -export const CatalogItemSchema = z.object({ - id: z.number().int().positive(), - name: z.string(), - productUrl: z.string(), - sku: z.string(), - weight: z.number().nonnegative(), - weightUnit: z.string(), - description: z.string().optional(), - categories: z.array(z.string()).optional(), - images: z.array(z.string()).optional(), - brand: z.string().optional(), - model: z.string().optional(), - ratingValue: z.number().optional(), - color: z.string().optional(), - size: z.string().optional(), - price: z.number().optional(), - availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).optional(), - seller: z.string().optional(), - productSku: z.string().optional(), - material: z.string().optional(), - currency: z.string().optional(), - condition: z.string().optional(), - reviewCount: z.number().int().optional(), - variants: z - .array( - z.object({ - attribute: z.string(), - values: z.array(z.string()), - }), - ) - .optional(), - techs: z.record(z.string(), z.string()).optional(), - links: z - .array( - z.object({ - title: z.string(), - url: z.string(), - }), - ) - .optional(), - reviews: z - .array( - z.object({ - user_name: z.string(), - user_avatar: z.string().optional(), - context: z.record(z.string(), z.string()).optional(), - recommends: z.boolean().optional(), - rating: z.number(), - title: z.string(), - text: z.string(), - date: z.string(), - images: z.array(z.string()).optional(), - upvotes: z.number().optional(), - downvotes: z.number().optional(), - verified: z.boolean().optional(), - }), - ) - .optional(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), -}); - -export type CatalogItem = z.infer; - -export const PackItemSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().optional(), - weight: z.number().nonnegative(), - weightUnit: WeightUnitSchema, - quantity: z.number().int().positive(), - category: z.string(), - consumable: z.boolean(), - worn: z.boolean(), - image: z.string().url().optional(), - notes: z.string().optional(), - packId: z.string(), - catalogItemId: z.number().int().positive().optional(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - userId: z.string(), -}); - -export type PackItem = z.infer; - -export const PackSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string().optional(), - category: PackCategorySchema, - baseWeight: z.number().nonnegative().optional(), - totalWeight: z.number().nonnegative().optional(), - items: z.array(PackItemSchema).optional(), - userId: z.string(), - createdAt: z.string().datetime(), - updatedAt: z.string().datetime(), - isPublic: z.boolean(), - image: z.string().url().optional(), - tags: z.array(z.string()).optional(), -}); - -export type Pack = z.infer; - -export const UserSchema = z.object({ - id: z.string(), - name: z.string(), - email: z.string().email(), - avatar: z.string().url(), - experience: z.enum(['beginner', 'intermediate', 'expert']), - joinedAt: z.string().datetime(), - bio: z.string().optional(), -}); - -export type User = z.infer; +export type { CatalogItem, Pack, PackItem, PackWithItems, User } from '@packrat/db'; +export * from '@packrat/schemas/constants'; diff --git a/packages/api/src/types/validation.ts b/packages/api/src/types/validation.ts index 872d904e60..1955a47801 100644 --- a/packages/api/src/types/validation.ts +++ b/packages/api/src/types/validation.ts @@ -1,15 +1 @@ -import { z } from 'zod'; - -export interface ValidationError { - field: string; - reason: string; - value?: string | number | boolean | null | undefined; -} - -export const ValidationErrorSchema = z.object({ - field: z.string(), - reason: z.string(), - value: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(), -}); - -export const ValidationErrorsSchema = z.array(ValidationErrorSchema); +export * from '@packrat/schemas/validation'; diff --git a/packages/api/src/utils/__tests__/itemCalculations.test.ts b/packages/api/src/utils/__tests__/itemCalculations.test.ts index ebb76cb6e6..683b2a579b 100644 --- a/packages/api/src/utils/__tests__/itemCalculations.test.ts +++ b/packages/api/src/utils/__tests__/itemCalculations.test.ts @@ -21,17 +21,25 @@ function makePackItem(overrides: Partial = {}): PackItem { id: 'pack-item-1', packId: 'pack-1', name: 'Test Pack Item', + description: null, weight: 100, weightUnit: 'g', quantity: 1, + category: null, consumable: false, worn: false, + image: null, + notes: null, + catalogItemId: null, userId: 'user-1', - category: 'tools', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + deleted: false, + isAIGenerated: false, + templateItemId: null, + embedding: null, + createdAt: new Date(), + updatedAt: new Date(), ...overrides, - }; + } as PackItem; } function makeCatalogItem(overrides: Partial = {}): CatalogItem { @@ -48,24 +56,27 @@ function makeCatalogItem(overrides: Partial = {}): CatalogItem { brand: 'TestBrand', model: 'TestModel', ratingValue: 4.5, - color: undefined, - size: undefined, + color: null, + size: null, price: 99.99, availability: 'in_stock', - seller: undefined, - productSku: undefined, - material: undefined, + seller: null, + productSku: null, + material: null, currency: 'USD', - condition: undefined, + condition: null, reviewCount: 0, - variants: undefined, - techs: undefined, - links: undefined, - reviews: undefined, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), + variants: null, + techs: null, + links: null, + reviews: null, + qas: null, + faqs: null, + embedding: null, + createdAt: new Date(), + updatedAt: new Date(), ...overrides, - }; + } as CatalogItem; } // --------------------------------------------------------------------------- @@ -146,9 +157,9 @@ describe('itemCalculations', () => { expect(getNotes(item)).toBe('Important notes'); }); - it('returns undefined for pack items without notes', () => { + it('returns null/undefined for pack items without notes', () => { const item = makePackItem(); - expect(getNotes(item)).toBeUndefined(); + expect(getNotes(item)).toBeFalsy(); }); it('returns undefined for catalog items', () => { diff --git a/packages/api/src/utils/__tests__/weight.test.ts b/packages/api/src/utils/__tests__/weight.test.ts index caab2122ca..50c8ccb1cd 100644 --- a/packages/api/src/utils/__tests__/weight.test.ts +++ b/packages/api/src/utils/__tests__/weight.test.ts @@ -17,17 +17,24 @@ function makeItem( return { id: 'item-1', name: 'Test Item', - weight: overrides.weight, - weightUnit: overrides.weightUnit, + description: null, quantity: overrides.quantity ?? 1, + category: null, consumable: overrides.consumable ?? false, worn: overrides.worn ?? false, + image: null, + notes: null, packId: 'pack-1', + catalogItemId: null, userId: 'user-1', - category: 'tools', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }; + deleted: false, + isAIGenerated: false, + templateItemId: null, + embedding: null, + createdAt: new Date(), + updatedAt: new Date(), + ...overrides, + } as PackItem; } // --------------------------------------------------------------------------- diff --git a/packages/api/src/utils/itemCalculations.ts b/packages/api/src/utils/itemCalculations.ts index 7871d1b83a..61cb12b3be 100644 --- a/packages/api/src/utils/itemCalculations.ts +++ b/packages/api/src/utils/itemCalculations.ts @@ -32,7 +32,7 @@ export function getWeightUnit(item: CatalogItem | PackItem): WeightUnit { } /** Gets the notes of an item */ -export function getNotes(item: CatalogItem | PackItem): string | undefined { +export function getNotes(item: CatalogItem | PackItem): string | null | undefined { return isPackItem(item) ? item.notes : undefined; } @@ -68,5 +68,5 @@ export function isWorn(item: CatalogItem | PackItem): boolean { * Check if the item has a notes field */ export function hasNotes(item: CatalogItem | PackItem): boolean { - return 'notes' in item && item.notes !== undefined && item.notes !== ''; + return 'notes' in item && item.notes != null && item.notes !== ''; } diff --git a/packages/app/package.json b/packages/app/package.json index 5c840233ac..5de4cafbb7 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@packrat/api-client": "workspace:*", + "@packrat/schemas": "workspace:*", "@tanstack/react-query": "catalog:", "jotai": "catalog:", "react": "catalog:", diff --git a/packages/app/src/entities/catalog/schema.ts b/packages/app/src/entities/catalog/schema.ts index 2b5c573e89..ca51bc3d29 100644 --- a/packages/app/src/entities/catalog/schema.ts +++ b/packages/app/src/entities/catalog/schema.ts @@ -1 +1 @@ -export { CatalogItemSchema, CatalogItemsResponseSchema } from '@packrat/api/schemas/catalog'; +export { CatalogItemSchema, CatalogItemsResponseSchema } from '@packrat/schemas/catalog'; diff --git a/packages/app/src/entities/feed/schema.ts b/packages/app/src/entities/feed/schema.ts index b7258c5e30..5cd7093e7e 100644 --- a/packages/app/src/entities/feed/schema.ts +++ b/packages/app/src/entities/feed/schema.ts @@ -3,4 +3,4 @@ export { FeedResponseSchema, PostAuthorSchema, PostSchema, -} from '@packrat/api/schemas/feed'; +} from '@packrat/schemas/feed'; diff --git a/packages/app/src/entities/pack/schema.ts b/packages/app/src/entities/pack/schema.ts index 340b958276..1259969ce3 100644 --- a/packages/app/src/entities/pack/schema.ts +++ b/packages/app/src/entities/pack/schema.ts @@ -3,4 +3,4 @@ export { PackListResponseSchema, PackSchema, PackWithWeightsSchema, -} from '@packrat/api/schemas/packs'; +} from '@packrat/schemas/packs'; diff --git a/packages/app/src/entities/trip/schema.ts b/packages/app/src/entities/trip/schema.ts index 93ede99836..36afacba9d 100644 --- a/packages/app/src/entities/trip/schema.ts +++ b/packages/app/src/entities/trip/schema.ts @@ -1 +1 @@ -export { TripLocationSchema, TripSchema } from '@packrat/api/schemas/trips'; +export { TripLocationSchema, TripSchema } from '@packrat/schemas/trips'; diff --git a/packages/app/src/entities/user/schema.ts b/packages/app/src/entities/user/schema.ts index 74fb9b0a46..759e3c079c 100644 --- a/packages/app/src/entities/user/schema.ts +++ b/packages/app/src/entities/user/schema.ts @@ -1 +1 @@ -export { UserProfileSchema, UserSchema } from '@packrat/api/schemas/users'; +export { UserProfileSchema, UserSchema } from '@packrat/schemas/users'; diff --git a/packages/db/package.json b/packages/db/package.json new file mode 100644 index 0000000000..1aa8ca7cc8 --- /dev/null +++ b/packages/db/package.json @@ -0,0 +1,26 @@ +{ + "name": "@packrat/db", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./*": { + "types": "./src/*", + "default": "./src/*" + } + }, + "scripts": { + "check-types": "tsc --noEmit" + }, + "dependencies": { + "drizzle-orm": "catalog:", + "drizzle-zod": "catalog:" + }, + "devDependencies": { + "typescript": "catalog:" + } +} diff --git a/packages/db/src/constants.ts b/packages/db/src/constants.ts new file mode 100644 index 0000000000..42ab143c25 --- /dev/null +++ b/packages/db/src/constants.ts @@ -0,0 +1,55 @@ +export const PACK_CATEGORIES = Object.freeze([ + 'hiking', + 'backpacking', + 'camping', + 'climbing', + 'winter', + 'desert', + 'custom', + 'water sports', + 'skiing', +] as const); + +export type PackCategory = (typeof PACK_CATEGORIES)[number]; + +export const ITEM_CATEGORIES = Object.freeze([ + 'clothing', + 'shelter', + 'sleep', + 'kitchen', + 'water', + 'electronics', + 'first-aid', + 'navigation', + 'tools', + 'consumables', + 'miscellaneous', +] as const); + +export type ItemCategory = (typeof ITEM_CATEGORIES)[number]; + +export const WEIGHT_UNITS = Object.freeze(['g', 'oz', 'kg', 'lb'] as const); +export type WeightUnit = (typeof WEIGHT_UNITS)[number]; + +export const AVAILABILITY_VALUES = Object.freeze(['in_stock', 'out_of_stock', 'preorder'] as const); + +export type Availability = (typeof AVAILABILITY_VALUES)[number]; + +export interface ItemLink { + id: string; + title: string; + url: string; + type: 'official' | 'review' | 'guide' | 'purchase' | 'other'; +} + +export interface ItemReview { + id: string; + userId: string; + userName: string; + userAvatar?: string; + rating: number; + text: string; + date: string; + helpful?: number; + verified?: boolean; +} diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts new file mode 100644 index 0000000000..8ce55998ec --- /dev/null +++ b/packages/db/src/index.ts @@ -0,0 +1,4 @@ +export * from './constants'; +export * from './schema'; +export * from './validation'; +export * from './zod-schemas'; diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts new file mode 100644 index 0000000000..53760f064d --- /dev/null +++ b/packages/db/src/schema.ts @@ -0,0 +1,636 @@ +import { type InferInsertModel, type InferSelectModel, relations, sql } from 'drizzle-orm'; +import { + type AnyPgColumn, + bigint, + boolean, + index, + integer, + jsonb, + pgEnum, + pgTable, + real, + serial, + text, + timestamp, + unique, + vector, +} from 'drizzle-orm/pg-core'; +import type { PackCategory, WeightUnit } from './constants'; +import type { ValidationError } from './validation'; + +const availabilityEnum = pgEnum('availability', ['in_stock', 'out_of_stock', 'preorder']); + +// User table +export const users = pgTable('users', { + id: text('id').primaryKey(), + name: text('name').notNull(), + email: text('email').unique().notNull(), + emailVerified: boolean('email_verified').default(false).notNull(), + image: text('image'), + role: text('role').default('USER').notNull(), + banned: boolean('banned').default(false), + banReason: text('ban_reason'), + banExpires: timestamp('ban_expires'), + firstName: text('first_name'), + lastName: text('last_name'), + avatarUrl: text('avatar_url'), + passwordHash: text('password_hash'), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), +}); + +// Better Auth — session table +export const session = pgTable( + 'session', + { + id: text('id').primaryKey(), + expiresAt: timestamp('expires_at').notNull(), + token: text('token').notNull().unique(), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + ipAddress: text('ip_address'), + userAgent: text('user_agent'), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + impersonatedBy: text('impersonated_by'), + }, + (table) => [index('session_userId_idx').on(table.userId)], +); + +// Better Auth — account table (OAuth + credential provider) +export const account = pgTable( + 'account', + { + id: text('id').primaryKey(), + accountId: text('account_id').notNull(), + providerId: text('provider_id').notNull(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + accessToken: text('access_token'), + refreshToken: text('refresh_token'), + idToken: text('id_token'), + accessTokenExpiresAt: timestamp('access_token_expires_at'), + refreshTokenExpiresAt: timestamp('refresh_token_expires_at'), + scope: text('scope'), + password: text('password'), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + }, + (t) => [ + unique('account_provider_account_idx').on(t.providerId, t.accountId), + index('account_userId_idx').on(t.userId), + ], +); + +// Better Auth — verification table (email/OTP verification tokens) +export const verification = pgTable( + 'verification', + { + id: text('id').primaryKey(), + identifier: text('identifier').notNull(), + value: text('value').notNull(), + expiresAt: timestamp('expires_at').notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + }, + (table) => [index('verification_identifier_idx').on(table.identifier)], +); + +// Better Auth — jwks table (asymmetric JWT key pairs for jwt() plugin) +export const jwks = pgTable('jwks', { + id: text('id').primaryKey(), + publicKey: text('public_key').notNull(), + privateKey: text('private_key').notNull(), + createdAt: timestamp('created_at').notNull(), +}); + +// Packs table +export const packs = pgTable('packs', { + id: text('id').primaryKey(), + name: text('name').notNull(), + description: text('description'), + category: text('category').notNull().$type(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + templateId: text('template_id').references(() => packTemplates.id), + + isPublic: boolean('is_public').notNull().default(false), + image: text('image'), + tags: jsonb('tags').$type(), + deleted: boolean('deleted').notNull().default(false), + isAIGenerated: boolean('is_ai_generated').notNull().default(false), + localCreatedAt: timestamp('local_created_at').notNull(), + localUpdatedAt: timestamp('local_updated_at').notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), +}); + +// Catalog items table +export const catalogItems = pgTable( + 'catalog_items', + { + id: serial('id').primaryKey(), + name: text('name').notNull(), + productUrl: text('product_url').notNull(), + sku: text('sku').unique().notNull(), + weight: real('weight'), + weightUnit: text('weight_unit').$type(), + description: text('description'), + categories: jsonb('categories').$type(), + images: jsonb('images').$type(), + brand: text('brand'), + model: text('model'), + ratingValue: real('rating_value'), + color: text('color'), + size: text('size'), + price: real('price'), + availability: availabilityEnum('availability'), + seller: text('seller'), + productSku: text('product_sku'), + material: text('material'), + currency: text('currency'), + condition: text('condition'), + reviewCount: integer('review_count'), + + variants: + jsonb('variants').$type< + Array<{ + attribute: string; + values: string[]; + }> + >(), + + techs: jsonb('techs').$type>(), + + links: + jsonb('links').$type< + Array<{ + title: string; + url: string; + }> + >(), + + reviews: + jsonb('reviews').$type< + Array<{ + user_name: string; + user_avatar?: string | null; + context?: Record | null; + recommends?: boolean | null; + rating: number; + title: string; + text: string; + date: string; + images?: string[] | null; + upvotes?: number | null; + downvotes?: number | null; + verified?: boolean | null; + }> + >(), + + qas: jsonb('qas').$type< + Array<{ + question: string; + user?: string | null; + date: string; + answers: Array<{ + a: string; + date: string; + user?: string | null; + upvotes?: number | null; + }>; + }> + >(), + + faqs: jsonb('faqs').$type< + Array<{ + question: string; + answer: string; + }> + >(), + embedding: vector('embedding', { dimensions: 1536 }), + + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + }, + (table) => ({ + embeddingIndex: index('embedding_idx').using('hnsw', table.embedding.op('vector_cosine_ops')), + }), +); + +// Pack items table +export const packItems = pgTable( + 'pack_items', + { + id: text('id').primaryKey(), + name: text('name').notNull(), + description: text('description'), + weight: real('weight').notNull(), + weightUnit: text('weight_unit').notNull().$type(), + quantity: integer('quantity').default(1).notNull(), + category: text('category'), + consumable: boolean('consumable').notNull().default(false), + worn: boolean('worn').notNull().default(false), + image: text('image'), + notes: text('notes'), + packId: text('pack_id') + .references(() => packs.id, { onDelete: 'cascade' }) + .notNull(), + catalogItemId: integer('catalog_item_id').references(() => catalogItems.id), + userId: text('user_id') + .references(() => users.id) + .notNull(), + deleted: boolean('deleted').notNull().default(false), + isAIGenerated: boolean('is_ai_generated').notNull().default(false), + templateItemId: text('template_item_id').references(() => packTemplateItems.id), + embedding: vector('embedding', { dimensions: 1536 }), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + }, + (table) => ({ + embeddingIndex: index('pack_items_embedding_idx').using( + 'hnsw', + table.embedding.op('vector_cosine_ops'), + ), + }), +); + +export const packWeightHistory = pgTable('weight_history', { + id: text('id').primaryKey(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + packId: text('pack_id') + .references(() => packs.id, { onDelete: 'cascade' }) + .notNull(), + weight: real('weight').notNull(), + localCreatedAt: timestamp('local_created_at').notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), +}); + +export const packTemplates = pgTable('pack_templates', { + id: text('id').primaryKey(), + name: text('name').notNull(), + description: text('description'), + category: text('category').notNull(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + image: text('image'), + tags: jsonb('tags').$type(), + isAppTemplate: boolean('is_app_template').notNull().default(false), + deleted: boolean('deleted').notNull().default(false), + contentSource: text('content_source'), + contentId: text('content_id'), + + localCreatedAt: timestamp('local_created_at').notNull(), + localUpdatedAt: timestamp('local_updated_at').notNull(), + + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), +}); + +export const packTemplateItems = pgTable('pack_template_items', { + id: text('id').primaryKey(), + name: text('name').notNull(), + description: text('description'), + weight: real('weight').notNull(), + weightUnit: text('weight_unit').notNull().$type(), + quantity: integer('quantity').default(1).notNull(), + category: text('category'), + consumable: boolean('consumable').notNull().default(false), + worn: boolean('worn').notNull().default(false), + image: text('image'), + notes: text('notes'), + packTemplateId: text('pack_template_id') + .references(() => packTemplates.id, { onDelete: 'cascade' }) + .notNull(), + catalogItemId: integer('catalog_item_id').references(() => catalogItems.id), + userId: text('user_id') + .references(() => users.id) + .notNull(), + deleted: boolean('deleted').notNull().default(false), + + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), +}); + +// Trail condition reports table +export const trailConditionReports = pgTable( + 'trail_condition_reports', + { + id: text('id').primaryKey(), + trailName: text('trail_name').notNull(), + trailRegion: text('trail_region'), + surface: text('surface').notNull(), + overallCondition: text('overall_condition').notNull(), + hazards: jsonb('hazards').$type().notNull().default([]), + waterCrossings: integer('water_crossings').notNull().default(0), + waterCrossingDifficulty: text('water_crossing_difficulty'), + notes: text('notes'), + photos: jsonb('photos').$type().notNull().default([]), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + tripId: text('trip_id').references(() => trips.id, { onDelete: 'set null' }), + deleted: boolean('deleted').notNull().default(false), + localCreatedAt: timestamp('local_created_at').notNull(), + localUpdatedAt: timestamp('local_updated_at').notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), + }, + (table) => ({ + userIdIdx: index('trail_condition_reports_user_id_idx').on(table.userId), + activeCreatedIdx: index('trail_condition_reports_active_created_idx').on( + table.deleted, + table.createdAt.desc(), + ), + trailNameIdx: index('trail_condition_reports_trail_name_idx').on(table.trailName), + tripIdIdx: index('trail_condition_reports_trip_id_idx') + .on(table.tripId) + .where(sql`${table.tripId} IS NOT NULL`), + }), +); + +export const trips = pgTable('trips', { + id: text('id').primaryKey(), + name: text('name').notNull(), + description: text('description'), + startDate: timestamp('start_date'), + endDate: timestamp('end_date'), + location: jsonb('location').$type<{ latitude: number; longitude: number; name?: string }>(), + notes: text('notes'), + userId: text('user_id') + .references(() => users.id) + .notNull(), + packId: text('pack_id').references(() => packs.id, { onDelete: 'set null' }), + trailOsmId: bigint('trail_osm_id', { mode: 'bigint' }), + localCreatedAt: timestamp('local_created_at').notNull(), + localUpdatedAt: timestamp('local_updated_at').notNull(), + deleted: boolean('deleted').notNull().default(false), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), +}); + +// Relations + +export const packsRelations = relations(packs, ({ one, many }) => ({ + user: one(users, { fields: [packs.userId], references: [users.id] }), + items: many(packItems), +})); + +export const packItemsRelations = relations(packItems, ({ one }) => ({ + pack: one(packs, { fields: [packItems.packId], references: [packs.id] }), + user: one(users, { fields: [packItems.userId], references: [users.id] }), + catalogItem: one(catalogItems, { + fields: [packItems.catalogItemId], + references: [catalogItems.id], + }), +})); + +export const catalogItemsRelations = relations(catalogItems, ({ many }) => ({ + packItems: many(packItems), + etlJobs: many(catalogItemEtlJobs), +})); + +export const packWeightHistoryRelations = relations(packWeightHistory, ({ one }) => ({ + pack: one(packs, { fields: [packWeightHistory.packId], references: [packs.id] }), +})); + +export const tripsRelations = relations(trips, ({ one }) => ({ + user: one(users, { fields: [trips.userId], references: [users.id] }), + pack: one(packs, { fields: [trips.packId], references: [packs.id] }), +})); + +export const reportedContent = pgTable('reported_content', { + id: serial('id').primaryKey(), + userId: text('user_id') + .references(() => users.id) + .notNull(), + userQuery: text('user_query').notNull(), + aiResponse: text('ai_response').notNull(), + reason: text('reason').notNull(), + userComment: text('user_comment'), + status: text('status').default('pending').notNull(), + reviewed: boolean('reviewed').default(false), + reviewedBy: text('reviewed_by').references(() => users.id), + reviewedAt: timestamp('reviewed_at'), + createdAt: timestamp('created_at').defaultNow().notNull(), +}); + +export const reportedContentRelations = relations(reportedContent, ({ one }) => ({ + user: one(users, { fields: [reportedContent.userId], references: [users.id] }), + reviewer: one(users, { fields: [reportedContent.reviewedBy], references: [users.id] }), +})); + +export const packTemplatesRelations = relations(packTemplates, ({ one, many }) => ({ + user: one(users, { fields: [packTemplates.userId], references: [users.id] }), + items: many(packTemplateItems), +})); + +export const packTemplateItemsRelations = relations(packTemplateItems, ({ one }) => ({ + template: one(packTemplates, { + fields: [packTemplateItems.packTemplateId], + references: [packTemplates.id], + }), + user: one(users, { fields: [packTemplateItems.userId], references: [users.id] }), + catalogItem: one(catalogItems, { + fields: [packTemplateItems.catalogItemId], + references: [catalogItems.id], + }), +})); + +export const invalidItemLogs = pgTable('invalid_item_logs', { + id: serial('id').primaryKey(), + jobId: text('job_id') + .references(() => etlJobs.id) + .notNull(), + errors: jsonb('errors').notNull().$type(), + rawData: jsonb('raw_data').notNull(), + rowIndex: integer('row_index').notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), +}); + +export type InvalidItemLog = typeof invalidItemLogs.$inferSelect; +export type NewInvalidItemLog = typeof invalidItemLogs.$inferInsert; + +const etlJobStatusEnum = pgEnum('etl_job_status', ['running', 'completed', 'failed']); + +export const etlJobs = pgTable( + 'etl_jobs', + { + id: text('id').primaryKey(), + status: etlJobStatusEnum('status').notNull(), + source: text('source').notNull(), + filename: text('filename').notNull(), + startedAt: timestamp('started_at').notNull(), + completedAt: timestamp('completed_at'), + totalProcessed: integer('total_processed'), + totalValid: integer('total_valid'), + totalInvalid: integer('total_invalid'), + scraperRevision: text('scraper_revision').notNull(), + }, + (table) => ({ + scraperRevisionIdx: index('etl_jobs_scraper_revision_idx').on(table.scraperRevision), + }), +); + +export type ETLJob = typeof etlJobs.$inferSelect; +export type NewETLJob = typeof etlJobs.$inferInsert; + +export const etlJobsRelations = relations(etlJobs, ({ many }) => ({ + logs: many(invalidItemLogs), + catalogItems: many(catalogItemEtlJobs), +})); + +export const invalidItemLogsRelations = relations(invalidItemLogs, ({ one }) => ({ + job: one(etlJobs, { fields: [invalidItemLogs.jobId], references: [etlJobs.id] }), +})); + +export const catalogItemEtlJobs = pgTable('catalog_item_etl_jobs', { + id: serial('id').primaryKey(), + catalogItemId: integer('catalog_item_id') + .references(() => catalogItems.id, { onDelete: 'cascade' }) + .notNull(), + etlJobId: text('etl_job_id') + .references(() => etlJobs.id, { onDelete: 'cascade' }) + .notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), +}); + +export const catalogItemEtlJobsRelations = relations(catalogItemEtlJobs, ({ one }) => ({ + catalogItem: one(catalogItems, { + fields: [catalogItemEtlJobs.catalogItemId], + references: [catalogItems.id], + }), + etlJob: one(etlJobs, { fields: [catalogItemEtlJobs.etlJobId], references: [etlJobs.id] }), +})); + +// Infer model types +export type User = InferSelectModel; +export type NewUser = InferInsertModel; +export type Session = InferSelectModel; +export type NewSession = InferInsertModel; +export type Account = InferSelectModel; +export type NewAccount = InferInsertModel; +export type Verification = InferSelectModel; +export type NewVerification = InferInsertModel; +export type Jwks = InferSelectModel; +export type NewJwks = InferInsertModel; +export type Pack = InferSelectModel; +export type PackWithItems = Pack & { items: PackItem[] }; +export type NewPack = InferInsertModel; +export type CatalogItem = InferSelectModel; +export type NewCatalogItem = InferInsertModel; +export type PackItem = InferSelectModel; +export type NewPackItem = InferInsertModel; +export type ReportedContent = InferSelectModel; +export type NewReportedContent = InferInsertModel; +export type PackTemplate = InferSelectModel; +export type NewPackTemplate = InferInsertModel; +export type PackTemplateItem = InferSelectModel; +export type NewPackTemplateItem = InferInsertModel; +export type TrailConditionReport = InferSelectModel; +export type NewTrailConditionReport = InferInsertModel; +export type Trip = InferSelectModel; +export type NewTrip = InferInsertModel; +export type PackTemplateWithItems = PackTemplate & { items: PackTemplateItem[] }; + +// Social Feed tables +export const posts = pgTable('posts', { + id: serial('id').primaryKey(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + caption: text('caption'), + images: jsonb('images').$type().notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), +}); + +export const postLikes = pgTable( + 'post_likes', + { + id: serial('id').primaryKey(), + postId: integer('post_id') + .references(() => posts.id, { onDelete: 'cascade' }) + .notNull(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), + }, + (table) => ({ + uniquePostUser: unique('post_likes_post_id_user_id_unique').on(table.postId, table.userId), + }), +); + +export const postComments = pgTable('post_comments', { + id: serial('id').primaryKey(), + postId: integer('post_id') + .references(() => posts.id, { onDelete: 'cascade' }) + .notNull(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + content: text('content').notNull(), + parentCommentId: integer('parent_comment_id').references((): AnyPgColumn => postComments.id, { + onDelete: 'cascade', + }), + createdAt: timestamp('created_at').defaultNow().notNull(), + updatedAt: timestamp('updated_at').defaultNow().notNull(), +}); + +export const commentLikes = pgTable( + 'comment_likes', + { + id: serial('id').primaryKey(), + commentId: integer('comment_id') + .references(() => postComments.id, { onDelete: 'cascade' }) + .notNull(), + userId: text('user_id') + .references(() => users.id, { onDelete: 'cascade' }) + .notNull(), + createdAt: timestamp('created_at').defaultNow().notNull(), + }, + (table) => ({ + uniqueCommentUser: unique('comment_likes_comment_id_user_id_unique').on( + table.commentId, + table.userId, + ), + }), +); + +export const postsRelations = relations(posts, ({ one, many }) => ({ + user: one(users, { fields: [posts.userId], references: [users.id] }), + likes: many(postLikes), + comments: many(postComments), +})); + +export const postLikesRelations = relations(postLikes, ({ one }) => ({ + post: one(posts, { fields: [postLikes.postId], references: [posts.id] }), + user: one(users, { fields: [postLikes.userId], references: [users.id] }), +})); + +export const postCommentsRelations = relations(postComments, ({ one, many }) => ({ + post: one(posts, { fields: [postComments.postId], references: [posts.id] }), + user: one(users, { fields: [postComments.userId], references: [users.id] }), + likes: many(commentLikes), +})); + +export const commentLikesRelations = relations(commentLikes, ({ one }) => ({ + comment: one(postComments, { fields: [commentLikes.commentId], references: [postComments.id] }), + user: one(users, { fields: [commentLikes.userId], references: [users.id] }), +})); + +export type Post = InferSelectModel; +export type NewPost = InferInsertModel; +export type PostLike = InferSelectModel; +export type NewPostLike = InferInsertModel; +export type PostComment = InferSelectModel; +export type NewPostComment = InferInsertModel; +export type CommentLike = InferSelectModel; +export type NewCommentLike = InferInsertModel; diff --git a/packages/db/src/validation.ts b/packages/db/src/validation.ts new file mode 100644 index 0000000000..e2d273875b --- /dev/null +++ b/packages/db/src/validation.ts @@ -0,0 +1,5 @@ +export interface ValidationError { + field: string; + reason: string; + value?: string | number | boolean | null | undefined; +} diff --git a/packages/db/src/zod-schemas.ts b/packages/db/src/zod-schemas.ts new file mode 100644 index 0000000000..edc351c112 --- /dev/null +++ b/packages/db/src/zod-schemas.ts @@ -0,0 +1,41 @@ +import { createInsertSchema, createSelectSchema } from 'drizzle-zod'; +import { + catalogItemEtlJobs, + catalogItems, + etlJobs, + invalidItemLogs, + packItems, + packs, + packTemplateItems, + packTemplates, + packWeightHistory, + reportedContent, + users, +} from './schema'; + +export const selectUserSchema = createSelectSchema(users); +export const insertUserSchema = createInsertSchema(users); + +export const selectPackSchema = createSelectSchema(packs); +export const insertPackSchema = createInsertSchema(packs); +export const selectPackItemSchema = createSelectSchema(packItems); +export const insertPackItemSchema = createInsertSchema(packItems); +export const selectPackWeightHistorySchema = createSelectSchema(packWeightHistory); +export const insertPackWeightHistorySchema = createInsertSchema(packWeightHistory); + +export const selectCatalogItemSchema = createSelectSchema(catalogItems); +export const insertCatalogItemSchema = createInsertSchema(catalogItems); + +export const selectPackTemplateSchema = createSelectSchema(packTemplates); +export const insertPackTemplateSchema = createInsertSchema(packTemplates); +export const selectPackTemplateItemSchema = createSelectSchema(packTemplateItems); +export const insertPackTemplateItemSchema = createInsertSchema(packTemplateItems); + +export const selectReportedContentSchema = createSelectSchema(reportedContent); +export const insertReportedContentSchema = createInsertSchema(reportedContent); +export const selectInvalidItemLogSchema = createSelectSchema(invalidItemLogs); +export const insertInvalidItemLogSchema = createInsertSchema(invalidItemLogs); +export const selectEtlJobSchema = createSelectSchema(etlJobs); +export const insertEtlJobSchema = createInsertSchema(etlJobs); +export const selectCatalogItemEtlJobSchema = createSelectSchema(catalogItemEtlJobs); +export const insertCatalogItemEtlJobSchema = createInsertSchema(catalogItemEtlJobs); diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json new file mode 100644 index 0000000000..753a3038e6 --- /dev/null +++ b/packages/db/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "skipLibCheck": true, + "noUncheckedIndexedAccess": true + }, + "include": ["src"] +} diff --git a/packages/schemas/package.json b/packages/schemas/package.json new file mode 100644 index 0000000000..fe5c2caca7 --- /dev/null +++ b/packages/schemas/package.json @@ -0,0 +1,27 @@ +{ + "name": "@packrat/schemas", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./*": { + "types": "./src/*", + "default": "./src/*" + } + }, + "scripts": { + "check-types": "tsc --noEmit" + }, + "dependencies": { + "@packrat/db": "workspace:*", + "@packrat/guards": "workspace:*", + "zod": "catalog:" + }, + "devDependencies": { + "typescript": "catalog:" + } +} diff --git a/packages/schemas/src/ai.ts b/packages/schemas/src/ai.ts new file mode 100644 index 0000000000..fbe0beae1d --- /dev/null +++ b/packages/schemas/src/ai.ts @@ -0,0 +1,30 @@ +import { z } from 'zod'; + +export const RagSearchQuerySchema = z.object({ + q: z.string().min(1), + limit: z.coerce.number().int().min(1).max(100).optional().default(5), +}); + +export const WebSearchQuerySchema = z.object({ + q: z.string().min(1), +}); + +export const RagSearchResponseSchema = z.object({ + object: z.string(), + search_query: z.string(), + has_more: z.boolean(), + next_page: z.string().nullable(), + data: z.array( + z.object({ + filename: z.string(), + url: z.string(), + score: z.number().optional(), + content: z.array(z.unknown()).optional(), + }), + ), +}); + +export const WebSearchResponseSchema = z.object({ + answer: z.string(), + sources: z.array(z.unknown()), +}); diff --git a/packages/schemas/src/auth.ts b/packages/schemas/src/auth.ts new file mode 100644 index 0000000000..987efef2f9 --- /dev/null +++ b/packages/schemas/src/auth.ts @@ -0,0 +1,133 @@ +import { z } from 'zod'; + +export const LoginRequestSchema = z.object({ + email: z.string().email(), + password: z.string().min(8), +}); + +export const LoginResponseSchema = z.object({ + success: z.boolean(), + accessToken: z.string(), + refreshToken: z.string(), + user: z.object({ + id: z.string(), + email: z.string().email(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), + emailVerified: z.boolean().nullable(), + }), +}); + +export const RegisterRequestSchema = z.object({ + email: z.string().email(), + password: z.string().min(8), + firstName: z.string().optional(), + lastName: z.string().optional(), +}); + +export const RegisterResponseSchema = z.object({ + success: z.boolean(), + message: z.string(), + userId: z.string(), +}); + +export const VerifyEmailRequestSchema = z.object({ + email: z.string().email(), + code: z.string().length(5), +}); + +export const VerifyEmailResponseSchema = z.object({ + success: z.boolean(), + message: z.string(), + accessToken: z.string(), + refreshToken: z.string(), + user: z.object({ + id: z.string(), + email: z.string().email(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), + emailVerified: z.boolean().nullable(), + }), +}); + +export const RefreshTokenRequestSchema = z.object({ + refreshToken: z.string(), +}); + +export const RefreshTokenResponseSchema = z.object({ + success: z.boolean(), + accessToken: z.string(), + refreshToken: z.string(), + user: z.object({ + id: z.string(), + email: z.string().email(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), + emailVerified: z.boolean().nullable(), + role: z.string().nullable(), + }), +}); + +export const ForgotPasswordRequestSchema = z.object({ + email: z.string().email(), +}); + +export const ForgotPasswordResponseSchema = z.object({ + success: z.boolean(), + message: z.string(), +}); + +export const ResetPasswordRequestSchema = z.object({ + email: z.string().email(), + code: z.string().length(5), + newPassword: z.string().min(8), +}); + +export const ResetPasswordResponseSchema = z.object({ + success: z.boolean(), + message: z.string(), +}); + +export const GoogleAuthRequestSchema = z.object({ + idToken: z.string(), +}); + +export const AppleAuthRequestSchema = z.object({ + identityToken: z.string(), + authorizationCode: z.string(), +}); + +export const SocialAuthResponseSchema = z.object({ + success: z.boolean(), + accessToken: z.string(), + refreshToken: z.string(), + user: z.object({ + id: z.string(), + email: z.string().email(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), + emailVerified: z.boolean().nullable(), + role: z.string().nullable(), + }), + isNewUser: z.boolean().optional(), +}); + +export const LogoutRequestSchema = z.object({ + refreshToken: z.string(), +}); + +export const LogoutResponseSchema = z.object({ + success: z.boolean(), + message: z.string(), +}); + +export const MeResponseSchema = z.object({ + success: z.boolean(), + user: z.object({ + id: z.string(), + email: z.string().email(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), + emailVerified: z.boolean().nullable(), + }), +}); diff --git a/packages/schemas/src/catalog.ts b/packages/schemas/src/catalog.ts new file mode 100644 index 0000000000..f692781f2a --- /dev/null +++ b/packages/schemas/src/catalog.ts @@ -0,0 +1,333 @@ +import { isString } from '@packrat/guards'; +import { z } from 'zod'; +import { WEIGHT_UNITS } from './constants'; + +// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. +const datetimeString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().datetime(), +); + +export const CatalogItemSchema = z.object({ + id: z.number().int().positive(), + name: z.string(), + productUrl: z.string(), + sku: z.string(), + weight: z.number(), + weightUnit: z.enum(WEIGHT_UNITS), + description: z.string().nullable(), + categories: z.array(z.string()).nullable(), + images: z.array(z.string()).nullable(), + brand: z.string().nullable(), + model: z.string().nullable(), + ratingValue: z.number().nullable(), + color: z.string().nullable(), + size: z.string().nullable(), + price: z.number().nullable(), + availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).nullable(), + seller: z.string().nullable(), + productSku: z.string().nullable(), + material: z.string().nullable(), + currency: z.string().nullable(), + condition: z.string().nullable(), + reviewCount: z.number().int().nullable(), + variants: z + .array( + z.object({ + attribute: z.string(), + values: z.array(z.string()), + }), + ) + .nullable() + .optional(), + techs: z.record(z.string(), z.string()).nullable().optional(), + links: z + .array( + z.object({ + title: z.string(), + url: z.string(), + }), + ) + .nullable() + .optional(), + reviews: z + .array( + z.object({ + user_name: z.string().nullable().optional(), + user_avatar: z.string().nullable().optional(), + context: z.record(z.string(), z.string()).nullable().optional(), + recommends: z.boolean().nullable().optional(), + rating: z.number(), + title: z.string().nullable().optional(), + text: z.string().nullable().optional(), + date: z.string().nullable().optional(), + images: z.array(z.string()).nullable().optional(), + upvotes: z.number().nullable().optional(), + downvotes: z.number().nullable().optional(), + verified: z.boolean().nullable().optional(), + }), + ) + .nullable() + .optional(), + qas: z + .array( + z.object({ + question: z.string(), + user: z.string().nullable().optional(), + date: z.string(), + answers: z.array( + z.object({ + a: z.string(), + date: z.string(), + user: z.string().nullable().optional(), + upvotes: z.number().nullable().optional(), + }), + ), + }), + ) + .nullable() + .optional(), + faqs: z + .array( + z.object({ + question: z.string(), + answer: z.string(), + }), + ) + .nullable() + .optional(), + usageCount: z.number().int().min(0).optional(), + createdAt: datetimeString, + updatedAt: datetimeString, +}); + +const SortSchema = z.object({ + field: z.enum([ + 'name', + 'brand', + 'category', + 'price', + 'ratingValue', + 'createdAt', + 'updatedAt', + 'usage', + ]), + order: z.enum(['asc', 'desc']), +}); + +export const CatalogItemsQuerySchema = z.object({ + page: z.coerce.number().int().positive().optional().default(1), + limit: z.coerce.number().int().min(1).max(100).optional().default(20), + q: z.string().optional(), + category: z.string().optional(), + // Eden Treaty serializes nested objects as JSON strings in query params. + // z.preprocess parses the JSON string before Zod validates the shape. + sort: z + .preprocess((val) => { + if (isString(val)) { + try { + return JSON.parse(val); + } catch { + return undefined; + } + } + return val; + }, SortSchema.optional()) + .optional(), +}); + +export const CatalogItemsResponseSchema = z.object({ + items: z.array(CatalogItemSchema), + totalCount: z.number(), + page: z.number(), + limit: z.number(), + totalPages: z.number(), +}); + +export const CreateCatalogItemRequestSchema = z.object({ + name: z.string().min(1).max(255), + productUrl: z.string().url(), + sku: z.string(), + weight: z.number().positive(), + weightUnit: z.enum(WEIGHT_UNITS), + description: z.string().optional(), + categories: z.array(z.string()).optional(), + images: z.array(z.string()).optional(), + brand: z.string().optional(), + model: z.string().optional(), + ratingValue: z.number().min(0).max(5).optional(), + color: z.string().optional(), + size: z.string().optional(), + price: z.number().optional(), + availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).optional(), + seller: z.string().optional(), + productSku: z.string().optional(), + material: z.string().optional(), + currency: z.string().optional(), + condition: z.string().optional(), + reviewCount: z.number().min(0).optional(), + variants: z + .array( + z.object({ + attribute: z.string(), + values: z.array(z.string()), + }), + ) + .optional(), + techs: z.record(z.string(), z.string()).optional(), + links: z + .array( + z.object({ + title: z.string(), + url: z.string(), + }), + ) + .optional(), + reviews: z + .array( + z.object({ + user_name: z.string(), + user_avatar: z.string().optional(), + context: z.record(z.string(), z.string()).optional(), + recommends: z.boolean().optional(), + rating: z.number(), + title: z.string(), + text: z.string(), + date: z.string(), + images: z.array(z.string()).optional(), + upvotes: z.number().optional(), + downvotes: z.number().optional(), + verified: z.boolean().optional(), + }), + ) + .optional(), + qas: z + .array( + z.object({ + question: z.string(), + user: z.string().optional(), + date: z.string(), + answers: z.array( + z.object({ + a: z.string(), + date: z.string(), + user: z.string().optional(), + upvotes: z.number().optional(), + }), + ), + }), + ) + .optional(), + faqs: z + .array( + z.object({ + question: z.string(), + answer: z.string(), + }), + ) + .optional(), +}); + +export const UpdateCatalogItemRequestSchema = z.object({ + name: z.string().min(1).max(255).optional(), + productUrl: z.string().url().optional(), + sku: z.string().optional(), + weight: z.number().optional(), + weightUnit: z.enum(WEIGHT_UNITS).optional(), + description: z.string().optional(), + categories: z.array(z.string()).optional(), + images: z.array(z.string()).optional(), + brand: z.string().optional(), + model: z.string().optional(), + ratingValue: z.number().min(0).max(5).optional(), + color: z.string().optional(), + size: z.string().optional(), + price: z.number().optional(), + availability: z.enum(['in_stock', 'out_of_stock', 'preorder']).optional(), + seller: z.string().optional(), + productSku: z.string().optional(), + material: z.string().optional(), + currency: z.string().optional(), + condition: z.string().optional(), + reviewCount: z.number().min(0).optional(), + variants: z + .array( + z.object({ + attribute: z.string(), + values: z.array(z.string()), + }), + ) + .optional(), + techs: z.record(z.string(), z.string()).optional(), + links: z + .array( + z.object({ + title: z.string(), + url: z.string(), + }), + ) + .optional(), + reviews: z + .array( + z.object({ + user_name: z.string(), + user_avatar: z.string().optional(), + context: z.record(z.string(), z.string()).optional(), + recommends: z.boolean().optional(), + rating: z.number(), + title: z.string(), + text: z.string(), + date: z.string(), + images: z.array(z.string()).optional(), + upvotes: z.number().optional(), + downvotes: z.number().optional(), + verified: z.boolean().optional(), + }), + ) + .optional(), + qas: z + .array( + z.object({ + question: z.string(), + user: z.string().optional(), + date: z.string(), + answers: z.array( + z.object({ + a: z.string(), + date: z.string(), + user: z.string().optional(), + upvotes: z.number().optional(), + }), + ), + }), + ) + .optional(), + faqs: z + .array( + z.object({ + question: z.string(), + answer: z.string(), + }), + ) + .optional(), +}); + +export const CatalogCategoriesResponseSchema = z.array(z.string()); + +export const VectorSearchQuerySchema = z.object({ + q: z.string().min(1), + limit: z.coerce.number().int().min(1).max(50).optional().default(10), + offset: z.coerce.number().int().min(0).optional().default(0), +}); + +export const SimilarItemSchema = CatalogItemSchema.extend({ + similarity: z.number().min(0).max(1), +}); + +export const VectorSearchResponseSchema = z.object({ + items: z.array(SimilarItemSchema), + total: z.number(), + limit: z.number(), + offset: z.number(), + nextOffset: z.number(), +}); diff --git a/packages/schemas/src/chat.ts b/packages/schemas/src/chat.ts new file mode 100644 index 0000000000..194ffb1134 --- /dev/null +++ b/packages/schemas/src/chat.ts @@ -0,0 +1,54 @@ +import { z } from 'zod'; + +export const ChatMessageSchema = z.object({ + role: z.enum(['user', 'assistant', 'system']), + content: z.string(), +}); + +export const ChatRequestSchema = z.any(); +// .oRbject({ +// messages: z.array(ChatMessageSchema), +// contextType: z.string().optional(), +// itemId: z.string().optional(), +// packId: z.string().optional(), +// location: z.string().optional(), +// }) +// ; + +export const ReportedContentUserSchema = z.object({ + id: z.string(), + email: z.string().email(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), +}); + +export const ReportedContentSchema = z.object({ + id: z.number(), + userId: z.string(), + userQuery: z.string(), + aiResponse: z.string(), + reason: z.string(), + userComment: z.string().nullable(), + status: z.string(), + reviewed: z.boolean().nullable(), + reviewedBy: z.string().nullable(), + reviewedAt: z.string().datetime().nullable(), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), + user: ReportedContentUserSchema, +}); + +export const CreateReportRequestSchema = z.object({ + userQuery: z.string(), + aiResponse: z.string(), + reason: z.string(), + userComment: z.string().optional(), +}); + +export const ReportsResponseSchema = z.object({ + reportedItems: z.array(ReportedContentSchema), +}); + +export const UpdateReportStatusRequestSchema = z.object({ + status: z.string(), +}); diff --git a/packages/schemas/src/constants.ts b/packages/schemas/src/constants.ts new file mode 100644 index 0000000000..5c2be2a3a1 --- /dev/null +++ b/packages/schemas/src/constants.ts @@ -0,0 +1,26 @@ +import { z } from 'zod'; + +export { + AVAILABILITY_VALUES, + type Availability, + ITEM_CATEGORIES, + type ItemCategory, + type ItemLink, + type ItemReview, + PACK_CATEGORIES, + type PackCategory, + WEIGHT_UNITS, + type WeightUnit, +} from '@packrat/db/constants'; + +import { + AVAILABILITY_VALUES, + ITEM_CATEGORIES, + PACK_CATEGORIES, + WEIGHT_UNITS, +} from '@packrat/db/constants'; + +export const PackCategorySchema = z.enum(PACK_CATEGORIES); +export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); +export const WeightUnitSchema = z.enum(WEIGHT_UNITS); +export const AvailabilitySchema = z.enum(AVAILABILITY_VALUES); diff --git a/packages/schemas/src/feed.ts b/packages/schemas/src/feed.ts new file mode 100644 index 0000000000..4b4b5f31a7 --- /dev/null +++ b/packages/schemas/src/feed.ts @@ -0,0 +1,64 @@ +import { z } from 'zod'; + +export const PostAuthorSchema = z.object({ + id: z.string(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), +}); + +export const PostSchema = z.object({ + id: z.number().int(), + userId: z.string(), + caption: z.string().nullable(), + images: z.array(z.string()), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), + author: PostAuthorSchema.optional(), + likeCount: z.number().int(), + commentCount: z.number().int(), + likedByMe: z.boolean(), +}); + +export const CreatePostRequestSchema = z.object({ + caption: z.string().max(2000).optional(), + images: z.array(z.string()).min(1).max(10), +}); + +export const FeedResponseSchema = z.object({ + items: z.array(PostSchema), + page: z.number().int(), + limit: z.number().int(), + total: z.number().int(), + totalPages: z.number().int(), +}); + +export const CommentSchema = z.object({ + id: z.number().int(), + postId: z.number().int(), + userId: z.string(), + content: z.string(), + parentCommentId: z.number().int().nullable(), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), + author: PostAuthorSchema.optional(), + likeCount: z.number().int(), + likedByMe: z.boolean(), +}); + +export const CreateCommentRequestSchema = z.object({ + content: z.string().min(1).max(1000), + parentCommentId: z.number().int().optional(), +}); + +export const CommentsResponseSchema = z.object({ + items: z.array(CommentSchema), + page: z.number().int(), + limit: z.number().int(), + total: z.number().int(), + totalPages: z.number().int(), +}); + +export const LikeToggleResponseSchema = z.object({ + liked: z.boolean(), + likeCount: z.number().int(), +}); diff --git a/packages/schemas/src/guides.ts b/packages/schemas/src/guides.ts new file mode 100644 index 0000000000..186230e63f --- /dev/null +++ b/packages/schemas/src/guides.ts @@ -0,0 +1,61 @@ +import { z } from 'zod'; + +export const GuideSchema = z.object({ + id: z.string(), + key: z.string(), + title: z.string(), + category: z.string(), + categories: z.array(z.string()).optional(), + description: z.string(), + author: z.string().optional(), + readingTime: z.number().optional(), + difficulty: z.string().optional(), + content: z.string().optional(), + createdAt: z.string().datetime(), + updatedAt: z.string().datetime(), +}); + +export const GuideDetailSchema = GuideSchema.extend({ + content: z.string(), +}); + +export const GuidesQuerySchema = z.object({ + page: z.coerce.number().int().positive().optional().default(1), + limit: z.coerce.number().int().positive().optional().default(20), + category: z.string().optional(), + sort: z + .object({ + field: z.enum(['title', 'category', 'createdAt', 'updatedAt']), + order: z.enum(['asc', 'desc']), + }) + .optional(), +}); + +export const GuidesResponseSchema = z.object({ + items: z.array(GuideSchema), + totalCount: z.number(), + page: z.number(), + limit: z.number(), + totalPages: z.number(), +}); + +export const GuideSearchQuerySchema = z.object({ + q: z.string().min(1), + page: z.coerce.number().int().positive().optional().default(1), + limit: z.coerce.number().int().positive().optional().default(20), + category: z.string().optional(), +}); + +export const GuideSearchResponseSchema = z.object({ + items: z.array(GuideSchema), + totalCount: z.number(), + page: z.number(), + limit: z.number(), + totalPages: z.number(), + query: z.string(), +}); + +export const GuideCategoriesResponseSchema = z.object({ + categories: z.array(z.string()), + count: z.number().int(), +}); diff --git a/packages/schemas/src/imageDetection.ts b/packages/schemas/src/imageDetection.ts new file mode 100644 index 0000000000..4d4b399256 --- /dev/null +++ b/packages/schemas/src/imageDetection.ts @@ -0,0 +1,25 @@ +import { z } from 'zod'; +import { CatalogItemSchema } from './catalog'; + +export const DetectedItemSchema = z.object({ + name: z.string(), + description: z.string(), + quantity: z.number().int().positive(), + category: z.string(), + consumable: z.boolean().default(false).describe('Whether the item is consumable'), + worn: z.boolean().default(false).describe('Whether the item is worn'), + notes: z.string().nullable().optional(), + confidence: z.number().min(0).max(1), +}); + +export const DetectedItemWithMatchesSchema = z.object({ + detected: DetectedItemSchema, + catalogMatches: z.array(CatalogItemSchema), +}); + +export const AnalyzeImageRequestSchema = z.object({ + image: z.string(), + matchLimit: z.number().int().min(1).max(10).optional().default(3), +}); + +export const AnalyzeImageResponseSchema = z.array(DetectedItemWithMatchesSchema); diff --git a/packages/schemas/src/index.ts b/packages/schemas/src/index.ts new file mode 100644 index 0000000000..577b959de1 --- /dev/null +++ b/packages/schemas/src/index.ts @@ -0,0 +1,18 @@ +export * from './ai'; +export * from './auth'; +export * from './catalog'; +export * from './chat'; +export * from './constants'; +export * from './feed'; +export * from './guides'; +export * from './imageDetection'; +export * from './packs'; +export * from './packTemplates'; +export * from './seasonSuggestions'; +export * from './shared'; +export * from './trailConditions'; +export * from './trips'; +export * from './upload'; +export * from './users'; +export * from './validation'; +export * from './weather'; diff --git a/packages/schemas/src/packTemplates.ts b/packages/schemas/src/packTemplates.ts new file mode 100644 index 0000000000..7ff78add00 --- /dev/null +++ b/packages/schemas/src/packTemplates.ts @@ -0,0 +1,112 @@ +import { z } from 'zod'; + +const datetimeString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().datetime(), +); + +export const PackTemplateErrorResponseSchema = z.object({ + error: z.string(), + code: z.string().optional(), + existingTemplateId: z.string().optional(), +}); + +export const PackTemplateSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string().nullable(), + category: z.string(), + userId: z.string(), + image: z.string().nullable(), + tags: z.array(z.string()).nullable(), + isAppTemplate: z.boolean(), + deleted: z.boolean(), + localCreatedAt: datetimeString, + localUpdatedAt: datetimeString, + createdAt: datetimeString, + updatedAt: datetimeString, + contentSource: z.string().nullable(), + contentId: z.string().nullable(), +}); + +export const PackTemplateItemSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string().nullable(), + weight: z.number(), + weightUnit: z.string(), + quantity: z.number(), + category: z.string().nullable(), + consumable: z.boolean(), + worn: z.boolean(), + image: z.string().nullable(), + notes: z.string().nullable(), + packTemplateId: z.string(), + catalogItemId: z.number().nullable(), + userId: z.string(), + deleted: z.boolean(), + createdAt: datetimeString, + updatedAt: datetimeString, +}); + +export const PackTemplateWithItemsSchema = PackTemplateSchema.extend({ + items: z.array(PackTemplateItemSchema), +}); + +export const CreatePackTemplateRequestSchema = z.object({ + id: z.string(), + name: z.string().min(1).max(255), + description: z.string().optional(), + category: z.string().min(1), + image: z.string().url().optional(), + tags: z.array(z.string()).optional(), + isAppTemplate: z.boolean().optional(), + localCreatedAt: z.string().datetime(), + localUpdatedAt: z.string().datetime(), +}); + +export const UpdatePackTemplateRequestSchema = z.object({ + name: z.string().min(1).max(255).optional(), + description: z.string().nullable(), + category: z.string().min(1).optional(), + image: z.string().url().nullable(), + tags: z.array(z.string()).nullable(), + isAppTemplate: z.boolean().optional(), + deleted: z.boolean().optional(), + localUpdatedAt: z.string().datetime().optional(), +}); + +export const CreatePackTemplateItemRequestSchema = z.object({ + id: z.string(), + name: z.string().min(1).max(255), + description: z.string().optional(), + weight: z.number().min(0), + weightUnit: z.enum(['g', 'kg', 'lb', 'oz']), + quantity: z.number().int().min(1).optional().default(1), + category: z.string().optional(), + consumable: z.boolean().optional().default(false), + worn: z.boolean().optional().default(false), + image: z.string().nullish(), + notes: z.string().optional(), +}); + +export const UpdatePackTemplateItemRequestSchema = z.object({ + name: z.string().min(1).max(255).optional(), + description: z.string().optional(), + weight: z.number().min(0).optional(), + weightUnit: z.enum(['g', 'kg', 'lb', 'oz']).optional(), + quantity: z.number().int().min(1).optional(), + category: z.string().optional(), + consumable: z.boolean().optional(), + worn: z.boolean().optional(), + image: z.string().url().optional(), + notes: z.string().optional(), + deleted: z.boolean().optional(), +}); + +export const GenerateFromOnlineContentRequestSchema = z.object({ + contentUrl: z.string().url(), + isAppTemplate: z.boolean().optional().default(true), +}); + +export const GenerateFromOnlineContentResponseSchema = PackTemplateWithItemsSchema; diff --git a/packages/schemas/src/packs.ts b/packages/schemas/src/packs.ts new file mode 100644 index 0000000000..f56b3e9b7c --- /dev/null +++ b/packages/schemas/src/packs.ts @@ -0,0 +1,172 @@ +import { z } from 'zod'; +import { PACK_CATEGORIES, WEIGHT_UNITS } from './constants'; + +// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. +const datetimeString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().datetime(), +); + +export const PackItemSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string().nullable(), + weight: z.number(), + weightUnit: z.enum(WEIGHT_UNITS), + quantity: z.number().int().min(1), + category: z.string().nullable(), + consumable: z.boolean(), + worn: z.boolean(), + image: z.string().nullable(), + notes: z.string().nullable(), + packId: z.string(), + catalogItemId: z.number().int().nullable(), + userId: z.string(), + deleted: z.boolean(), + isAIGenerated: z.boolean(), + templateItemId: z.string().nullable(), + createdAt: datetimeString, + updatedAt: datetimeString, +}); + +export const PackSchema = z.object({ + id: z.string(), + userId: z.string(), + name: z.string(), + description: z.string().nullable(), + category: z.enum(PACK_CATEGORIES).nullable(), + isPublic: z.boolean(), + image: z.string().nullable(), + tags: z.array(z.string()).nullable(), + templateId: z.string().nullable().optional(), + deleted: z.boolean(), + isAIGenerated: z.boolean(), + localCreatedAt: datetimeString.optional(), + localUpdatedAt: datetimeString.optional(), + createdAt: datetimeString, + updatedAt: datetimeString, + items: z.array(PackItemSchema).optional(), +}); + +export const PackWithWeightsSchema = PackSchema.extend({ + totalWeight: z.number(), + baseWeight: z.number(), +}); + +export const CreatePackRequestSchema = z.object({ + name: z.string().min(1).max(255), + description: z.string().optional(), + category: z.string().optional(), + isPublic: z.boolean().optional().default(false), + image: z.string().nullish(), + tags: z.array(z.string()).optional(), +}); + +export const UpdatePackRequestSchema = z.object({ + name: z.string().min(1).max(255).optional(), + description: z.string().optional(), + category: z.string().optional(), + isPublic: z.boolean().optional(), + image: z.string().nullish(), + tags: z.array(z.string()).optional(), + deleted: z.boolean().optional(), +}); + +export const CreatePackItemRequestSchema = z.object({ + name: z.string().min(1).max(255), + description: z.string().optional(), + weight: z.number(), + weightUnit: z.enum(WEIGHT_UNITS).default('g'), + quantity: z.number().int().min(1).default(1), + category: z.string().optional(), + consumable: z.boolean().optional().default(false), + worn: z.boolean().optional().default(false), + image: z.string().nullish(), + notes: z.string().nullish(), + catalogItemId: z.number().int().nullish(), +}); + +export const UpdatePackItemRequestSchema = z.object({ + name: z.string().min(1).max(255).optional(), + description: z.string().optional(), + weight: z.number().optional(), + weightUnit: z.enum(WEIGHT_UNITS).optional(), + quantity: z.number().int().min(1).optional(), + category: z.string().optional(), + consumable: z.boolean().optional(), + worn: z.boolean().optional(), + image: z.string().nullish(), + notes: z.string().nullish(), + catalogItemId: z.number().int().nullish(), + deleted: z.boolean().optional(), +}); + +export const PackListResponseSchema = z.object({ + packs: z.array(PackWithWeightsSchema), + total: z.number(), + page: z.number(), + limit: z.number(), + totalPages: z.number(), +}); + +export const ItemSuggestionsResponseSchema = z.object({ + suggestions: z.array( + z.object({ + name: z.string(), + category: z.string(), + weight: z.number().optional(), + description: z.string().optional(), + confidence: z.number().min(0).max(1), + }), + ), +}); + +export const GapAnalysisRequestSchema = z.object({ + destination: z.string().optional(), + tripType: z.string().optional(), + duration: z.string().optional(), + startDate: z.string().optional(), + endDate: z.string().optional(), +}); + +export const GapAnalysisItemSchema = z.object({ + suggestion: z.string(), + reason: z.string(), + consumable: z.boolean(), + worn: z.boolean(), + category: z.string().optional(), + priority: z.enum(['must-have', 'nice-to-have', 'optional']).optional(), +}); + +export const GapAnalysisResponseSchema = z.object({ + gaps: z.array(GapAnalysisItemSchema), + summary: z.string().optional(), +}); + +// Body schemas mirroring the inline route schemas (exported so stores/clients +// can use ApiBody<> or direct z.infer<> without importing from route files). +export const CreatePackBodySchema = CreatePackRequestSchema.extend({ + id: z.string(), + localCreatedAt: z.string().datetime(), + localUpdatedAt: z.string().datetime(), +}); + +export const UpdatePackBodySchema = UpdatePackRequestSchema.extend({ + localUpdatedAt: z.string().datetime().optional(), +}); + +export const PackWeightHistoryResponseSchema = z.object({ + id: z.string(), + packId: z.string(), + userId: z.string(), + weight: z.number(), + localCreatedAt: datetimeString.optional(), + createdAt: datetimeString, + updatedAt: datetimeString, +}); + +export const CreatePackWeightHistoryBodySchema = z.object({ + id: z.string(), + weight: z.number(), + localCreatedAt: z.string().datetime(), +}); diff --git a/packages/schemas/src/seasonSuggestions.ts b/packages/schemas/src/seasonSuggestions.ts new file mode 100644 index 0000000000..296919c6c2 --- /dev/null +++ b/packages/schemas/src/seasonSuggestions.ts @@ -0,0 +1,34 @@ +import { z } from 'zod'; + +export const SeasonSuggestionsRequestSchema = z.object({ + location: z.string(), + date: z.string(), +}); + +export const PackSuggestionSchema = z.object({ + name: z.string(), + description: z.string(), + category: z.string().nullable(), + items: z.array( + z.object({ + name: z.string(), + description: z.string().nullable(), + weight: z.number().int(), + weightUnit: z.string(), + quantity: z.number().int().min(1), + category: z.string().nullable(), + consumable: z.boolean(), + worn: z.boolean(), + image: z.string().nullable().optional(), + notes: z.string().nullable(), + catalogItemId: z.number().int().nullable(), + }), + ), +}); + +export const SeasonSuggestionsResponseSchema = z.object({ + suggestions: z.array(PackSuggestionSchema), + totalInventoryItems: z.number(), + location: z.string(), + season: z.string(), +}); diff --git a/packages/schemas/src/shared.ts b/packages/schemas/src/shared.ts new file mode 100644 index 0000000000..487097150a --- /dev/null +++ b/packages/schemas/src/shared.ts @@ -0,0 +1,13 @@ +import { z } from 'zod'; + +export const ErrorResponseSchema = z.object({ + error: z.string(), + code: z.string().optional(), +}); + +export const SuccessResponseSchema = z.object({ + success: z.boolean(), +}); + +export type ErrorResponse = z.infer; +export type SuccessResponse = z.infer; diff --git a/packages/schemas/src/trailConditions.ts b/packages/schemas/src/trailConditions.ts new file mode 100644 index 0000000000..1dc4254775 --- /dev/null +++ b/packages/schemas/src/trailConditions.ts @@ -0,0 +1,32 @@ +import { z } from 'zod'; + +const datetimeString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().datetime(), +); + +export const TrailSurfaceSchema = z.enum(['paved', 'gravel', 'dirt', 'rocky', 'snow', 'mud']); +export const OverallConditionSchema = z.enum(['excellent', 'good', 'fair', 'poor']); +export const WaterCrossingDifficultySchema = z.enum(['easy', 'moderate', 'difficult']); + +export const TrailConditionReportSchema = z.object({ + id: z.string(), + trailName: z.string(), + trailRegion: z.string().nullable().optional(), + surface: TrailSurfaceSchema, + overallCondition: OverallConditionSchema, + hazards: z.array(z.string()), + waterCrossings: z.number(), + waterCrossingDifficulty: WaterCrossingDifficultySchema.nullable().optional(), + notes: z.string().nullable().optional(), + photos: z.array(z.string()), + userId: z.string().optional(), + tripId: z.string().nullable().optional(), + deleted: z.boolean(), + localCreatedAt: datetimeString.optional(), + localUpdatedAt: datetimeString.optional(), + createdAt: datetimeString.optional(), + updatedAt: datetimeString.optional(), +}); + +export type TrailConditionReport = z.infer; diff --git a/packages/schemas/src/trips.ts b/packages/schemas/src/trips.ts new file mode 100644 index 0000000000..9e4e301dc1 --- /dev/null +++ b/packages/schemas/src/trips.ts @@ -0,0 +1,61 @@ +import { z } from 'zod'; + +const datetimeString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().datetime(), +); + +const nullableDateString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().nullable(), +); + +export const TripLocationSchema = z.object({ + latitude: z.number(), + longitude: z.number(), + name: z.string().optional(), +}); + +export const TripSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string().nullable().optional(), + notes: z.string().nullable().optional(), + location: TripLocationSchema.nullable().optional(), + startDate: nullableDateString.optional(), + endDate: nullableDateString.optional(), + userId: z.string().optional(), + packId: z.string().nullable().optional(), + deleted: z.boolean(), + localCreatedAt: datetimeString.optional(), + localUpdatedAt: datetimeString.optional(), + createdAt: datetimeString.optional(), + updatedAt: datetimeString.optional(), +}); + +export const CreateTripBodySchema = z.object({ + id: z.string(), + name: z.string().min(1).max(255), + description: z.string().nullable().optional(), + notes: z.string().nullable().optional(), + location: TripLocationSchema.nullable().optional(), + startDate: z.string().nullable().optional(), + endDate: z.string().nullable().optional(), + packId: z.string().nullable().optional(), + localCreatedAt: z.string().datetime(), + localUpdatedAt: z.string().datetime(), +}); + +export const UpdateTripBodySchema = z.object({ + name: z.string().min(1).max(255).optional(), + description: z.string().nullable().optional(), + notes: z.string().nullable().optional(), + location: TripLocationSchema.nullable().optional(), + startDate: z.string().nullable().optional(), + endDate: z.string().nullable().optional(), + packId: z.string().nullable().optional(), + localUpdatedAt: z.string().datetime().optional(), +}); + +export type TripLocation = z.infer; +export type Trip = z.infer; diff --git a/packages/schemas/src/upload.ts b/packages/schemas/src/upload.ts new file mode 100644 index 0000000000..66f5a44258 --- /dev/null +++ b/packages/schemas/src/upload.ts @@ -0,0 +1,13 @@ +import { z } from 'zod'; + +export const PresignedUploadQuerySchema = z.object({ + fileName: z.string().optional(), + contentType: z.string().optional(), + size: z.string().optional(), +}); + +export const PresignedUploadResponseSchema = z.object({ + url: z.string().url(), + objectKey: z.string(), + publicUrl: z.string().url(), +}); diff --git a/packages/schemas/src/users.ts b/packages/schemas/src/users.ts new file mode 100644 index 0000000000..a1c1f15bba --- /dev/null +++ b/packages/schemas/src/users.ts @@ -0,0 +1,90 @@ +import { z } from 'zod'; + +// Base user schema +export const UserSchema = z.object({ + id: z.string(), + email: z.string().email(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), + role: z.string().nullable().default('USER'), + emailVerified: z.boolean().nullable(), + createdAt: z.string().nullable(), + updatedAt: z.string().nullable(), + avatarUrl: z.string().nullable().optional(), +}); + +// User profile response schema +export const UserProfileSchema = z.object({ + success: z.boolean(), + user: UserSchema, +}); + +// Update user request schema +export const UpdateUserRequestSchema = z.object({ + firstName: z.string().optional(), + lastName: z.string().optional(), + email: z.string().email().optional(), + avatarUrl: z.string().nullable().optional(), +}); + +// Update user response schema +export const UpdateUserResponseSchema = z.object({ + success: z.boolean(), + message: z.string(), + user: UserSchema, +}); + +// User search query schema +export const UserSearchQuerySchema = z.object({ + q: z.string().optional(), + limit: z.number().int().positive().max(100).default(20), + offset: z.number().int().min(0).default(0), +}); + +// User list response schema +export const UserListResponseSchema = z.object({ + success: z.boolean(), + users: z.array(UserSchema), + pagination: z.object({ + total: z.number().int().min(0), + limit: z.number().int().positive(), + offset: z.number().int().min(0), + hasMore: z.boolean(), + }), +}); + +// User items response schema (for pack items belonging to user) +export const UserItemsResponseSchema = z.array( + z.object({ + id: z.string(), + name: z.string(), + quantity: z.number().int().positive().default(1), + weight: z.number().positive().nullable(), + weightUnit: z.string().default('g'), + category: z.string().nullable(), + packId: z.string(), + catalogItem: z + .object({ + id: z.number(), + name: z.string(), + brand: z.string().nullable(), + categories: z.array(z.string()).nullable(), + description: z.string().nullable(), + price: z.number().nullable(), + weight: z.number().nullable(), + images: z.array(z.string()).nullable(), + }) + .nullable(), + createdAt: z.string(), + updatedAt: z.string(), + }), +); + +// Admin user stats schema +export const AdminUserStatsSchema = z.object({ + totalUsers: z.number().int().min(0), + verifiedUsers: z.number().int().min(0), + unverifiedUsers: z.number().int().min(0), + adminUsers: z.number().int().min(0), + recentSignups: z.number().int().min(0), +}); diff --git a/packages/schemas/src/validation.ts b/packages/schemas/src/validation.ts new file mode 100644 index 0000000000..1d72a902b2 --- /dev/null +++ b/packages/schemas/src/validation.ts @@ -0,0 +1,11 @@ +import { z } from 'zod'; + +export type { ValidationError } from '@packrat/db/validation'; + +export const ValidationErrorSchema = z.object({ + field: z.string(), + reason: z.string(), + value: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(), +}); + +export const ValidationErrorsSchema = z.array(ValidationErrorSchema); diff --git a/packages/schemas/src/weather.ts b/packages/schemas/src/weather.ts new file mode 100644 index 0000000000..915db646ba --- /dev/null +++ b/packages/schemas/src/weather.ts @@ -0,0 +1,254 @@ +import { z } from 'zod'; + +export const LocationSchema = z.object({ + id: z.number(), + name: z.string(), + region: z.string(), + country: z.string(), + lat: z.number(), + lon: z.number(), +}); + +// Extended location schema for API responses +export const WeatherAPILocationSchema = z.object({ + id: z.number(), + name: z.string(), + region: z.string(), + country: z.string(), + lat: z.number(), + lon: z.number(), + tz_id: z.string().optional(), + localtime_epoch: z.number().optional(), + localtime: z.string().optional(), +}); + +export const WeatherSearchQuerySchema = z.object({ + q: z.string().optional(), +}); + +export const WeatherCoordinateQuerySchema = z.object({ + lat: z.string(), + lon: z.string(), +}); + +export const WeatherLocationIdSchema = z.object({ + id: z.string(), +}); + +export const WeatherConditionSchema = z.object({ + text: z.string(), + icon: z.string(), + code: z.number(), +}); + +// Air quality schema based on actual API response +export const AirQualitySchema = z.object({ + co: z.number(), + no2: z.number(), + o3: z.number(), + so2: z.number(), + pm2_5: z.number(), + pm10: z.number(), + 'us-epa-index': z.number(), + 'gb-defra-index': z.number(), +}); + +export const WeatherCurrentSchema = z.object({ + last_updated: z.string(), + temp_c: z.number(), + temp_f: z.number(), + condition: WeatherConditionSchema, + wind_mph: z.number(), + wind_kph: z.number(), + wind_degree: z.number(), + wind_dir: z.string(), + pressure_mb: z.number(), + pressure_in: z.number(), + precip_mm: z.number(), + precip_in: z.number(), + humidity: z.number(), + cloud: z.number(), + feelslike_c: z.number(), + feelslike_f: z.number(), + vis_km: z.number(), + vis_miles: z.number(), + uv: z.number(), + gust_mph: z.number().optional(), + gust_kph: z.number().optional(), + // Additional fields from actual API response + is_day: z.number(), + windchill_c: z.number().optional(), + windchill_f: z.number().optional(), + heatindex_c: z.number().optional(), + heatindex_f: z.number().optional(), + dewpoint_c: z.number().optional(), + dewpoint_f: z.number().optional(), + will_it_rain: z.number().optional(), + chance_of_rain: z.number().optional(), + will_it_snow: z.number().optional(), + chance_of_snow: z.number().optional(), + snow_cm: z.number().optional(), + air_quality: AirQualitySchema.optional(), + short_rad: z.number().optional(), + diff_rad: z.number().optional(), + dni: z.number().optional(), + gti: z.number().optional(), +}); + +export const WeatherDaySchema = z.object({ + maxtemp_c: z.number(), + maxtemp_f: z.number(), + mintemp_c: z.number(), + mintemp_f: z.number(), + avgtemp_c: z.number(), + avgtemp_f: z.number(), + maxwind_mph: z.number(), + maxwind_kph: z.number(), + totalprecip_mm: z.number(), + totalprecip_in: z.number(), + totalsnow_cm: z.number(), + avghumidity: z.number(), + avgvis_km: z.number(), + avgvis_miles: z.number(), + uv: z.number(), + condition: WeatherConditionSchema, + daily_chance_of_rain: z.number().optional(), + daily_chance_of_snow: z.number().optional(), +}); + +export const WeatherHourSchema = z.object({ + time_epoch: z.number(), + time: z.string(), + temp_c: z.number(), + temp_f: z.number(), + condition: WeatherConditionSchema, + wind_mph: z.number(), + wind_kph: z.number(), + wind_degree: z.number(), + wind_dir: z.string(), + pressure_mb: z.number(), + pressure_in: z.number(), + precip_mm: z.number(), + precip_in: z.number(), + humidity: z.number(), + cloud: z.number(), + feelslike_c: z.number(), + feelslike_f: z.number(), + vis_km: z.number(), + vis_miles: z.number(), + uv: z.number(), + gust_mph: z.number().optional(), + gust_kph: z.number().optional(), + chance_of_rain: z.number().optional(), + chance_of_snow: z.number().optional(), + // Additional fields from actual API response + is_day: z.number(), + windchill_c: z.number().optional(), + windchill_f: z.number().optional(), + heatindex_c: z.number().optional(), + heatindex_f: z.number().optional(), + dewpoint_c: z.number().optional(), + dewpoint_f: z.number().optional(), + will_it_rain: z.number().optional(), + will_it_snow: z.number().optional(), + snow_cm: z.number().optional(), + air_quality: AirQualitySchema.optional(), + short_rad: z.number().optional(), + diff_rad: z.number().optional(), + dni: z.number().optional(), + gti: z.number().optional(), +}); + +export const WeatherForecastDaySchema = z.object({ + date: z.string(), + date_epoch: z.number(), + day: WeatherDaySchema, + astro: z + .object({ + sunrise: z.string(), + sunset: z.string(), + moonrise: z.string(), + moonset: z.string(), + moon_phase: z.string(), + moon_illumination: z.number(), + }) + .optional(), + hour: z.array(WeatherHourSchema), +}); + +export const WeatherAlertSchema = z + .object({ + alert: z + .array( + z.object({ + headline: z.string(), + msgtype: z.string(), + severity: z.string(), + urgency: z.string(), + areas: z.string(), + category: z.string(), + certainty: z.string(), + event: z.string(), + note: z.string().optional(), + effective: z.string(), + expires: z.string(), + desc: z.string(), + instruction: z.string().optional(), + }), + ) + .optional(), + }) + .optional(); + +export const WeatherForecastSchema = z.object({ + location: WeatherAPILocationSchema, + current: WeatherCurrentSchema, + forecast: z.object({ + forecastday: z.array(WeatherForecastDaySchema), + }), +}); + +// Raw WeatherAPI.com response schemas for internal use +export const WeatherAPISearchResponseSchema = z.array( + z.object({ + id: z.number(), + name: z.string(), + region: z.string(), + country: z.string(), + lat: z.number(), + lon: z.number(), + url: z.string().optional(), + }), +); + +export const WeatherAPICurrentResponseSchema = z.object({ + location: WeatherAPILocationSchema, + current: WeatherCurrentSchema, +}); + +export const WeatherAPIForecastResponseSchema = z.object({ + location: WeatherAPILocationSchema, + current: WeatherCurrentSchema, + forecast: z.object({ + forecastday: z.array(WeatherForecastDaySchema), + }), + alerts: WeatherAlertSchema.optional(), +}); + +export const LocationSearchResponseSchema = z.array(LocationSchema); + +// Export types for use in the application +export type Location = z.infer; +export type WeatherAPILocation = z.infer; +export type WeatherCondition = z.infer; +export type AirQuality = z.infer; +export type WeatherCurrent = z.infer; +export type WeatherDay = z.infer; +export type WeatherHour = z.infer; +export type WeatherForecastDay = z.infer; +export type WeatherAlert = z.infer; +export type WeatherForecast = z.infer; +export type WeatherAPISearchResponse = z.infer; +export type WeatherAPICurrentResponse = z.infer; +export type WeatherAPIForecastResponse = z.infer; +export type LocationSearchResponse = z.infer; diff --git a/packages/schemas/tsconfig.json b/packages/schemas/tsconfig.json new file mode 100644 index 0000000000..c942c77e31 --- /dev/null +++ b/packages/schemas/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "skipLibCheck": true, + "noUncheckedIndexedAccess": true, + "paths": { + "@packrat/db": ["../db/src/index.ts"], + "@packrat/db/*": ["../db/src/*"], + "@packrat/guards": ["../guards/src/index.ts"], + "@packrat/guards/*": ["../guards/src/*"] + } + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json index 643cb3a87d..7ba8032e18 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -44,7 +44,11 @@ "@packrat/cli/*": ["./packages/cli/src/*"], "@packrat/web-ui": ["./packages/web-ui/src"], "@packrat/web-ui/*": ["./packages/web-ui/src/*"], - "nativewindui/*": ["./apps/expo/components/ui/*"] + "nativewindui/*": ["./apps/expo/components/ui/*"], + "@packrat/db": ["./packages/db/src/index.ts"], + "@packrat/db/*": ["./packages/db/src/*"], + "@packrat/schemas": ["./packages/schemas/src/index.ts"], + "@packrat/schemas/*": ["./packages/schemas/src/*"] } }, "include": [ From 957fe34bca9e0f0bb6cfa7d743440beef9f4f98c Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 10:25:45 -0600 Subject: [PATCH 34/51] fix: use catalog: for drizzle-zod in packages/api --- packages/api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/package.json b/packages/api/package.json index 865c55c436..67fbd40280 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -55,7 +55,7 @@ "csv-parse": "^6.2.1", "drizzle-kit": "catalog:", "drizzle-orm": "catalog:", - "drizzle-zod": "^0.8.3", + "drizzle-zod": "catalog:", "elysia": "catalog:", "google-auth-library": "catalog:", "gray-matter": "catalog:", From a4eda7bf9fdb7d6b5dccc43a9c44c71623c46632 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 10:33:10 -0600 Subject: [PATCH 35/51] refactor: migrate all consumer imports from @packrat/api shims to @packrat/db and @packrat/schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all import paths that went through the @packrat/api re-export shims: - @packrat/api/types/constants → @packrat/db (Pack, PackItem, User, CatalogItem, etc.) - @packrat/api/schemas/* → @packrat/schemas/* - @packrat/api/types → @packrat/schemas/constants Adds @packrat/db and @packrat/schemas as direct workspace deps in apps/expo. Migrates apps/expo features, utils, and packages/api/src/utils to source from canonical packages rather than the backward-compat shims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/expo/app/(app)/current-pack/[id].tsx | 2 +- apps/expo/components/initial/WeightBadge.tsx | 2 +- apps/expo/features/catalog/hooks/useCatalogItemDetails.ts | 2 +- apps/expo/features/catalog/hooks/useCatalogItems.ts | 2 +- apps/expo/features/catalog/hooks/useVectorSearch.ts | 2 +- .../features/catalog/screens/AddCatalogItemDetailsScreen.tsx | 2 +- apps/expo/features/catalog/types.ts | 2 +- .../features/pack-templates/components/PackTemplateForm.tsx | 2 +- .../features/pack-templates/hooks/useBulkAddCatalogItems.ts | 2 +- apps/expo/features/pack-templates/packTemplateListAtoms.ts | 2 +- .../pack-templates/screens/CreatePackTemplateItemForm.tsx | 2 +- apps/expo/features/pack-templates/store/packTemplateItems.ts | 2 +- apps/expo/features/pack-templates/store/packTemplates.ts | 5 +---- apps/expo/features/packs/components/PackForm.tsx | 2 +- apps/expo/features/packs/components/TemplateItemsSection.tsx | 2 +- apps/expo/features/packs/hooks/useAddCatalogItem.ts | 2 +- apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts | 2 +- apps/expo/features/packs/hooks/usePackDetailsFromApi.ts | 2 +- apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts | 2 +- apps/expo/features/packs/input.ts | 2 +- apps/expo/features/packs/packListAtoms.ts | 2 +- apps/expo/features/packs/screens/CreatePackItemForm.tsx | 2 +- apps/expo/features/packs/store/packItems.ts | 2 +- apps/expo/features/packs/store/packWeightHistory.ts | 2 +- apps/expo/features/packs/store/packs.ts | 2 +- apps/expo/features/packs/types.ts | 2 +- apps/expo/features/profile/types.ts | 2 +- .../features/trail-conditions/store/trailConditionReports.ts | 2 +- apps/expo/features/trips/store/trips.ts | 2 +- apps/expo/features/weather/lib/weatherService.ts | 4 ++-- apps/expo/lib/utils/__tests__/compute-pack.test.ts | 2 +- apps/expo/lib/utils/compute-pack.ts | 2 +- apps/expo/package.json | 2 ++ apps/expo/utils/__tests__/weight.test.ts | 2 +- apps/expo/utils/weight.ts | 2 +- packages/api/src/utils/__tests__/itemCalculations.test.ts | 2 +- packages/api/src/utils/__tests__/weight.test.ts | 2 +- packages/api/src/utils/itemCalculations.ts | 2 +- packages/api/src/utils/weight.ts | 2 +- 39 files changed, 41 insertions(+), 42 deletions(-) diff --git a/apps/expo/app/(app)/current-pack/[id].tsx b/apps/expo/app/(app)/current-pack/[id].tsx index 0aabb315b2..f8d10710e7 100644 --- a/apps/expo/app/(app)/current-pack/[id].tsx +++ b/apps/expo/app/(app)/current-pack/[id].tsx @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/api/types/constants'; +import type { PackItem } from '@packrat/db'; import { Avatar, AvatarFallback, diff --git a/apps/expo/components/initial/WeightBadge.tsx b/apps/expo/components/initial/WeightBadge.tsx index 652e290be7..1ccbd714cf 100644 --- a/apps/expo/components/initial/WeightBadge.tsx +++ b/apps/expo/components/initial/WeightBadge.tsx @@ -1,4 +1,4 @@ -import type { WeightUnit } from '@packrat/api/types/constants'; +import type { WeightUnit } from '@packrat/db'; import { isString } from '@packrat/guards'; import { cn } from 'expo-app/lib/cn'; import { formatWeight } from 'expo-app/utils/weight'; diff --git a/apps/expo/features/catalog/hooks/useCatalogItemDetails.ts b/apps/expo/features/catalog/hooks/useCatalogItemDetails.ts index f672865154..b141891f81 100644 --- a/apps/expo/features/catalog/hooks/useCatalogItemDetails.ts +++ b/apps/expo/features/catalog/hooks/useCatalogItemDetails.ts @@ -1,4 +1,4 @@ -import { CatalogItemSchema } from '@packrat/api/schemas/catalog'; +import { CatalogItemSchema } from '@packrat/schemas/catalog'; import { useQuery } from '@tanstack/react-query'; import { apiClient } from 'expo-app/lib/api/packrat'; import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit'; diff --git a/apps/expo/features/catalog/hooks/useCatalogItems.ts b/apps/expo/features/catalog/hooks/useCatalogItems.ts index 50e9e0e7c9..528b58296b 100644 --- a/apps/expo/features/catalog/hooks/useCatalogItems.ts +++ b/apps/expo/features/catalog/hooks/useCatalogItems.ts @@ -1,4 +1,4 @@ -import { CatalogItemsResponseSchema } from '@packrat/api/schemas/catalog'; +import { CatalogItemsResponseSchema } from '@packrat/schemas/catalog'; import { useInfiniteQuery } from '@tanstack/react-query'; import { apiClient } from 'expo-app/lib/api/packrat'; diff --git a/apps/expo/features/catalog/hooks/useVectorSearch.ts b/apps/expo/features/catalog/hooks/useVectorSearch.ts index 34baec182b..ac0d8a83cb 100644 --- a/apps/expo/features/catalog/hooks/useVectorSearch.ts +++ b/apps/expo/features/catalog/hooks/useVectorSearch.ts @@ -1,4 +1,4 @@ -import { VectorSearchResponseSchema } from '@packrat/api/schemas/catalog'; +import { VectorSearchResponseSchema } from '@packrat/schemas/catalog'; import { useQuery } from '@tanstack/react-query'; import { apiClient } from 'expo-app/lib/api/packrat'; import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit'; diff --git a/apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx b/apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx index 80aec8997c..cedaffedec 100644 --- a/apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx +++ b/apps/expo/features/catalog/screens/AddCatalogItemDetailsScreen.tsx @@ -1,5 +1,5 @@ -import { WeightUnitSchema } from '@packrat/api/types'; import { assertDefined, fromZod } from '@packrat/guards'; +import { WeightUnitSchema } from '@packrat/schemas/constants'; import { Button, Text } from '@packrat/ui/nativewindui'; import { useQueryClient } from '@tanstack/react-query'; import * as Burnt from 'burnt'; diff --git a/apps/expo/features/catalog/types.ts b/apps/expo/features/catalog/types.ts index a00dbdf8e8..8b2e07e5a8 100644 --- a/apps/expo/features/catalog/types.ts +++ b/apps/expo/features/catalog/types.ts @@ -1,4 +1,4 @@ -import type { CatalogItemSchema } from '@packrat/api/schemas/catalog'; +import type { CatalogItemSchema } from '@packrat/schemas/catalog'; import type { z } from 'zod'; import type { PackItemInput } from '../packs/input'; diff --git a/apps/expo/features/pack-templates/components/PackTemplateForm.tsx b/apps/expo/features/pack-templates/components/PackTemplateForm.tsx index a52eb54cb3..7d4a6a1168 100644 --- a/apps/expo/features/pack-templates/components/PackTemplateForm.tsx +++ b/apps/expo/features/pack-templates/components/PackTemplateForm.tsx @@ -1,5 +1,5 @@ -import { PackCategorySchema } from '@packrat/api/types'; import { fromZod } from '@packrat/guards'; +import { PackCategorySchema } from '@packrat/schemas/constants'; import { Button, createDropdownItem, diff --git a/apps/expo/features/pack-templates/hooks/useBulkAddCatalogItems.ts b/apps/expo/features/pack-templates/hooks/useBulkAddCatalogItems.ts index ffb1e49686..68b0c57afe 100644 --- a/apps/expo/features/pack-templates/hooks/useBulkAddCatalogItems.ts +++ b/apps/expo/features/pack-templates/hooks/useBulkAddCatalogItems.ts @@ -1,5 +1,5 @@ -import { WeightUnitSchema } from '@packrat/api/types'; import { fromZod } from '@packrat/guards'; +import { WeightUnitSchema } from '@packrat/schemas/constants'; import { cacheCatalogItemImage } from 'expo-app/features/catalog/lib/cacheCatalogItemImage'; import type { CatalogItemWithPackItemFields } from 'expo-app/features/catalog/types'; import { useState } from 'react'; diff --git a/apps/expo/features/pack-templates/packTemplateListAtoms.ts b/apps/expo/features/pack-templates/packTemplateListAtoms.ts index c26db60cc5..f3cc105f0c 100644 --- a/apps/expo/features/pack-templates/packTemplateListAtoms.ts +++ b/apps/expo/features/pack-templates/packTemplateListAtoms.ts @@ -1,4 +1,4 @@ -import type { PackCategory } from '@packrat/api/types/constants'; +import type { PackCategory } from '@packrat/db'; import { atom } from 'jotai'; export const activeTemplateFilterAtom = atom('all'); diff --git a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx b/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx index 1b976c7d3b..528554db6c 100644 --- a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx +++ b/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx @@ -1,7 +1,7 @@ // CreatePackTemplateItemForm.tsx import { useActionSheet } from '@expo/react-native-action-sheet'; -import type { WeightUnit } from '@packrat/api/types/constants'; +import type { WeightUnit } from '@packrat/db'; import { safeIndexOf } from '@packrat/guards'; import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui'; import { useForm } from '@tanstack/react-form'; diff --git a/apps/expo/features/pack-templates/store/packTemplateItems.ts b/apps/expo/features/pack-templates/store/packTemplateItems.ts index f942a5e6f4..8ac2fbaef4 100644 --- a/apps/expo/features/pack-templates/store/packTemplateItems.ts +++ b/apps/expo/features/pack-templates/store/packTemplateItems.ts @@ -4,7 +4,7 @@ import { syncedCrud } from '@legendapp/state/sync-plugins/crud'; import { PackTemplateItemSchema, PackTemplateWithItemsSchema, -} from '@packrat/api/schemas/packTemplates'; +} from '@packrat/schemas/packTemplates'; import { isAuthed } from 'expo-app/features/auth/store'; import { apiClient } from 'expo-app/lib/api/packrat'; import { persistPlugin } from 'expo-app/lib/persist-plugin'; diff --git a/apps/expo/features/pack-templates/store/packTemplates.ts b/apps/expo/features/pack-templates/store/packTemplates.ts index 8e6afbf641..78d023754b 100644 --- a/apps/expo/features/pack-templates/store/packTemplates.ts +++ b/apps/expo/features/pack-templates/store/packTemplates.ts @@ -1,10 +1,7 @@ import { observable, syncState } from '@legendapp/state'; import { syncObservable } from '@legendapp/state/sync'; import { syncedCrud } from '@legendapp/state/sync-plugins/crud'; -import { - PackTemplateSchema, - PackTemplateWithItemsSchema, -} from '@packrat/api/schemas/packTemplates'; +import { PackTemplateSchema, PackTemplateWithItemsSchema } from '@packrat/schemas/packTemplates'; import { isAuthed } from 'expo-app/features/auth/store'; import { apiClient } from 'expo-app/lib/api/packrat'; import { persistPlugin } from 'expo-app/lib/persist-plugin'; diff --git a/apps/expo/features/packs/components/PackForm.tsx b/apps/expo/features/packs/components/PackForm.tsx index 2b60376d96..f1f8c34966 100644 --- a/apps/expo/features/packs/components/PackForm.tsx +++ b/apps/expo/features/packs/components/PackForm.tsx @@ -1,5 +1,5 @@ -import { PackCategorySchema } from '@packrat/api/types'; import { fromZod } from '@packrat/guards'; +import { PackCategorySchema } from '@packrat/schemas/constants'; import { Button, createDropdownItem, diff --git a/apps/expo/features/packs/components/TemplateItemsSection.tsx b/apps/expo/features/packs/components/TemplateItemsSection.tsx index 312e10e689..e1a306d754 100644 --- a/apps/expo/features/packs/components/TemplateItemsSection.tsx +++ b/apps/expo/features/packs/components/TemplateItemsSection.tsx @@ -1,4 +1,4 @@ -import type { WeightUnit } from '@packrat/api/types/constants'; +import type { WeightUnit } from '@packrat/db'; import { Icon } from 'expo-app/components/Icon'; import { cn } from 'expo-app/lib/cn'; import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme'; diff --git a/apps/expo/features/packs/hooks/useAddCatalogItem.ts b/apps/expo/features/packs/hooks/useAddCatalogItem.ts index d5a27dfbf4..63a509d77b 100644 --- a/apps/expo/features/packs/hooks/useAddCatalogItem.ts +++ b/apps/expo/features/packs/hooks/useAddCatalogItem.ts @@ -1,5 +1,5 @@ -import { WeightUnitSchema } from '@packrat/api/types'; import { fromZod } from '@packrat/guards'; +import { WeightUnitSchema } from '@packrat/schemas/constants'; import * as Burnt from 'burnt'; import { cacheCatalogItemImage } from 'expo-app/features/catalog/lib/cacheCatalogItemImage'; import type { CatalogItem } from 'expo-app/features/catalog/types'; diff --git a/apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts b/apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts index 24a36cd18d..463b4a4b42 100644 --- a/apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts +++ b/apps/expo/features/packs/hooks/useBulkAddCatalogItems.ts @@ -1,5 +1,5 @@ -import { WeightUnitSchema } from '@packrat/api/types'; import { fromZod } from '@packrat/guards'; +import { WeightUnitSchema } from '@packrat/schemas/constants'; import { useState } from 'react'; import { cacheCatalogItemImage } from '../../catalog/lib/cacheCatalogItemImage'; import type { CatalogItemWithPackItemFields } from '../../catalog/types'; diff --git a/apps/expo/features/packs/hooks/usePackDetailsFromApi.ts b/apps/expo/features/packs/hooks/usePackDetailsFromApi.ts index 67714cec3f..8bf90ec97a 100644 --- a/apps/expo/features/packs/hooks/usePackDetailsFromApi.ts +++ b/apps/expo/features/packs/hooks/usePackDetailsFromApi.ts @@ -1,4 +1,4 @@ -import { PackWithWeightsSchema } from '@packrat/api/schemas/packs'; +import { PackWithWeightsSchema } from '@packrat/schemas/packs'; import { useQuery } from '@tanstack/react-query'; import { apiClient } from 'expo-app/lib/api/packrat'; import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit'; diff --git a/apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts b/apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts index 8229888c23..b12f8f829f 100644 --- a/apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts +++ b/apps/expo/features/packs/hooks/usePackItemDetailsFromApi.ts @@ -1,4 +1,4 @@ -import { PackItemSchema } from '@packrat/api/schemas/packs'; +import { PackItemSchema } from '@packrat/schemas/packs'; import { useQuery } from '@tanstack/react-query'; import { apiClient } from 'expo-app/lib/api/packrat'; import { useAuthenticatedQueryToolkit } from 'expo-app/lib/hooks/useAuthenticatedQueryToolkit'; diff --git a/apps/expo/features/packs/input.ts b/apps/expo/features/packs/input.ts index 557c53cb18..39f411923a 100644 --- a/apps/expo/features/packs/input.ts +++ b/apps/expo/features/packs/input.ts @@ -1,4 +1,4 @@ -import type { WeightUnit } from '@packrat/api/types/constants'; +import type { WeightUnit } from '@packrat/db'; export interface PackItemInput { name: string; diff --git a/apps/expo/features/packs/packListAtoms.ts b/apps/expo/features/packs/packListAtoms.ts index cd83839fd6..6557f2e8c1 100644 --- a/apps/expo/features/packs/packListAtoms.ts +++ b/apps/expo/features/packs/packListAtoms.ts @@ -1,4 +1,4 @@ -import type { PackCategory } from '@packrat/api/types/constants'; +import type { PackCategory } from '@packrat/db'; import { atom } from 'jotai'; export const activeFilterAtom = atom('all'); diff --git a/apps/expo/features/packs/screens/CreatePackItemForm.tsx b/apps/expo/features/packs/screens/CreatePackItemForm.tsx index aeab94e8d0..10f9ee2cce 100644 --- a/apps/expo/features/packs/screens/CreatePackItemForm.tsx +++ b/apps/expo/features/packs/screens/CreatePackItemForm.tsx @@ -1,5 +1,5 @@ import { useActionSheet } from '@expo/react-native-action-sheet'; -import type { WeightUnit } from '@packrat/api/types/constants'; +import type { WeightUnit } from '@packrat/db'; import { safeIndexOf } from '@packrat/guards'; import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui'; import { useForm } from '@tanstack/react-form'; diff --git a/apps/expo/features/packs/store/packItems.ts b/apps/expo/features/packs/store/packItems.ts index e8339a567f..e7d88f1744 100644 --- a/apps/expo/features/packs/store/packItems.ts +++ b/apps/expo/features/packs/store/packItems.ts @@ -1,8 +1,8 @@ import { observable, syncState } from '@legendapp/state'; import { syncObservable } from '@legendapp/state/sync'; import { syncedCrud } from '@legendapp/state/sync-plugins/crud'; -import { PackItemSchema, PackWithWeightsSchema } from '@packrat/api/schemas/packs'; import { isRemoteUrl } from '@packrat/guards'; +import { PackItemSchema, PackWithWeightsSchema } from '@packrat/schemas/packs'; import { isAuthed } from 'expo-app/features/auth/store'; import { apiClient } from 'expo-app/lib/api/packrat'; import { persistPlugin } from 'expo-app/lib/persist-plugin'; diff --git a/apps/expo/features/packs/store/packWeightHistory.ts b/apps/expo/features/packs/store/packWeightHistory.ts index 75dc2f32a1..8a5f979ce6 100644 --- a/apps/expo/features/packs/store/packWeightHistory.ts +++ b/apps/expo/features/packs/store/packWeightHistory.ts @@ -1,7 +1,7 @@ import { observable, syncState } from '@legendapp/state'; import { syncObservable } from '@legendapp/state/sync'; import { syncedCrud } from '@legendapp/state/sync-plugins/crud'; -import { PackWeightHistoryResponseSchema } from '@packrat/api/schemas/packs'; +import { PackWeightHistoryResponseSchema } from '@packrat/schemas/packs'; import { isAuthed } from 'expo-app/features/auth/store'; import { apiClient } from 'expo-app/lib/api/packrat'; import { persistPlugin } from 'expo-app/lib/persist-plugin'; diff --git a/apps/expo/features/packs/store/packs.ts b/apps/expo/features/packs/store/packs.ts index 4cf21b0b8b..f73daf13e3 100644 --- a/apps/expo/features/packs/store/packs.ts +++ b/apps/expo/features/packs/store/packs.ts @@ -1,7 +1,7 @@ import { observable, syncState } from '@legendapp/state'; import { syncObservable } from '@legendapp/state/sync'; import { syncedCrud } from '@legendapp/state/sync-plugins/crud'; -import { PackWithWeightsSchema } from '@packrat/api/schemas/packs'; +import { PackWithWeightsSchema } from '@packrat/schemas/packs'; import { isAuthed } from 'expo-app/features/auth/store'; import { apiClient } from 'expo-app/lib/api/packrat'; import { persistPlugin } from 'expo-app/lib/persist-plugin'; diff --git a/apps/expo/features/packs/types.ts b/apps/expo/features/packs/types.ts index dc1f4f0129..73387c063a 100644 --- a/apps/expo/features/packs/types.ts +++ b/apps/expo/features/packs/types.ts @@ -1,4 +1,4 @@ -import type { PackCategory, WeightUnit } from '@packrat/api/types/constants'; +import type { PackCategory, WeightUnit } from '@packrat/db'; import type { CatalogItem } from 'expo-app/features/catalog/types'; import type { PackTemplateItem } from 'expo-app/features/pack-templates/types'; diff --git a/apps/expo/features/profile/types.ts b/apps/expo/features/profile/types.ts index 433004df47..00836cd2eb 100644 --- a/apps/expo/features/profile/types.ts +++ b/apps/expo/features/profile/types.ts @@ -1,4 +1,4 @@ -import type { UserSchema } from '@packrat/api/schemas/users'; +import type { UserSchema } from '@packrat/schemas/users'; import type { z } from 'zod'; import type { WeightUnit } from '../packs/types'; diff --git a/apps/expo/features/trail-conditions/store/trailConditionReports.ts b/apps/expo/features/trail-conditions/store/trailConditionReports.ts index 0ddc8d5115..21a5989235 100644 --- a/apps/expo/features/trail-conditions/store/trailConditionReports.ts +++ b/apps/expo/features/trail-conditions/store/trailConditionReports.ts @@ -1,7 +1,7 @@ import { observable, syncState } from '@legendapp/state'; import { syncObservable } from '@legendapp/state/sync'; import { syncedCrud } from '@legendapp/state/sync-plugins/crud'; -import { TrailConditionReportSchema } from '@packrat/api/schemas/trailConditions'; +import { TrailConditionReportSchema } from '@packrat/schemas/trailConditions'; import { isAuthed } from 'expo-app/features/auth/store'; import { apiClient } from 'expo-app/lib/api/packrat'; import { persistPlugin } from 'expo-app/lib/persist-plugin'; diff --git a/apps/expo/features/trips/store/trips.ts b/apps/expo/features/trips/store/trips.ts index 6d428457d2..d14d5c0bc6 100644 --- a/apps/expo/features/trips/store/trips.ts +++ b/apps/expo/features/trips/store/trips.ts @@ -1,7 +1,7 @@ import { observable, syncState } from '@legendapp/state'; import { syncObservable } from '@legendapp/state/sync'; import { syncedCrud } from '@legendapp/state/sync-plugins/crud'; -import { TripSchema } from '@packrat/api/schemas/trips'; +import { TripSchema } from '@packrat/schemas/trips'; import { isAuthed } from 'expo-app/features/auth/store'; import { apiClient } from 'expo-app/lib/api/packrat'; import { persistPlugin } from 'expo-app/lib/persist-plugin'; diff --git a/apps/expo/features/weather/lib/weatherService.ts b/apps/expo/features/weather/lib/weatherService.ts index 89f4078671..284cc2dd57 100644 --- a/apps/expo/features/weather/lib/weatherService.ts +++ b/apps/expo/features/weather/lib/weatherService.ts @@ -1,9 +1,9 @@ +import { assertDefined } from '@packrat/guards'; import { LocationSearchResponseSchema, type WeatherAPIForecastResponse, WeatherAPIForecastResponseSchema, -} from '@packrat/api/schemas/weather'; -import { assertDefined } from '@packrat/guards'; +} from '@packrat/schemas/weather'; import * as Sentry from '@sentry/react-native'; import { apiClient } from 'expo-app/lib/api/packrat'; import { getWeatherIconName as getIconNameFromCode } from './weatherIcons'; diff --git a/apps/expo/lib/utils/__tests__/compute-pack.test.ts b/apps/expo/lib/utils/__tests__/compute-pack.test.ts index 322d8ae2bd..535935d8a0 100644 --- a/apps/expo/lib/utils/__tests__/compute-pack.test.ts +++ b/apps/expo/lib/utils/__tests__/compute-pack.test.ts @@ -1,4 +1,4 @@ -import type { PackItem, PackWithItems } from '@packrat/api/types/constants'; +import type { PackItem, PackWithItems } from '@packrat/db'; import { describe, expect, it } from 'vitest'; import { computePacksWeights, computePackWeights } from '../compute-pack'; diff --git a/apps/expo/lib/utils/compute-pack.ts b/apps/expo/lib/utils/compute-pack.ts index 99bbf900a4..4d24f85db7 100644 --- a/apps/expo/lib/utils/compute-pack.ts +++ b/apps/expo/lib/utils/compute-pack.ts @@ -1,4 +1,4 @@ -import type { PackWithItems } from '@packrat/api/types/constants'; +import type { PackWithItems } from '@packrat/db'; import type { WeightUnit } from '@packrat/units'; import { displayWeight, normalize, parseWeightUnit } from '@packrat/units'; diff --git a/apps/expo/package.json b/apps/expo/package.json index 66c209211d..0e25b70e2a 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -53,8 +53,10 @@ "@legendapp/state": "^3.0.0-beta.30", "@packrat/api-client": "workspace:*", "@packrat/config": "workspace:*", + "@packrat/db": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", + "@packrat/schemas": "workspace:*", "@packrat/units": "workspace:*", "@react-native-ai/apple": "~0.10.0", "@react-native-ai/llama": "~0.10.0", diff --git a/apps/expo/utils/__tests__/weight.test.ts b/apps/expo/utils/__tests__/weight.test.ts index c8080690f4..dfbbec4e57 100644 --- a/apps/expo/utils/__tests__/weight.test.ts +++ b/apps/expo/utils/__tests__/weight.test.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/api/types/constants'; +import type { PackItem } from '@packrat/db'; import { describe, expect, it } from 'vitest'; import { calculateBaseWeight, calculateTotalWeight, convertWeight, formatWeight } from '../weight'; diff --git a/apps/expo/utils/weight.ts b/apps/expo/utils/weight.ts index 33bb474974..b036d4f995 100644 --- a/apps/expo/utils/weight.ts +++ b/apps/expo/utils/weight.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/api/types/constants'; +import type { PackItem } from '@packrat/db'; import type { WeightUnit } from '@packrat/units'; import { convert, displayWeight, normalize, parseWeightUnit } from '@packrat/units'; diff --git a/packages/api/src/utils/__tests__/itemCalculations.test.ts b/packages/api/src/utils/__tests__/itemCalculations.test.ts index 683b2a579b..349ab74b0a 100644 --- a/packages/api/src/utils/__tests__/itemCalculations.test.ts +++ b/packages/api/src/utils/__tests__/itemCalculations.test.ts @@ -1,4 +1,4 @@ -import type { CatalogItem, PackItem } from '@packrat/api/types/constants'; +import type { CatalogItem, PackItem } from '@packrat/db'; import { describe, expect, it } from 'vitest'; import { calculateTotalWeight, diff --git a/packages/api/src/utils/__tests__/weight.test.ts b/packages/api/src/utils/__tests__/weight.test.ts index 50c8ccb1cd..289c3f978d 100644 --- a/packages/api/src/utils/__tests__/weight.test.ts +++ b/packages/api/src/utils/__tests__/weight.test.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/api/types/constants'; +import type { PackItem } from '@packrat/db'; import { describe, expect, it } from 'vitest'; import { calculateBaseWeight, diff --git a/packages/api/src/utils/itemCalculations.ts b/packages/api/src/utils/itemCalculations.ts index 61cb12b3be..bb23b494e5 100644 --- a/packages/api/src/utils/itemCalculations.ts +++ b/packages/api/src/utils/itemCalculations.ts @@ -1,4 +1,4 @@ -import type { CatalogItem, PackItem, WeightUnit } from '@packrat/api/types/constants'; +import type { CatalogItem, PackItem, WeightUnit } from '@packrat/db'; /** * Checks if an item is a pack item diff --git a/packages/api/src/utils/weight.ts b/packages/api/src/utils/weight.ts index 223f922733..16ba98ea8c 100644 --- a/packages/api/src/utils/weight.ts +++ b/packages/api/src/utils/weight.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/api/types/constants'; +import type { PackItem } from '@packrat/db'; import type { WeightUnit } from '@packrat/units'; import { convert, displayWeight, fromGrams, normalize, parseWeightUnit } from '@packrat/units'; From 51628ab7d84c4c0b33ab7ebf2280942401af10d9 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 11:07:18 -0600 Subject: [PATCH 36/51] refactor: extract @packrat/constants and @packrat/types packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a clean 4-layer architecture: @packrat/constants (zero dep) raw arrays, TS unions, plain interfaces @packrat/db Drizzle tables + inferred model types @packrat/schemas Zod schemas built on constants + db @packrat/types re-exports model types from db for frontend consumption - @packrat/constants: PACK_CATEGORIES, WEIGHT_UNITS, ITEM_CATEGORIES, WeightUnit, PackCategory, ItemLink, ItemReview, etc. - @packrat/types: Pack, PackItem, User, CatalogItem, PackWithItems, Trip, etc. - @packrat/db/constants.ts becomes a re-export shim (backward compat) - @packrat/units now sources WeightUnit/WEIGHT_UNITS from @packrat/constants instead of defining its own duplicate - Consumer imports in apps/expo and packages/api updated accordingly 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/expo/app/(app)/current-pack/[id].tsx | 2 +- apps/expo/components/initial/WeightBadge.tsx | 2 +- .../pack-templates/packTemplateListAtoms.ts | 2 +- .../screens/CreatePackTemplateItemForm.tsx | 2 +- .../packs/components/TemplateItemsSection.tsx | 2 +- apps/expo/features/packs/input.ts | 2 +- apps/expo/features/packs/packListAtoms.ts | 2 +- .../packs/screens/CreatePackItemForm.tsx | 2 +- apps/expo/features/packs/types.ts | 2 +- .../lib/utils/__tests__/compute-pack.test.ts | 2 +- apps/expo/lib/utils/compute-pack.ts | 2 +- apps/expo/package.json | 2 + apps/expo/utils/__tests__/weight.test.ts | 2 +- apps/expo/utils/weight.ts | 2 +- bun.lock | 32 ++++++++++- packages/api/package.json | 2 + packages/api/src/types/constants.ts | 2 +- .../utils/__tests__/itemCalculations.test.ts | 2 +- .../api/src/utils/__tests__/weight.test.ts | 2 +- packages/api/src/utils/itemCalculations.ts | 3 +- packages/api/src/utils/weight.ts | 2 +- packages/constants/package.json | 17 ++++++ packages/constants/src/index.ts | 55 ++++++++++++++++++ packages/constants/tsconfig.json | 8 +++ packages/db/package.json | 1 + packages/db/src/constants.ts | 56 +------------------ packages/schemas/package.json | 1 + packages/schemas/src/constants.ts | 4 +- packages/types/package.json | 20 +++++++ packages/types/src/index.ts | 52 +++++++++++++++++ packages/types/tsconfig.json | 8 +++ packages/units/package.json | 1 + packages/units/src/index.ts | 7 +-- tsconfig.json | 6 +- 34 files changed, 228 insertions(+), 81 deletions(-) create mode 100644 packages/constants/package.json create mode 100644 packages/constants/src/index.ts create mode 100644 packages/constants/tsconfig.json create mode 100644 packages/types/package.json create mode 100644 packages/types/src/index.ts create mode 100644 packages/types/tsconfig.json diff --git a/apps/expo/app/(app)/current-pack/[id].tsx b/apps/expo/app/(app)/current-pack/[id].tsx index f8d10710e7..0e0b28f5e3 100644 --- a/apps/expo/app/(app)/current-pack/[id].tsx +++ b/apps/expo/app/(app)/current-pack/[id].tsx @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/db'; +import type { PackItem } from '@packrat/types'; import { Avatar, AvatarFallback, diff --git a/apps/expo/components/initial/WeightBadge.tsx b/apps/expo/components/initial/WeightBadge.tsx index 1ccbd714cf..10b58ae0da 100644 --- a/apps/expo/components/initial/WeightBadge.tsx +++ b/apps/expo/components/initial/WeightBadge.tsx @@ -1,4 +1,4 @@ -import type { WeightUnit } from '@packrat/db'; +import type { WeightUnit } from '@packrat/constants'; import { isString } from '@packrat/guards'; import { cn } from 'expo-app/lib/cn'; import { formatWeight } from 'expo-app/utils/weight'; diff --git a/apps/expo/features/pack-templates/packTemplateListAtoms.ts b/apps/expo/features/pack-templates/packTemplateListAtoms.ts index f3cc105f0c..beea35f837 100644 --- a/apps/expo/features/pack-templates/packTemplateListAtoms.ts +++ b/apps/expo/features/pack-templates/packTemplateListAtoms.ts @@ -1,4 +1,4 @@ -import type { PackCategory } from '@packrat/db'; +import type { PackCategory } from '@packrat/constants'; import { atom } from 'jotai'; export const activeTemplateFilterAtom = atom('all'); diff --git a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx b/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx index 528554db6c..8a9db11b7c 100644 --- a/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx +++ b/apps/expo/features/pack-templates/screens/CreatePackTemplateItemForm.tsx @@ -1,7 +1,7 @@ // CreatePackTemplateItemForm.tsx import { useActionSheet } from '@expo/react-native-action-sheet'; -import type { WeightUnit } from '@packrat/db'; +import type { WeightUnit } from '@packrat/constants'; import { safeIndexOf } from '@packrat/guards'; import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui'; import { useForm } from '@tanstack/react-form'; diff --git a/apps/expo/features/packs/components/TemplateItemsSection.tsx b/apps/expo/features/packs/components/TemplateItemsSection.tsx index e1a306d754..e4086b0903 100644 --- a/apps/expo/features/packs/components/TemplateItemsSection.tsx +++ b/apps/expo/features/packs/components/TemplateItemsSection.tsx @@ -1,4 +1,4 @@ -import type { WeightUnit } from '@packrat/db'; +import type { WeightUnit } from '@packrat/constants'; import { Icon } from 'expo-app/components/Icon'; import { cn } from 'expo-app/lib/cn'; import { useColorScheme } from 'expo-app/lib/hooks/useColorScheme'; diff --git a/apps/expo/features/packs/input.ts b/apps/expo/features/packs/input.ts index 39f411923a..2ae602c9b2 100644 --- a/apps/expo/features/packs/input.ts +++ b/apps/expo/features/packs/input.ts @@ -1,4 +1,4 @@ -import type { WeightUnit } from '@packrat/db'; +import type { WeightUnit } from '@packrat/constants'; export interface PackItemInput { name: string; diff --git a/apps/expo/features/packs/packListAtoms.ts b/apps/expo/features/packs/packListAtoms.ts index 6557f2e8c1..83d91ef285 100644 --- a/apps/expo/features/packs/packListAtoms.ts +++ b/apps/expo/features/packs/packListAtoms.ts @@ -1,4 +1,4 @@ -import type { PackCategory } from '@packrat/db'; +import type { PackCategory } from '@packrat/constants'; import { atom } from 'jotai'; export const activeFilterAtom = atom('all'); diff --git a/apps/expo/features/packs/screens/CreatePackItemForm.tsx b/apps/expo/features/packs/screens/CreatePackItemForm.tsx index 10f9ee2cce..324de70dd7 100644 --- a/apps/expo/features/packs/screens/CreatePackItemForm.tsx +++ b/apps/expo/features/packs/screens/CreatePackItemForm.tsx @@ -1,5 +1,5 @@ import { useActionSheet } from '@expo/react-native-action-sheet'; -import type { WeightUnit } from '@packrat/db'; +import type { WeightUnit } from '@packrat/constants'; import { safeIndexOf } from '@packrat/guards'; import { Form, FormItem, FormSection, SegmentedControl, TextField } from '@packrat/ui/nativewindui'; import { useForm } from '@tanstack/react-form'; diff --git a/apps/expo/features/packs/types.ts b/apps/expo/features/packs/types.ts index 73387c063a..512988ee48 100644 --- a/apps/expo/features/packs/types.ts +++ b/apps/expo/features/packs/types.ts @@ -1,4 +1,4 @@ -import type { PackCategory, WeightUnit } from '@packrat/db'; +import type { PackCategory, WeightUnit } from '@packrat/constants'; import type { CatalogItem } from 'expo-app/features/catalog/types'; import type { PackTemplateItem } from 'expo-app/features/pack-templates/types'; diff --git a/apps/expo/lib/utils/__tests__/compute-pack.test.ts b/apps/expo/lib/utils/__tests__/compute-pack.test.ts index 535935d8a0..fb881e4ee9 100644 --- a/apps/expo/lib/utils/__tests__/compute-pack.test.ts +++ b/apps/expo/lib/utils/__tests__/compute-pack.test.ts @@ -1,4 +1,4 @@ -import type { PackItem, PackWithItems } from '@packrat/db'; +import type { PackItem, PackWithItems } from '@packrat/types'; import { describe, expect, it } from 'vitest'; import { computePacksWeights, computePackWeights } from '../compute-pack'; diff --git a/apps/expo/lib/utils/compute-pack.ts b/apps/expo/lib/utils/compute-pack.ts index 4d24f85db7..28b81d97df 100644 --- a/apps/expo/lib/utils/compute-pack.ts +++ b/apps/expo/lib/utils/compute-pack.ts @@ -1,4 +1,4 @@ -import type { PackWithItems } from '@packrat/db'; +import type { PackWithItems } from '@packrat/types'; import type { WeightUnit } from '@packrat/units'; import { displayWeight, normalize, parseWeightUnit } from '@packrat/units'; diff --git a/apps/expo/package.json b/apps/expo/package.json index 0e25b70e2a..596bf4ef50 100644 --- a/apps/expo/package.json +++ b/apps/expo/package.json @@ -53,10 +53,12 @@ "@legendapp/state": "^3.0.0-beta.30", "@packrat/api-client": "workspace:*", "@packrat/config": "workspace:*", + "@packrat/constants": "workspace:*", "@packrat/db": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/schemas": "workspace:*", + "@packrat/types": "workspace:*", "@packrat/units": "workspace:*", "@react-native-ai/apple": "~0.10.0", "@react-native-ai/llama": "~0.10.0", diff --git a/apps/expo/utils/__tests__/weight.test.ts b/apps/expo/utils/__tests__/weight.test.ts index dfbbec4e57..2cc756e161 100644 --- a/apps/expo/utils/__tests__/weight.test.ts +++ b/apps/expo/utils/__tests__/weight.test.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/db'; +import type { PackItem } from '@packrat/types'; import { describe, expect, it } from 'vitest'; import { calculateBaseWeight, calculateTotalWeight, convertWeight, formatWeight } from '../weight'; diff --git a/apps/expo/utils/weight.ts b/apps/expo/utils/weight.ts index b036d4f995..c790dce53d 100644 --- a/apps/expo/utils/weight.ts +++ b/apps/expo/utils/weight.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/db'; +import type { PackItem } from '@packrat/types'; import type { WeightUnit } from '@packrat/units'; import { convert, displayWeight, normalize, parseWeightUnit } from '@packrat/units'; diff --git a/bun.lock b/bun.lock index 7a204cbed1..4575f9f26f 100644 --- a/bun.lock +++ b/bun.lock @@ -77,8 +77,12 @@ "@legendapp/state": "^3.0.0-beta.30", "@packrat/api-client": "workspace:*", "@packrat/config": "workspace:*", + "@packrat/constants": "workspace:*", + "@packrat/db": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", + "@packrat/schemas": "workspace:*", + "@packrat/types": "workspace:*", "@packrat/units": "workspace:*", "@react-native-ai/apple": "~0.10.0", "@react-native-ai/llama": "~0.10.0", @@ -452,11 +456,13 @@ "@elysiajs/openapi": "catalog:", "@mozilla/readability": "^0.6.0", "@neondatabase/serverless": "catalog:", + "@packrat/constants": "workspace:*", "@packrat/db": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/overpass": "workspace:*", "@packrat/schemas": "workspace:*", + "@packrat/types": "workspace:*", "@packrat/units": "workspace:*", "@sinclair/typebox": "^0.34.15", "@types/nodemailer": "^6.4.17", @@ -465,7 +471,7 @@ "csv-parse": "^6.2.1", "drizzle-kit": "catalog:", "drizzle-orm": "catalog:", - "drizzle-zod": "^0.8.3", + "drizzle-zod": "catalog:", "elysia": "catalog:", "google-auth-library": "catalog:", "gray-matter": "catalog:", @@ -569,10 +575,18 @@ "@packrat/guards": "workspace:*", }, }, + "packages/constants": { + "name": "@packrat/constants", + "version": "0.0.0", + "devDependencies": { + "typescript": "catalog:", + }, + }, "packages/db": { "name": "@packrat/db", "version": "0.0.0", "dependencies": { + "@packrat/constants": "workspace:*", "drizzle-orm": "catalog:", "drizzle-zod": "catalog:", }, @@ -654,6 +668,7 @@ "name": "@packrat/schemas", "version": "0.0.0", "dependencies": { + "@packrat/constants": "workspace:*", "@packrat/db": "workspace:*", "@packrat/guards": "workspace:*", "zod": "catalog:", @@ -662,6 +677,16 @@ "typescript": "catalog:", }, }, + "packages/types": { + "name": "@packrat/types", + "version": "0.0.0", + "dependencies": { + "@packrat/db": "workspace:*", + }, + "devDependencies": { + "typescript": "catalog:", + }, + }, "packages/ui": { "name": "@packrat/ui", "version": "2.0.25", @@ -673,6 +698,7 @@ "name": "@packrat/units", "version": "0.1.0", "dependencies": { + "@packrat/constants": "workspace:*", "@packrat/guards": "workspace:*", }, "devDependencies": { @@ -1567,6 +1593,8 @@ "@packrat/config": ["@packrat/config@workspace:packages/config"], + "@packrat/constants": ["@packrat/constants@workspace:packages/constants"], + "@packrat/db": ["@packrat/db@workspace:packages/db"], "@packrat/env": ["@packrat/env@workspace:packages/env"], @@ -1583,6 +1611,8 @@ "@packrat/schemas": ["@packrat/schemas@workspace:packages/schemas"], + "@packrat/types": ["@packrat/types@workspace:packages/types"], + "@packrat/ui": ["@packrat/ui@workspace:packages/ui"], "@packrat/units": ["@packrat/units@workspace:packages/units"], diff --git a/packages/api/package.json b/packages/api/package.json index 67fbd40280..11688447e2 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -42,11 +42,13 @@ "@elysiajs/openapi": "catalog:", "@mozilla/readability": "^0.6.0", "@neondatabase/serverless": "catalog:", + "@packrat/constants": "workspace:*", "@packrat/db": "workspace:*", "@packrat/env": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/overpass": "workspace:*", "@packrat/schemas": "workspace:*", + "@packrat/types": "workspace:*", "@packrat/units": "workspace:*", "@sinclair/typebox": "^0.34.15", "@types/nodemailer": "^6.4.17", diff --git a/packages/api/src/types/constants.ts b/packages/api/src/types/constants.ts index 8643efaba1..9797bbf02a 100644 --- a/packages/api/src/types/constants.ts +++ b/packages/api/src/types/constants.ts @@ -1,2 +1,2 @@ -export type { CatalogItem, Pack, PackItem, PackWithItems, User } from '@packrat/db'; export * from '@packrat/schemas/constants'; +export type { CatalogItem, Pack, PackItem, PackWithItems, User } from '@packrat/types'; diff --git a/packages/api/src/utils/__tests__/itemCalculations.test.ts b/packages/api/src/utils/__tests__/itemCalculations.test.ts index 349ab74b0a..de5fee5d61 100644 --- a/packages/api/src/utils/__tests__/itemCalculations.test.ts +++ b/packages/api/src/utils/__tests__/itemCalculations.test.ts @@ -1,4 +1,4 @@ -import type { CatalogItem, PackItem } from '@packrat/db'; +import type { CatalogItem, PackItem } from '@packrat/types'; import { describe, expect, it } from 'vitest'; import { calculateTotalWeight, diff --git a/packages/api/src/utils/__tests__/weight.test.ts b/packages/api/src/utils/__tests__/weight.test.ts index 289c3f978d..b1220ac556 100644 --- a/packages/api/src/utils/__tests__/weight.test.ts +++ b/packages/api/src/utils/__tests__/weight.test.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/db'; +import type { PackItem } from '@packrat/types'; import { describe, expect, it } from 'vitest'; import { calculateBaseWeight, diff --git a/packages/api/src/utils/itemCalculations.ts b/packages/api/src/utils/itemCalculations.ts index bb23b494e5..c65eaa63cf 100644 --- a/packages/api/src/utils/itemCalculations.ts +++ b/packages/api/src/utils/itemCalculations.ts @@ -1,4 +1,5 @@ -import type { CatalogItem, PackItem, WeightUnit } from '@packrat/db'; +import type { WeightUnit } from '@packrat/constants'; +import type { CatalogItem, PackItem } from '@packrat/types'; /** * Checks if an item is a pack item diff --git a/packages/api/src/utils/weight.ts b/packages/api/src/utils/weight.ts index 16ba98ea8c..1654bd70bc 100644 --- a/packages/api/src/utils/weight.ts +++ b/packages/api/src/utils/weight.ts @@ -1,4 +1,4 @@ -import type { PackItem } from '@packrat/db'; +import type { PackItem } from '@packrat/types'; import type { WeightUnit } from '@packrat/units'; import { convert, displayWeight, fromGrams, normalize, parseWeightUnit } from '@packrat/units'; diff --git a/packages/constants/package.json b/packages/constants/package.json new file mode 100644 index 0000000000..3162cad936 --- /dev/null +++ b/packages/constants/package.json @@ -0,0 +1,17 @@ +{ + "name": "@packrat/constants", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "main": "./src/index.ts", + "types": "./src/index.ts", + "scripts": { + "check-types": "tsc --noEmit" + }, + "devDependencies": { + "typescript": "catalog:" + } +} diff --git a/packages/constants/src/index.ts b/packages/constants/src/index.ts new file mode 100644 index 0000000000..42ab143c25 --- /dev/null +++ b/packages/constants/src/index.ts @@ -0,0 +1,55 @@ +export const PACK_CATEGORIES = Object.freeze([ + 'hiking', + 'backpacking', + 'camping', + 'climbing', + 'winter', + 'desert', + 'custom', + 'water sports', + 'skiing', +] as const); + +export type PackCategory = (typeof PACK_CATEGORIES)[number]; + +export const ITEM_CATEGORIES = Object.freeze([ + 'clothing', + 'shelter', + 'sleep', + 'kitchen', + 'water', + 'electronics', + 'first-aid', + 'navigation', + 'tools', + 'consumables', + 'miscellaneous', +] as const); + +export type ItemCategory = (typeof ITEM_CATEGORIES)[number]; + +export const WEIGHT_UNITS = Object.freeze(['g', 'oz', 'kg', 'lb'] as const); +export type WeightUnit = (typeof WEIGHT_UNITS)[number]; + +export const AVAILABILITY_VALUES = Object.freeze(['in_stock', 'out_of_stock', 'preorder'] as const); + +export type Availability = (typeof AVAILABILITY_VALUES)[number]; + +export interface ItemLink { + id: string; + title: string; + url: string; + type: 'official' | 'review' | 'guide' | 'purchase' | 'other'; +} + +export interface ItemReview { + id: string; + userId: string; + userName: string; + userAvatar?: string; + rating: number; + text: string; + date: string; + helpful?: number; + verified?: boolean; +} diff --git a/packages/constants/tsconfig.json b/packages/constants/tsconfig.json new file mode 100644 index 0000000000..a086b149de --- /dev/null +++ b/packages/constants/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/db/package.json b/packages/db/package.json index 1aa8ca7cc8..5485d4aed7 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -17,6 +17,7 @@ "check-types": "tsc --noEmit" }, "dependencies": { + "@packrat/constants": "workspace:*", "drizzle-orm": "catalog:", "drizzle-zod": "catalog:" }, diff --git a/packages/db/src/constants.ts b/packages/db/src/constants.ts index 42ab143c25..cf8d5c5bcf 100644 --- a/packages/db/src/constants.ts +++ b/packages/db/src/constants.ts @@ -1,55 +1 @@ -export const PACK_CATEGORIES = Object.freeze([ - 'hiking', - 'backpacking', - 'camping', - 'climbing', - 'winter', - 'desert', - 'custom', - 'water sports', - 'skiing', -] as const); - -export type PackCategory = (typeof PACK_CATEGORIES)[number]; - -export const ITEM_CATEGORIES = Object.freeze([ - 'clothing', - 'shelter', - 'sleep', - 'kitchen', - 'water', - 'electronics', - 'first-aid', - 'navigation', - 'tools', - 'consumables', - 'miscellaneous', -] as const); - -export type ItemCategory = (typeof ITEM_CATEGORIES)[number]; - -export const WEIGHT_UNITS = Object.freeze(['g', 'oz', 'kg', 'lb'] as const); -export type WeightUnit = (typeof WEIGHT_UNITS)[number]; - -export const AVAILABILITY_VALUES = Object.freeze(['in_stock', 'out_of_stock', 'preorder'] as const); - -export type Availability = (typeof AVAILABILITY_VALUES)[number]; - -export interface ItemLink { - id: string; - title: string; - url: string; - type: 'official' | 'review' | 'guide' | 'purchase' | 'other'; -} - -export interface ItemReview { - id: string; - userId: string; - userName: string; - userAvatar?: string; - rating: number; - text: string; - date: string; - helpful?: number; - verified?: boolean; -} +export * from '@packrat/constants'; diff --git a/packages/schemas/package.json b/packages/schemas/package.json index fe5c2caca7..defcd15ee1 100644 --- a/packages/schemas/package.json +++ b/packages/schemas/package.json @@ -17,6 +17,7 @@ "check-types": "tsc --noEmit" }, "dependencies": { + "@packrat/constants": "workspace:*", "@packrat/db": "workspace:*", "@packrat/guards": "workspace:*", "zod": "catalog:" diff --git a/packages/schemas/src/constants.ts b/packages/schemas/src/constants.ts index 5c2be2a3a1..d90093f6a3 100644 --- a/packages/schemas/src/constants.ts +++ b/packages/schemas/src/constants.ts @@ -11,14 +11,14 @@ export { type PackCategory, WEIGHT_UNITS, type WeightUnit, -} from '@packrat/db/constants'; +} from '@packrat/constants'; import { AVAILABILITY_VALUES, ITEM_CATEGORIES, PACK_CATEGORIES, WEIGHT_UNITS, -} from '@packrat/db/constants'; +} from '@packrat/constants'; export const PackCategorySchema = z.enum(PACK_CATEGORIES); export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 0000000000..f59daf7bd1 --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,20 @@ +{ + "name": "@packrat/types", + "version": "0.0.0", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "main": "./src/index.ts", + "types": "./src/index.ts", + "scripts": { + "check-types": "tsc --noEmit" + }, + "dependencies": { + "@packrat/db": "workspace:*" + }, + "devDependencies": { + "typescript": "catalog:" + } +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts new file mode 100644 index 0000000000..b91301d644 --- /dev/null +++ b/packages/types/src/index.ts @@ -0,0 +1,52 @@ +// Model types from @packrat/db + +// Constant types from @packrat/constants +export type { + Availability, + ItemCategory, + ItemLink, + ItemReview, + PackCategory, + WeightUnit, +} from '@packrat/constants'; +export type { + Account, + CatalogItem, + CommentLike, + ETLJob, + InvalidItemLog, + Jwks, + NewAccount, + NewCatalogItem, + NewCommentLike, + NewETLJob, + NewInvalidItemLog, + NewJwks, + NewPack, + NewPackItem, + NewPackTemplate, + NewPackTemplateItem, + NewPost, + NewPostComment, + NewPostLike, + NewReportedContent, + NewSession, + NewTrip, + NewUser, + NewVerification, + Pack, + PackItem, + PackTemplate, + PackTemplateItem, + PackTemplateWithItems, + PackWithItems, + Post, + PostComment, + PostLike, + ReportedContent, + Session, + TrailConditionReport, + Trip, + User, + Verification, +} from '@packrat/db'; diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json new file mode 100644 index 0000000000..a086b149de --- /dev/null +++ b/packages/types/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/units/package.json b/packages/units/package.json index d2436dac73..c5f3d9582c 100644 --- a/packages/units/package.json +++ b/packages/units/package.json @@ -13,6 +13,7 @@ "test:coverage": "vitest run --coverage" }, "dependencies": { + "@packrat/constants": "workspace:*", "@packrat/guards": "workspace:*" }, "devDependencies": { diff --git a/packages/units/src/index.ts b/packages/units/src/index.ts index 1a21806cde..be40406b3b 100644 --- a/packages/units/src/index.ts +++ b/packages/units/src/index.ts @@ -1,5 +1,8 @@ +import { WEIGHT_UNITS, type WeightUnit } from '@packrat/constants'; import { isString } from '@packrat/guards'; +export { WEIGHT_UNITS, type WeightUnit }; + // Exact avoirdupois values per NIST. These constants are the single source of // truth for all weight math in the monorepo — do not inline elsewhere. const TO_GRAMS = { @@ -9,10 +12,6 @@ const TO_GRAMS = { lb: 453.59237, } as const; -export const WEIGHT_UNITS = Object.freeze(['g', 'oz', 'kg', 'lb'] as const); - -export type WeightUnit = keyof typeof TO_GRAMS; - /** * Normalize a weight value to grams. * Use this before summing items with mixed units. diff --git a/tsconfig.json b/tsconfig.json index 7ba8032e18..97dc7bd226 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -45,10 +45,14 @@ "@packrat/web-ui": ["./packages/web-ui/src"], "@packrat/web-ui/*": ["./packages/web-ui/src/*"], "nativewindui/*": ["./apps/expo/components/ui/*"], + "@packrat/constants": ["./packages/constants/src/index.ts"], + "@packrat/constants/*": ["./packages/constants/src/*"], "@packrat/db": ["./packages/db/src/index.ts"], "@packrat/db/*": ["./packages/db/src/*"], "@packrat/schemas": ["./packages/schemas/src/index.ts"], - "@packrat/schemas/*": ["./packages/schemas/src/*"] + "@packrat/schemas/*": ["./packages/schemas/src/*"], + "@packrat/types": ["./packages/types/src/index.ts"], + "@packrat/types/*": ["./packages/types/src/*"] } }, "include": [ From 0154b8711999abd274ae673cd08dcc99f89c94d8 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 11:30:53 -0600 Subject: [PATCH 37/51] refactor: delete all backward-compat re-export shims MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove 21 shim files that existed solely to forward imports to their new canonical homes after the @packrat/db and @packrat/schemas extraction: - packages/db/src/constants.ts (→ @packrat/constants) - packages/api/src/db/schema.ts (→ @packrat/db/schema) - packages/api/src/db/zod-schemas.ts (→ @packrat/db/zod-schemas) - packages/api/src/schemas/{ai,auth,catalog,chat,feed,guides, imageDetection,packTemplates,packs,seasonSuggestions, trailConditions,trips,upload,users,weather}.ts (→ @packrat/schemas/*) - packages/api/src/types/{validation,constants,index}.ts All consumers updated to import directly from canonical sources: @packrat/constants, @packrat/db, @packrat/schemas, @packrat/types. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../screens/TripWeatherDetailsScreen.tsx | 2 +- packages/api/src/auth/auth.config.ts | 2 +- packages/api/src/auth/index.ts | 2 +- packages/api/src/db/index.ts | 2 +- packages/api/src/db/schema.ts | 1 - packages/api/src/db/seed-e2e-user.ts | 2 +- packages/api/src/db/seed.ts | 2 +- packages/api/src/db/zod-schemas.ts | 1 - .../api/src/routes/admin/analytics/catalog.ts | 2 +- .../src/routes/admin/analytics/platform.ts | 9 +------- packages/api/src/routes/admin/index.ts | 2 +- packages/api/src/routes/admin/trails.ts | 2 +- packages/api/src/routes/ai/index.ts | 2 +- packages/api/src/routes/catalog/index.ts | 18 +++++++-------- packages/api/src/routes/chat.ts | 10 ++++----- packages/api/src/routes/feed/index.ts | 4 ++-- packages/api/src/routes/guides/index.ts | 8 +++---- .../api/src/routes/packTemplates/index.ts | 10 ++++----- packages/api/src/routes/packs/index.ts | 22 +++++++++---------- packages/api/src/routes/seasonSuggestions.ts | 4 ++-- .../api/src/routes/trailConditions/reports.ts | 4 ++-- packages/api/src/routes/trips/index.ts | 4 ++-- packages/api/src/routes/upload.ts | 5 +---- packages/api/src/routes/user/index.ts | 4 ++-- packages/api/src/routes/weather.ts | 6 ++--- packages/api/src/schemas/ai.ts | 1 - packages/api/src/schemas/auth.ts | 1 - packages/api/src/schemas/catalog.ts | 1 - packages/api/src/schemas/chat.ts | 1 - packages/api/src/schemas/feed.ts | 1 - packages/api/src/schemas/guides.ts | 1 - packages/api/src/schemas/imageDetection.ts | 1 - packages/api/src/schemas/packTemplates.ts | 1 - packages/api/src/schemas/packs.ts | 1 - packages/api/src/schemas/seasonSuggestions.ts | 1 - packages/api/src/schemas/trailConditions.ts | 1 - packages/api/src/schemas/trips.ts | 1 - packages/api/src/schemas/upload.ts | 1 - packages/api/src/schemas/users.ts | 1 - packages/api/src/schemas/weather.ts | 1 - packages/api/src/services/catalogService.ts | 8 +++---- .../src/services/etl/CatalogItemValidator.ts | 2 +- .../api/src/services/etl/mergeItemsBySku.ts | 2 +- .../api/src/services/etl/processCatalogEtl.ts | 2 +- .../api/src/services/etl/processLogsBatch.ts | 2 +- .../services/etl/processValidItemsBatch.ts | 2 +- .../src/services/etl/updateEtlJobProgress.ts | 2 +- .../api/src/services/imageDetectionService.ts | 2 +- packages/api/src/services/packItemService.ts | 2 +- packages/api/src/services/packService.ts | 10 ++------- packages/api/src/services/userService.ts | 2 +- packages/api/src/types/constants.ts | 2 -- packages/api/src/types/etl.ts | 6 ++--- packages/api/src/types/index.ts | 1 - packages/api/src/types/validation.ts | 1 - packages/api/src/utils/DbUtils.ts | 2 +- .../src/utils/__tests__/compute-pack.test.ts | 2 +- packages/api/src/utils/compute-pack.ts | 2 +- packages/api/src/utils/csv-utils.ts | 4 ++-- packages/api/src/utils/embeddingHelper.ts | 2 +- packages/api/test/auth.test.ts | 2 +- packages/api/test/db-test-helper.ts | 2 +- packages/api/test/etl.test.ts | 2 +- .../api/test/fixtures/catalog-fixtures.ts | 2 +- packages/api/test/fixtures/pack-fixtures.ts | 2 +- .../test/fixtures/pack-template-fixtures.ts | 2 +- packages/api/test/packs.test.ts | 2 +- packages/api/test/setup.ts | 2 +- packages/api/test/utils/db-helpers.ts | 9 ++++---- packages/api/test/utils/user-helpers.ts | 2 +- packages/db/src/constants.ts | 1 - packages/db/src/index.ts | 1 - packages/db/src/schema.ts | 2 +- 73 files changed, 97 insertions(+), 137 deletions(-) delete mode 100644 packages/api/src/db/schema.ts delete mode 100644 packages/api/src/db/zod-schemas.ts delete mode 100644 packages/api/src/schemas/ai.ts delete mode 100644 packages/api/src/schemas/auth.ts delete mode 100644 packages/api/src/schemas/catalog.ts delete mode 100644 packages/api/src/schemas/chat.ts delete mode 100644 packages/api/src/schemas/feed.ts delete mode 100644 packages/api/src/schemas/guides.ts delete mode 100644 packages/api/src/schemas/imageDetection.ts delete mode 100644 packages/api/src/schemas/packTemplates.ts delete mode 100644 packages/api/src/schemas/packs.ts delete mode 100644 packages/api/src/schemas/seasonSuggestions.ts delete mode 100644 packages/api/src/schemas/trailConditions.ts delete mode 100644 packages/api/src/schemas/trips.ts delete mode 100644 packages/api/src/schemas/upload.ts delete mode 100644 packages/api/src/schemas/users.ts delete mode 100644 packages/api/src/schemas/weather.ts delete mode 100644 packages/api/src/types/constants.ts delete mode 100644 packages/api/src/types/index.ts delete mode 100644 packages/api/src/types/validation.ts delete mode 100644 packages/db/src/constants.ts diff --git a/apps/expo/features/trips/screens/TripWeatherDetailsScreen.tsx b/apps/expo/features/trips/screens/TripWeatherDetailsScreen.tsx index bddef8c413..7fa5b63ef7 100644 --- a/apps/expo/features/trips/screens/TripWeatherDetailsScreen.tsx +++ b/apps/expo/features/trips/screens/TripWeatherDetailsScreen.tsx @@ -1,4 +1,4 @@ -import type { WeatherAPIForecastResponse } from '@packrat/api/schemas/weather'; +import type { WeatherAPIForecastResponse } from '@packrat/schemas/weather'; import { Icon } from 'expo-app/components/Icon'; import { WeatherForecast } from 'expo-app/features/weather/components/WeatherForecast'; import { diff --git a/packages/api/src/auth/auth.config.ts b/packages/api/src/auth/auth.config.ts index e8f21594b3..d120de462e 100644 --- a/packages/api/src/auth/auth.config.ts +++ b/packages/api/src/auth/auth.config.ts @@ -12,7 +12,7 @@ import { drizzleAdapter } from '@better-auth/drizzle-adapter'; import { neon } from '@neondatabase/serverless'; -import * as schema from '@packrat/api/db/schema'; +import * as schema from '@packrat/db'; import { betterAuth } from 'better-auth'; import { admin, bearer, jwt } from 'better-auth/plugins'; import { drizzle } from 'drizzle-orm/neon-http'; diff --git a/packages/api/src/auth/index.ts b/packages/api/src/auth/index.ts index 7c488119c4..8cd0933532 100644 --- a/packages/api/src/auth/index.ts +++ b/packages/api/src/auth/index.ts @@ -11,8 +11,8 @@ import { drizzleAdapter } from '@better-auth/drizzle-adapter'; import { expo } from '@better-auth/expo'; import { verifyPassword } from '@better-auth/utils/password'; import { neon } from '@neondatabase/serverless'; -import * as schema from '@packrat/api/db/schema'; import type { ValidatedEnv } from '@packrat/api/utils/env-validation'; +import * as schema from '@packrat/db'; import * as bcrypt from 'bcryptjs'; import { betterAuth } from 'better-auth'; import { admin, bearer, jwt } from 'better-auth/plugins'; diff --git a/packages/api/src/db/index.ts b/packages/api/src/db/index.ts index 28c5f0ba8b..4047afb8ec 100644 --- a/packages/api/src/db/index.ts +++ b/packages/api/src/db/index.ts @@ -1,7 +1,7 @@ import { Pool as NeonPool, neon } from '@neondatabase/serverless'; -import * as schema from '@packrat/api/db/schema'; import type { ValidatedEnv } from '@packrat/api/utils/env-validation'; import { getEnv } from '@packrat/api/utils/env-validation'; +import * as schema from '@packrat/db/schema'; import { drizzle } from 'drizzle-orm/neon-http'; import { drizzle as drizzleServerless } from 'drizzle-orm/neon-serverless'; import { drizzle as drizzlePg } from 'drizzle-orm/node-postgres'; diff --git a/packages/api/src/db/schema.ts b/packages/api/src/db/schema.ts deleted file mode 100644 index 48af2ccbc6..0000000000 --- a/packages/api/src/db/schema.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/db/schema'; diff --git a/packages/api/src/db/seed-e2e-user.ts b/packages/api/src/db/seed-e2e-user.ts index 0a7a23dcbe..f6e0143d91 100644 --- a/packages/api/src/db/seed-e2e-user.ts +++ b/packages/api/src/db/seed-e2e-user.ts @@ -10,6 +10,7 @@ */ import { neon, neonConfig } from '@neondatabase/serverless'; +import * as schema from '@packrat/db/schema'; import { nodeEnv } from '@packrat/env/node'; import { eq } from 'drizzle-orm'; import { drizzle, type NeonHttpDatabase } from 'drizzle-orm/neon-http'; @@ -17,7 +18,6 @@ import { drizzle as drizzlePg, type NodePgDatabase } from 'drizzle-orm/node-post import { Client } from 'pg'; import WebSocket from 'ws'; import { hashPassword } from '../utils/auth'; -import * as schema from './schema'; neonConfig.webSocketConstructor = WebSocket; diff --git a/packages/api/src/db/seed.ts b/packages/api/src/db/seed.ts index 2efae017c0..feed0a1b45 100644 --- a/packages/api/src/db/seed.ts +++ b/packages/api/src/db/seed.ts @@ -17,13 +17,13 @@ */ import { neon, neonConfig } from '@neondatabase/serverless'; +import * as schema from '@packrat/db/schema'; import { nodeEnv } from '@packrat/env/node'; import { and, eq } from 'drizzle-orm'; import { drizzle, type NeonHttpDatabase } from 'drizzle-orm/neon-http'; import { drizzle as drizzlePg, type NodePgDatabase } from 'drizzle-orm/node-postgres'; import { Client } from 'pg'; import WebSocket from 'ws'; -import * as schema from './schema'; neonConfig.webSocketConstructor = WebSocket; diff --git a/packages/api/src/db/zod-schemas.ts b/packages/api/src/db/zod-schemas.ts deleted file mode 100644 index a49150789f..0000000000 --- a/packages/api/src/db/zod-schemas.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/db/zod-schemas'; diff --git a/packages/api/src/routes/admin/analytics/catalog.ts b/packages/api/src/routes/admin/analytics/catalog.ts index 88b9151970..b0d8d0e3ce 100644 --- a/packages/api/src/routes/admin/analytics/catalog.ts +++ b/packages/api/src/routes/admin/analytics/catalog.ts @@ -1,5 +1,4 @@ import { createDb } from '@packrat/api/db'; -import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/api/db/schema'; import { AdminErrorResponses, BrandRowSchema, @@ -13,6 +12,7 @@ import { } from '@packrat/api/schemas/admin'; import { queueCatalogETL } from '@packrat/api/services/etl/queue'; import { getEnv } from '@packrat/api/utils/env-validation'; +import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/db'; import { and, avg, count, desc, eq, gt, isNotNull, lt, max, min, sql } from 'drizzle-orm'; import { Elysia, status, t } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/routes/admin/analytics/platform.ts b/packages/api/src/routes/admin/analytics/platform.ts index 4b3f35bae1..ac193307e5 100644 --- a/packages/api/src/routes/admin/analytics/platform.ts +++ b/packages/api/src/routes/admin/analytics/platform.ts @@ -1,12 +1,4 @@ import { createDb } from '@packrat/api/db'; -import { - catalogItems, - packs, - posts, - trailConditionReports, - trips, - users, -} from '@packrat/api/db/schema'; import { ActiveUsersSchema, ActivityPointSchema, @@ -14,6 +6,7 @@ import { BreakdownItemSchema, GrowthPointSchema, } from '@packrat/api/schemas/admin'; +import { catalogItems, packs, posts, trailConditionReports, trips, users } from '@packrat/db'; import { and, count, desc, eq, gte, sql } from 'drizzle-orm'; import { Elysia, status, t } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/routes/admin/index.ts b/packages/api/src/routes/admin/index.ts index 0ab63aeac9..d8e3a6fc93 100644 --- a/packages/api/src/routes/admin/index.ts +++ b/packages/api/src/routes/admin/index.ts @@ -1,6 +1,5 @@ import { cors } from '@elysiajs/cors'; import { createDb } from '@packrat/api/db'; -import { catalogItems, packs, users } from '@packrat/api/db/schema'; import { verifyCFAccessRequest } from '@packrat/api/middleware/cfAccess'; import { AdminCatalogListSchema, @@ -14,6 +13,7 @@ import { } from '@packrat/api/schemas/admin'; import { timingSafeEqual } from '@packrat/api/utils/auth'; import { getEnv } from '@packrat/api/utils/env-validation'; +import { catalogItems, packs, users } from '@packrat/db'; import { assertAllDefined } from '@packrat/guards'; import { and, count, desc, eq, ilike, or } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; diff --git a/packages/api/src/routes/admin/trails.ts b/packages/api/src/routes/admin/trails.ts index c04c388f75..b78b03b9f4 100644 --- a/packages/api/src/routes/admin/trails.ts +++ b/packages/api/src/routes/admin/trails.ts @@ -1,5 +1,4 @@ import { createDb, createOsmDb } from '@packrat/api/db'; -import { trailConditionReports, users } from '@packrat/api/db/schema'; import { AdminErrorResponses, SuccessSchema, @@ -8,6 +7,7 @@ import { TrailSearchItemSchema, TrailSearchResultSchema, } from '@packrat/api/schemas/admin'; +import { trailConditionReports, users } from '@packrat/db'; import { and, count, desc, eq, ilike, or, sql } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/routes/ai/index.ts b/packages/api/src/routes/ai/index.ts index e20013328c..80a72a7ea1 100644 --- a/packages/api/src/routes/ai/index.ts +++ b/packages/api/src/routes/ai/index.ts @@ -1,9 +1,9 @@ import { authPlugin } from '@packrat/api/middleware/auth'; -import { RagSearchQuerySchema, WebSearchQuerySchema } from '@packrat/api/schemas/ai'; import { AIService } from '@packrat/api/services/aiService'; import { executeSqlAiTool } from '@packrat/api/services/executeSqlAiTool'; import { getSchemaInfo } from '@packrat/api/utils/DbUtils'; import { isString } from '@packrat/guards'; +import { RagSearchQuerySchema, WebSearchQuerySchema } from '@packrat/schemas/ai'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/routes/catalog/index.ts b/packages/api/src/routes/catalog/index.ts index a6f5eb9549..a67ce32b45 100644 --- a/packages/api/src/routes/catalog/index.ts +++ b/packages/api/src/routes/catalog/index.ts @@ -1,6 +1,13 @@ import { createDb } from '@packrat/api/db'; -import { catalogItems, etlJobs, packItems } from '@packrat/api/db/schema'; import { apiKeyAuthPlugin, authPlugin } from '@packrat/api/middleware/auth'; +import { CatalogService } from '@packrat/api/services'; +import { generateEmbedding } from '@packrat/api/services/embeddingService'; +import { queueCatalogETL } from '@packrat/api/services/etl/queue'; +import { R2BucketService } from '@packrat/api/services/r2-bucket'; +import { getEmbeddingText } from '@packrat/api/utils/embeddingHelper'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { catalogItems, etlJobs, packItems } from '@packrat/db'; +import { isString } from '@packrat/guards'; import { CatalogCategoriesResponseSchema, CatalogItemSchema, @@ -9,14 +16,7 @@ import { CreateCatalogItemRequestSchema, UpdateCatalogItemRequestSchema, VectorSearchQuerySchema, -} from '@packrat/api/schemas/catalog'; -import { CatalogService } from '@packrat/api/services'; -import { generateEmbedding } from '@packrat/api/services/embeddingService'; -import { queueCatalogETL } from '@packrat/api/services/etl/queue'; -import { R2BucketService } from '@packrat/api/services/r2-bucket'; -import { getEmbeddingText } from '@packrat/api/utils/embeddingHelper'; -import { getEnv } from '@packrat/api/utils/env-validation'; -import { isString } from '@packrat/guards'; +} from '@packrat/schemas/catalog'; import { and, cosineDistance, diff --git a/packages/api/src/routes/chat.ts b/packages/api/src/routes/chat.ts index 7114373d2d..2c3a8875cc 100644 --- a/packages/api/src/routes/chat.ts +++ b/packages/api/src/routes/chat.ts @@ -1,14 +1,14 @@ import { createDb } from '@packrat/api/db'; -import { reportedContent } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; +import { createAIProvider } from '@packrat/api/utils/ai/provider'; +import { createTools } from '@packrat/api/utils/ai/tools'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { reportedContent } from '@packrat/db'; import { ChatRequestSchema, CreateReportRequestSchema, UpdateReportStatusRequestSchema, -} from '@packrat/api/schemas/chat'; -import { createAIProvider } from '@packrat/api/utils/ai/provider'; -import { createTools } from '@packrat/api/utils/ai/tools'; -import { getEnv } from '@packrat/api/utils/env-validation'; +} from '@packrat/schemas/chat'; import { convertToModelMessages, stepCountIs, streamText, type UIMessage } from 'ai'; import { eq } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; diff --git a/packages/api/src/routes/feed/index.ts b/packages/api/src/routes/feed/index.ts index d232171ca9..2400e628c2 100644 --- a/packages/api/src/routes/feed/index.ts +++ b/packages/api/src/routes/feed/index.ts @@ -1,11 +1,11 @@ import { createDb } from '@packrat/api/db'; -import { commentLikes, postComments, postLikes, posts, users } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; +import { commentLikes, postComments, postLikes, posts, users } from '@packrat/db'; import { CreateCommentRequestSchema, CreatePostRequestSchema, FeedResponseSchema, -} from '@packrat/api/schemas/feed'; +} from '@packrat/schemas/feed'; import { and, count, desc, eq, inArray } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/routes/guides/index.ts b/packages/api/src/routes/guides/index.ts index 8910ae466d..9770920f12 100644 --- a/packages/api/src/routes/guides/index.ts +++ b/packages/api/src/routes/guides/index.ts @@ -1,4 +1,7 @@ import { authPlugin } from '@packrat/api/middleware/auth'; +import { R2BucketService } from '@packrat/api/services/r2-bucket'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { asNumber, asString, isArray } from '@packrat/guards'; import { GuideCategoriesResponseSchema, GuideDetailSchema, @@ -6,10 +9,7 @@ import { GuideSearchResponseSchema, GuidesQuerySchema, GuidesResponseSchema, -} from '@packrat/api/schemas/guides'; -import { R2BucketService } from '@packrat/api/services/r2-bucket'; -import { getEnv } from '@packrat/api/utils/env-validation'; -import { asNumber, asString, isArray } from '@packrat/guards'; +} from '@packrat/schemas/guides'; const MDX_EXT_RE = /\.(mdx?|md)$/; const DASH_RE = /-/g; diff --git a/packages/api/src/routes/packTemplates/index.ts b/packages/api/src/routes/packTemplates/index.ts index a9e1dae0a8..2c0cf039e5 100644 --- a/packages/api/src/routes/packTemplates/index.ts +++ b/packages/api/src/routes/packTemplates/index.ts @@ -1,18 +1,18 @@ import { createGoogleGenerativeAI } from '@ai-sdk/google'; import { getContainer } from '@cloudflare/containers'; import { createDb } from '@packrat/api/db'; -import { type PackTemplate, packTemplateItems, packTemplates } from '@packrat/api/db/schema'; import { adminAuthPlugin, authPlugin } from '@packrat/api/middleware/auth'; +import { CatalogService } from '@packrat/api/services/catalogService'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { type PackTemplate, packTemplateItems, packTemplates } from '@packrat/db'; +import { assertDefined } from '@packrat/guards'; import { CreatePackTemplateItemRequestSchema, CreatePackTemplateRequestSchema, GenerateFromOnlineContentRequestSchema, UpdatePackTemplateItemRequestSchema, UpdatePackTemplateRequestSchema, -} from '@packrat/api/schemas/packTemplates'; -import { CatalogService } from '@packrat/api/services/catalogService'; -import { getEnv } from '@packrat/api/utils/env-validation'; -import { assertDefined } from '@packrat/guards'; +} from '@packrat/schemas/packTemplates'; import { generateObject } from 'ai'; import { and, eq, or, sql } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; diff --git a/packages/api/src/routes/packs/index.ts b/packages/api/src/routes/packs/index.ts index 9f75128a06..8425e453a8 100644 --- a/packages/api/src/routes/packs/index.ts +++ b/packages/api/src/routes/packs/index.ts @@ -1,5 +1,13 @@ import { GetObjectCommand } from '@aws-sdk/client-s3'; import { createDb } from '@packrat/api/db'; +import { adminAuthPlugin, authPlugin } from '@packrat/api/middleware/auth'; +import { ImageDetectionService, PackService } from '@packrat/api/services'; +import { generateEmbedding } from '@packrat/api/services/embeddingService'; +import { computePacksWeights, computePackWeights } from '@packrat/api/utils/compute-pack'; +import { getPackDetails } from '@packrat/api/utils/DbUtils'; +import { getEmbeddingText } from '@packrat/api/utils/embeddingHelper'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { getPresignedUrl } from '@packrat/api/utils/getPresignedUrl'; import { catalogItems, type NewPack, @@ -8,9 +16,8 @@ import { packItems, packs, packWeightHistory, -} from '@packrat/api/db/schema'; -import { adminAuthPlugin, authPlugin } from '@packrat/api/middleware/auth'; -import { AnalyzeImageRequestSchema } from '@packrat/api/schemas/imageDetection'; +} from '@packrat/db'; +import { AnalyzeImageRequestSchema } from '@packrat/schemas/imageDetection'; import { CreatePackItemRequestSchema, CreatePackRequestSchema, @@ -19,14 +26,7 @@ import { PackWithWeightsSchema, UpdatePackItemRequestSchema, UpdatePackRequestSchema, -} from '@packrat/api/schemas/packs'; -import { ImageDetectionService, PackService } from '@packrat/api/services'; -import { generateEmbedding } from '@packrat/api/services/embeddingService'; -import { computePacksWeights, computePackWeights } from '@packrat/api/utils/compute-pack'; -import { getPackDetails } from '@packrat/api/utils/DbUtils'; -import { getEmbeddingText } from '@packrat/api/utils/embeddingHelper'; -import { getEnv } from '@packrat/api/utils/env-validation'; -import { getPresignedUrl } from '@packrat/api/utils/getPresignedUrl'; +} from '@packrat/schemas/packs'; import { and, cosineDistance, diff --git a/packages/api/src/routes/seasonSuggestions.ts b/packages/api/src/routes/seasonSuggestions.ts index a74c784665..8599095d30 100644 --- a/packages/api/src/routes/seasonSuggestions.ts +++ b/packages/api/src/routes/seasonSuggestions.ts @@ -1,9 +1,9 @@ import { createOpenAI } from '@ai-sdk/openai'; import { createDb } from '@packrat/api/db'; -import { type PackItem, packItems } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; -import { SeasonSuggestionsRequestSchema } from '@packrat/api/schemas/seasonSuggestions'; import { getEnv } from '@packrat/api/utils/env-validation'; +import { type PackItem, packItems } from '@packrat/db'; +import { SeasonSuggestionsRequestSchema } from '@packrat/schemas/seasonSuggestions'; import { generateObject } from 'ai'; import { and, eq } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; diff --git a/packages/api/src/routes/trailConditions/reports.ts b/packages/api/src/routes/trailConditions/reports.ts index 47b854582c..58dfc683d9 100644 --- a/packages/api/src/routes/trailConditions/reports.ts +++ b/packages/api/src/routes/trailConditions/reports.ts @@ -1,7 +1,7 @@ import { createDb } from '@packrat/api/db'; -import type { NewTrailConditionReport } from '@packrat/api/db/schema'; -import { trailConditionReports } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; +import type { NewTrailConditionReport } from '@packrat/db'; +import { trailConditionReports } from '@packrat/db'; import { and, desc, eq, gte, ilike, type SQL } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/routes/trips/index.ts b/packages/api/src/routes/trips/index.ts index 17fe24bd21..c93d9c996d 100644 --- a/packages/api/src/routes/trips/index.ts +++ b/packages/api/src/routes/trips/index.ts @@ -1,7 +1,7 @@ import { createDb } from '@packrat/api/db'; -import { trips } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; -import { CreateTripBodySchema, TripSchema, UpdateTripBodySchema } from '@packrat/api/schemas/trips'; +import { trips } from '@packrat/db'; +import { CreateTripBodySchema, TripSchema, UpdateTripBodySchema } from '@packrat/schemas/trips'; import { and, eq } from 'drizzle-orm'; import { Elysia, NotFoundError, status } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/routes/upload.ts b/packages/api/src/routes/upload.ts index ea95e15ced..9129504b60 100644 --- a/packages/api/src/routes/upload.ts +++ b/packages/api/src/routes/upload.ts @@ -1,11 +1,8 @@ import { PutObjectCommand } from '@aws-sdk/client-s3'; import { authPlugin } from '@packrat/api/middleware/auth'; -import { - PresignedUploadQuerySchema, - PresignedUploadResponseSchema, -} from '@packrat/api/schemas/upload'; import { getEnv } from '@packrat/api/utils/env-validation'; import { getPresignedUrl } from '@packrat/api/utils/getPresignedUrl'; +import { PresignedUploadQuerySchema, PresignedUploadResponseSchema } from '@packrat/schemas/upload'; import { Elysia, status } from 'elysia'; const ALLOWED_IMAGE_TYPES = [ diff --git a/packages/api/src/routes/user/index.ts b/packages/api/src/routes/user/index.ts index 50b778e5f3..9189709139 100644 --- a/packages/api/src/routes/user/index.ts +++ b/packages/api/src/routes/user/index.ts @@ -1,11 +1,11 @@ import { createDb } from '@packrat/api/db'; -import { users } from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; +import { users } from '@packrat/db'; import { UpdateUserRequestSchema, UpdateUserResponseSchema, UserProfileSchema, -} from '@packrat/api/schemas/users'; +} from '@packrat/schemas/users'; import { eq } from 'drizzle-orm'; import { Elysia, NotFoundError, status } from 'elysia'; diff --git a/packages/api/src/routes/weather.ts b/packages/api/src/routes/weather.ts index c6bbd628ed..7752d1a9bd 100644 --- a/packages/api/src/routes/weather.ts +++ b/packages/api/src/routes/weather.ts @@ -1,4 +1,6 @@ import { authPlugin } from '@packrat/api/middleware/auth'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { isString } from '@packrat/guards'; import { type WeatherAPICurrentResponse, type WeatherAPIForecastResponse, @@ -7,9 +9,7 @@ import { WeatherCoordinateQuerySchema, WeatherLocationIdSchema, WeatherSearchQuerySchema, -} from '@packrat/api/schemas/weather'; -import { getEnv } from '@packrat/api/utils/env-validation'; -import { isString } from '@packrat/guards'; +} from '@packrat/schemas/weather'; import { Elysia, status } from 'elysia'; const WEATHER_API_BASE_URL = 'https://api.weatherapi.com/v1'; diff --git a/packages/api/src/schemas/ai.ts b/packages/api/src/schemas/ai.ts deleted file mode 100644 index 713838febe..0000000000 --- a/packages/api/src/schemas/ai.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/ai'; diff --git a/packages/api/src/schemas/auth.ts b/packages/api/src/schemas/auth.ts deleted file mode 100644 index a270614446..0000000000 --- a/packages/api/src/schemas/auth.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/auth'; diff --git a/packages/api/src/schemas/catalog.ts b/packages/api/src/schemas/catalog.ts deleted file mode 100644 index 6d7c49a83a..0000000000 --- a/packages/api/src/schemas/catalog.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/catalog'; diff --git a/packages/api/src/schemas/chat.ts b/packages/api/src/schemas/chat.ts deleted file mode 100644 index 1cfa4ba65c..0000000000 --- a/packages/api/src/schemas/chat.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/chat'; diff --git a/packages/api/src/schemas/feed.ts b/packages/api/src/schemas/feed.ts deleted file mode 100644 index f2d91373fe..0000000000 --- a/packages/api/src/schemas/feed.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/feed'; diff --git a/packages/api/src/schemas/guides.ts b/packages/api/src/schemas/guides.ts deleted file mode 100644 index de9168b02d..0000000000 --- a/packages/api/src/schemas/guides.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/guides'; diff --git a/packages/api/src/schemas/imageDetection.ts b/packages/api/src/schemas/imageDetection.ts deleted file mode 100644 index edbb760fb8..0000000000 --- a/packages/api/src/schemas/imageDetection.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/imageDetection'; diff --git a/packages/api/src/schemas/packTemplates.ts b/packages/api/src/schemas/packTemplates.ts deleted file mode 100644 index 9fe338b3a9..0000000000 --- a/packages/api/src/schemas/packTemplates.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/packTemplates'; diff --git a/packages/api/src/schemas/packs.ts b/packages/api/src/schemas/packs.ts deleted file mode 100644 index 5a0e2f87f9..0000000000 --- a/packages/api/src/schemas/packs.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/packs'; diff --git a/packages/api/src/schemas/seasonSuggestions.ts b/packages/api/src/schemas/seasonSuggestions.ts deleted file mode 100644 index 5ab283b2dc..0000000000 --- a/packages/api/src/schemas/seasonSuggestions.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/seasonSuggestions'; diff --git a/packages/api/src/schemas/trailConditions.ts b/packages/api/src/schemas/trailConditions.ts deleted file mode 100644 index 002ae17c8c..0000000000 --- a/packages/api/src/schemas/trailConditions.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/trailConditions'; diff --git a/packages/api/src/schemas/trips.ts b/packages/api/src/schemas/trips.ts deleted file mode 100644 index 56fc66d575..0000000000 --- a/packages/api/src/schemas/trips.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/trips'; diff --git a/packages/api/src/schemas/upload.ts b/packages/api/src/schemas/upload.ts deleted file mode 100644 index fd8925518a..0000000000 --- a/packages/api/src/schemas/upload.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/upload'; diff --git a/packages/api/src/schemas/users.ts b/packages/api/src/schemas/users.ts deleted file mode 100644 index 2b6a6d1d6b..0000000000 --- a/packages/api/src/schemas/users.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/users'; diff --git a/packages/api/src/schemas/weather.ts b/packages/api/src/schemas/weather.ts deleted file mode 100644 index 661cd72efd..0000000000 --- a/packages/api/src/schemas/weather.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/weather'; diff --git a/packages/api/src/services/catalogService.ts b/packages/api/src/services/catalogService.ts index 5fb5d15b13..6fe722ab70 100644 --- a/packages/api/src/services/catalogService.ts +++ b/packages/api/src/services/catalogService.ts @@ -1,13 +1,13 @@ import { createDb, createDbClient } from '@packrat/api/db'; +import { generateEmbedding, generateManyEmbeddings } from '@packrat/api/services/embeddingService'; +import type { Env } from '@packrat/api/types/env'; +import { getEnv } from '@packrat/api/utils/env-validation'; import { type CatalogItem, catalogItemEtlJobs, catalogItems, type NewCatalogItem, -} from '@packrat/api/db/schema'; -import { generateEmbedding, generateManyEmbeddings } from '@packrat/api/services/embeddingService'; -import type { Env } from '@packrat/api/types/env'; -import { getEnv } from '@packrat/api/utils/env-validation'; +} from '@packrat/db'; import { and, asc, diff --git a/packages/api/src/services/etl/CatalogItemValidator.ts b/packages/api/src/services/etl/CatalogItemValidator.ts index 6788ba475d..eba2a49ea8 100644 --- a/packages/api/src/services/etl/CatalogItemValidator.ts +++ b/packages/api/src/services/etl/CatalogItemValidator.ts @@ -1,5 +1,5 @@ -import type { NewCatalogItem } from '@packrat/api/db/schema'; import type { ValidatedCatalogItem, ValidationError } from '@packrat/api/types/etl'; +import type { NewCatalogItem } from '@packrat/db'; import { isNumber, isString } from '@packrat/guards'; export class CatalogItemValidator { diff --git a/packages/api/src/services/etl/mergeItemsBySku.ts b/packages/api/src/services/etl/mergeItemsBySku.ts index acdc8875aa..21642f3eff 100644 --- a/packages/api/src/services/etl/mergeItemsBySku.ts +++ b/packages/api/src/services/etl/mergeItemsBySku.ts @@ -1,4 +1,4 @@ -import type { NewCatalogItem } from '@packrat/api/db/schema'; +import type { NewCatalogItem } from '@packrat/db'; /** * Merges all occurrences of each SKU into a single item diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 05420aaede..2ada569d30 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -1,7 +1,7 @@ import { createDbClient } from '@packrat/api/db'; -import { etlJobs, type NewCatalogItem, type NewInvalidItemLog } from '@packrat/api/db/schema'; import type { Env } from '@packrat/api/types/env'; import { mapCsvRowToItem } from '@packrat/api/utils/csv-utils'; +import { etlJobs, type NewCatalogItem, type NewInvalidItemLog } from '@packrat/db'; import { parse } from 'csv-parse'; import { eq, sql } from 'drizzle-orm'; import { R2BucketService } from '../r2-bucket'; diff --git a/packages/api/src/services/etl/processLogsBatch.ts b/packages/api/src/services/etl/processLogsBatch.ts index 661755850b..3f6cf53406 100644 --- a/packages/api/src/services/etl/processLogsBatch.ts +++ b/packages/api/src/services/etl/processLogsBatch.ts @@ -1,6 +1,6 @@ import type { Env } from '@packrat/api/types/env'; +import { invalidItemLogs, type NewInvalidItemLog } from '@packrat/db'; import { createDbClient } from '../../db'; -import { invalidItemLogs, type NewInvalidItemLog } from '../../db/schema'; import { updateEtlJobProgress } from './updateEtlJobProgress'; export async function processLogsBatch({ diff --git a/packages/api/src/services/etl/processValidItemsBatch.ts b/packages/api/src/services/etl/processValidItemsBatch.ts index 51a6d36f2e..beb988ecb2 100644 --- a/packages/api/src/services/etl/processValidItemsBatch.ts +++ b/packages/api/src/services/etl/processValidItemsBatch.ts @@ -1,6 +1,6 @@ -import type { NewCatalogItem } from '@packrat/api/db/schema'; import type { Env } from '@packrat/api/types/env'; import { getEmbeddingText } from '@packrat/api/utils/embeddingHelper'; +import type { NewCatalogItem } from '@packrat/db'; import { CatalogService } from '../catalogService'; import { generateManyEmbeddings } from '../embeddingService'; import { mergeItemsBySku } from './mergeItemsBySku'; diff --git a/packages/api/src/services/etl/updateEtlJobProgress.ts b/packages/api/src/services/etl/updateEtlJobProgress.ts index 7bc5c623eb..24b37558cb 100644 --- a/packages/api/src/services/etl/updateEtlJobProgress.ts +++ b/packages/api/src/services/etl/updateEtlJobProgress.ts @@ -1,6 +1,6 @@ import { createDbClient } from '@packrat/api/db'; -import { etlJobs } from '@packrat/api/db/schema'; import type { Env } from '@packrat/api/types/env'; +import { etlJobs } from '@packrat/db'; import { eq, sql } from 'drizzle-orm'; export async function updateEtlJobProgress( diff --git a/packages/api/src/services/imageDetectionService.ts b/packages/api/src/services/imageDetectionService.ts index b83d74f8b5..628207da61 100644 --- a/packages/api/src/services/imageDetectionService.ts +++ b/packages/api/src/services/imageDetectionService.ts @@ -1,9 +1,9 @@ import { createOpenAI } from '@ai-sdk/openai'; import { DEFAULT_MODELS } from '@packrat/api/utils/ai/models'; import { getEnv } from '@packrat/api/utils/env-validation'; +import type { CatalogItem } from '@packrat/db'; import { generateObject } from 'ai'; import { z } from 'zod'; -import type { CatalogItem } from '../db/schema'; import { CatalogService } from './catalogService'; const ITEM_DETECTION_SYSTEM_PROMPT = `You are an expert gear identification assistant specializing in outdoor and adventure equipment. diff --git a/packages/api/src/services/packItemService.ts b/packages/api/src/services/packItemService.ts index bb1c0ffdde..dbca1f4c53 100644 --- a/packages/api/src/services/packItemService.ts +++ b/packages/api/src/services/packItemService.ts @@ -1,5 +1,5 @@ import { createDb } from '@packrat/api/db'; -import { packItems } from '@packrat/api/db/schema'; +import { packItems } from '@packrat/db'; import { and, eq } from 'drizzle-orm'; export class PackItemService { diff --git a/packages/api/src/services/packService.ts b/packages/api/src/services/packService.ts index 3c0ae68b3a..f7c9cd260f 100644 --- a/packages/api/src/services/packService.ts +++ b/packages/api/src/services/packService.ts @@ -1,15 +1,9 @@ import { createOpenAI } from '@ai-sdk/openai'; import { createDb } from '@packrat/api/db'; -import { - type NewPack, - type NewPackItem, - type PackWithItems, - packItems, - packs, -} from '@packrat/api/db/schema'; -import { PACK_CATEGORIES } from '@packrat/api/types/constants'; import { DEFAULT_MODELS } from '@packrat/api/utils/ai/models'; import { getEnv } from '@packrat/api/utils/env-validation'; +import { PACK_CATEGORIES } from '@packrat/constants'; +import { type NewPack, type NewPackItem, type PackWithItems, packItems, packs } from '@packrat/db'; import { generateObject } from 'ai'; import { and, eq } from 'drizzle-orm'; import { z } from 'zod'; diff --git a/packages/api/src/services/userService.ts b/packages/api/src/services/userService.ts index 755a708153..af277b9b0d 100644 --- a/packages/api/src/services/userService.ts +++ b/packages/api/src/services/userService.ts @@ -1,6 +1,6 @@ import { createDb } from '@packrat/api/db'; -import { type User, users } from '@packrat/api/db/schema'; import { hashPassword } from '@packrat/api/utils/auth'; +import { type User, users } from '@packrat/db'; import { eq } from 'drizzle-orm'; export type CreateUserInput = { diff --git a/packages/api/src/types/constants.ts b/packages/api/src/types/constants.ts deleted file mode 100644 index 9797bbf02a..0000000000 --- a/packages/api/src/types/constants.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from '@packrat/schemas/constants'; -export type { CatalogItem, Pack, PackItem, PackWithItems, User } from '@packrat/types'; diff --git a/packages/api/src/types/etl.ts b/packages/api/src/types/etl.ts index 6d31e97fca..4e23f2ba2e 100644 --- a/packages/api/src/types/etl.ts +++ b/packages/api/src/types/etl.ts @@ -1,7 +1,7 @@ -import type { NewCatalogItem } from '@packrat/api/db/schema'; -import type { ValidationError } from './validation'; +import type { NewCatalogItem } from '@packrat/db'; +import type { ValidationError } from '@packrat/schemas/validation'; -export type { ValidationError } from './validation'; +export type { ValidationError } from '@packrat/schemas/validation'; export interface ValidatedCatalogItem { item: Partial; diff --git a/packages/api/src/types/index.ts b/packages/api/src/types/index.ts deleted file mode 100644 index c94f80f843..0000000000 --- a/packages/api/src/types/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './constants'; diff --git a/packages/api/src/types/validation.ts b/packages/api/src/types/validation.ts deleted file mode 100644 index 1955a47801..0000000000 --- a/packages/api/src/types/validation.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/schemas/validation'; diff --git a/packages/api/src/utils/DbUtils.ts b/packages/api/src/utils/DbUtils.ts index 05edbc90b3..346bafa4b1 100644 --- a/packages/api/src/utils/DbUtils.ts +++ b/packages/api/src/utils/DbUtils.ts @@ -1,5 +1,5 @@ import { createDb } from '@packrat/api/db'; -import { catalogItems, packs } from '@packrat/api/db/schema'; +import { catalogItems, packs } from '@packrat/db'; import { and, arrayOverlaps, eq, inArray, type SQL, sql } from 'drizzle-orm'; // Get pack details from the database diff --git a/packages/api/src/utils/__tests__/compute-pack.test.ts b/packages/api/src/utils/__tests__/compute-pack.test.ts index 1eec9d5099..b3b379fc94 100644 --- a/packages/api/src/utils/__tests__/compute-pack.test.ts +++ b/packages/api/src/utils/__tests__/compute-pack.test.ts @@ -1,4 +1,4 @@ -import type { PackItem, PackWithItems } from '@packrat/api/db/schema'; +import type { PackItem, PackWithItems } from '@packrat/db'; import { describe, expect, it } from 'vitest'; import { computePacksWeights, computePackWeights } from '../compute-pack'; diff --git a/packages/api/src/utils/compute-pack.ts b/packages/api/src/utils/compute-pack.ts index da16adad10..39c32d04e8 100644 --- a/packages/api/src/utils/compute-pack.ts +++ b/packages/api/src/utils/compute-pack.ts @@ -1,4 +1,4 @@ -import type { PackWithItems } from '@packrat/api/db/schema'; +import type { PackWithItems } from '@packrat/db'; import type { WeightUnit } from '@packrat/units'; import { displayWeight, normalize, parseWeightUnit } from '@packrat/units'; diff --git a/packages/api/src/utils/csv-utils.ts b/packages/api/src/utils/csv-utils.ts index a6e1e0a688..01d17db178 100644 --- a/packages/api/src/utils/csv-utils.ts +++ b/packages/api/src/utils/csv-utils.ts @@ -1,6 +1,6 @@ +import type { NewCatalogItem } from '@packrat/db'; import { isString } from '@packrat/guards'; -import type { NewCatalogItem } from '../db/schema'; -import { AvailabilitySchema, WeightUnitSchema } from '../types'; +import { AvailabilitySchema, WeightUnitSchema } from '@packrat/schemas/constants'; // ── CSV sanitization regex constants ── const NEWLINE_CHARS = /[\r\n]+/g; diff --git a/packages/api/src/utils/embeddingHelper.ts b/packages/api/src/utils/embeddingHelper.ts index e937afb344..55c68e2c4a 100644 --- a/packages/api/src/utils/embeddingHelper.ts +++ b/packages/api/src/utils/embeddingHelper.ts @@ -1,4 +1,4 @@ -import type { CatalogItem, PackItem } from '../db/schema'; +import type { CatalogItem, PackItem } from '@packrat/db'; type ItemForEmbedding = Partial | Partial; diff --git a/packages/api/test/auth.test.ts b/packages/api/test/auth.test.ts index ae24d04299..c41a242647 100644 --- a/packages/api/test/auth.test.ts +++ b/packages/api/test/auth.test.ts @@ -22,8 +22,8 @@ import { drizzleAdapter } from '@better-auth/drizzle-adapter'; import { getAuth } from '@packrat/api/auth'; import { createDb } from '@packrat/api/db'; -import * as schema from '@packrat/api/db/schema'; import { authPlugin } from '@packrat/api/middleware/auth'; +import * as schema from '@packrat/db/schema'; import { betterAuth } from 'better-auth'; import { bearer, jwt } from 'better-auth/plugins'; import { eq } from 'drizzle-orm'; diff --git a/packages/api/test/db-test-helper.ts b/packages/api/test/db-test-helper.ts index 277a6f9f75..a94113e251 100644 --- a/packages/api/test/db-test-helper.ts +++ b/packages/api/test/db-test-helper.ts @@ -1,6 +1,6 @@ +import * as schema from '@packrat/db/schema'; import { drizzle } from 'drizzle-orm/node-postgres'; import { Client } from 'pg'; -import * as schema from '../src/db/schema'; // Create a PostgreSQL client for testing export const createTestDbConnection = () => { diff --git a/packages/api/test/etl.test.ts b/packages/api/test/etl.test.ts index f70c70e917..492e8edf69 100644 --- a/packages/api/test/etl.test.ts +++ b/packages/api/test/etl.test.ts @@ -1,8 +1,8 @@ import { createDbClient } from '@packrat/api/db'; -import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/api/db/schema'; import { processCatalogETL } from '@packrat/api/services/etl/processCatalogEtl'; import { processValidItemsBatch } from '@packrat/api/services/etl/processValidItemsBatch'; import { R2BucketService } from '@packrat/api/services/r2-bucket'; +import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/db'; import { count, eq } from 'drizzle-orm'; import { describe, expect, it, vi } from 'vitest'; diff --git a/packages/api/test/fixtures/catalog-fixtures.ts b/packages/api/test/fixtures/catalog-fixtures.ts index 636b172ca8..0be04b1617 100644 --- a/packages/api/test/fixtures/catalog-fixtures.ts +++ b/packages/api/test/fixtures/catalog-fixtures.ts @@ -1,5 +1,5 @@ +import type { catalogItems } from '@packrat/db'; import type { InferInsertModel } from 'drizzle-orm'; -import type { catalogItems } from '../../src/db/schema'; /** * Test fixture for creating a minimal valid catalog item diff --git a/packages/api/test/fixtures/pack-fixtures.ts b/packages/api/test/fixtures/pack-fixtures.ts index 0abc349be1..ea1ae72c50 100644 --- a/packages/api/test/fixtures/pack-fixtures.ts +++ b/packages/api/test/fixtures/pack-fixtures.ts @@ -1,5 +1,5 @@ +import type { packItems, packs } from '@packrat/db'; import type { InferInsertModel } from 'drizzle-orm'; -import type { packItems, packs } from '../../src/db/schema'; type PackOverrides = Partial> & { userId: string }; type PackItemOverrides = Partial> & { userId: string }; diff --git a/packages/api/test/fixtures/pack-template-fixtures.ts b/packages/api/test/fixtures/pack-template-fixtures.ts index 4afc7eae0e..43c8c64eab 100644 --- a/packages/api/test/fixtures/pack-template-fixtures.ts +++ b/packages/api/test/fixtures/pack-template-fixtures.ts @@ -1,5 +1,5 @@ +import type { packTemplateItems, packTemplates } from '@packrat/db'; import type { InferInsertModel } from 'drizzle-orm'; -import type { packTemplateItems, packTemplates } from '../../src/db/schema'; type PackTemplateOverrides = Partial> & { userId: string }; diff --git a/packages/api/test/packs.test.ts b/packages/api/test/packs.test.ts index acf67167c7..5ecfb3382e 100644 --- a/packages/api/test/packs.test.ts +++ b/packages/api/test/packs.test.ts @@ -1,4 +1,4 @@ -import type { Pack } from '@packrat/api/db/schema'; +import type { Pack } from '@packrat/db'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { seedAndLoginTestUser, diff --git a/packages/api/test/setup.ts b/packages/api/test/setup.ts index 8d3544cb6c..658dc9197c 100644 --- a/packages/api/test/setup.ts +++ b/packages/api/test/setup.ts @@ -1,9 +1,9 @@ import { neonConfig, Pool } from '@neondatabase/serverless'; +import * as schema from '@packrat/db/schema'; import { isFunction, isObject } from '@packrat/guards'; import { sql } from 'drizzle-orm'; import { drizzle } from 'drizzle-orm/neon-serverless'; import { afterAll, beforeAll, beforeEach, vi } from 'vitest'; -import * as schema from '../src/db/schema'; import { clearCurrentTestUsers } from './utils/test-helpers'; // ── Setup regex constants ── diff --git a/packages/api/test/utils/db-helpers.ts b/packages/api/test/utils/db-helpers.ts index 046540f955..d576676290 100644 --- a/packages/api/test/utils/db-helpers.ts +++ b/packages/api/test/utils/db-helpers.ts @@ -1,9 +1,5 @@ import { createDb } from '@packrat/api/db'; import { hashPassword } from '@packrat/api/utils/auth'; -import { assertDefined } from '@packrat/guards'; -import type { InferInsertModel } from 'drizzle-orm'; - -import * as schema from '../../src/db/schema'; import { catalogItems, packItems, @@ -11,7 +7,10 @@ import { packTemplateItems, packTemplates, type users, -} from '../../src/db/schema'; +} from '@packrat/db'; +import * as schema from '@packrat/db/schema'; +import { assertDefined } from '@packrat/guards'; +import type { InferInsertModel } from 'drizzle-orm'; import { createTestCatalogItem } from '../fixtures/catalog-fixtures'; import { createTestPack, createTestPackItem } from '../fixtures/pack-fixtures'; import { diff --git a/packages/api/test/utils/user-helpers.ts b/packages/api/test/utils/user-helpers.ts index 946a41a4a4..55a4b9d163 100644 --- a/packages/api/test/utils/user-helpers.ts +++ b/packages/api/test/utils/user-helpers.ts @@ -1,6 +1,6 @@ import { createDb } from '@packrat/api/db'; -import { users } from '@packrat/api/db/schema'; import { hashPassword } from '@packrat/api/utils/auth'; +import { users } from '@packrat/db'; import type { InferInsertModel } from 'drizzle-orm'; /** diff --git a/packages/db/src/constants.ts b/packages/db/src/constants.ts deleted file mode 100644 index cf8d5c5bcf..0000000000 --- a/packages/db/src/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@packrat/constants'; diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index 8ce55998ec..537e1903ca 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -1,4 +1,3 @@ -export * from './constants'; export * from './schema'; export * from './validation'; export * from './zod-schemas'; diff --git a/packages/db/src/schema.ts b/packages/db/src/schema.ts index 53760f064d..6f8b9d807a 100644 --- a/packages/db/src/schema.ts +++ b/packages/db/src/schema.ts @@ -1,3 +1,4 @@ +import type { PackCategory, WeightUnit } from '@packrat/constants'; import { type InferInsertModel, type InferSelectModel, relations, sql } from 'drizzle-orm'; import { type AnyPgColumn, @@ -15,7 +16,6 @@ import { unique, vector, } from 'drizzle-orm/pg-core'; -import type { PackCategory, WeightUnit } from './constants'; import type { ValidationError } from './validation'; const availabilityEnum = pgEnum('availability', ['in_stock', 'out_of_stock', 'preorder']); From 13e6d216ff65d2fe9f338318b6ca81d77d03c127 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 11:42:56 -0600 Subject: [PATCH 38/51] fix(expo): enable unstable_enablePackageExports for @packrat/schemas subpath resolution Metro does not use the package.json exports field by default; without unstable_enablePackageExports the bundler cannot resolve subpath imports like @packrat/schemas/constants, causing the E2E APK build to fail. --- apps/expo/metro.config.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/expo/metro.config.js b/apps/expo/metro.config.js index 2613f966d0..3796e5f589 100644 --- a/apps/expo/metro.config.js +++ b/apps/expo/metro.config.js @@ -10,6 +10,9 @@ const config = getSentryExpoConfig(__dirname); config.resolver = { ...config.resolver, assetExts: [...(config.resolver?.assetExts ?? []), 'wasm'], + // Enable package.json "exports" field resolution so workspace packages with + // subpath exports (e.g. @packrat/schemas/constants) resolve correctly. + unstable_enablePackageExports: true, // Exclude the ESM "import" condition so packages like Jotai resolve to their // CJS builds instead of .mjs files that contain import.meta (invalid in // Metro's __d() CJS module wrapper). From c9e64fa676b949cb893e7b4953037274b02d85b4 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 11:53:03 -0600 Subject: [PATCH 39/51] fix(schemas): include .ts extension in subpath exports for Metro resolution Metro's unstable_enablePackageExports resolves the exports map to an exact path. Without the .ts extension, ./src/constants resolves to a non-existent file and Metro warns + falls back, ultimately failing to find the module. Explicit ./src/*.ts makes Metro find the source file directly. --- packages/schemas/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/schemas/package.json b/packages/schemas/package.json index defcd15ee1..627dece8c8 100644 --- a/packages/schemas/package.json +++ b/packages/schemas/package.json @@ -9,8 +9,8 @@ "default": "./src/index.ts" }, "./*": { - "types": "./src/*", - "default": "./src/*" + "types": "./src/*.ts", + "default": "./src/*.ts" } }, "scripts": { From 2b667fbc9a44f517c3b40bb57290bc45db67e95d Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 12:34:35 -0600 Subject: [PATCH 40/51] fix: add .ts extension to all wildcard subpath exports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bun and Metro resolve exports map paths exactly — ./src/* (extensionless) produces a path that doesn't exist, causing module resolution failures. Apply ./src/*.ts to @packrat/db, @packrat/api, @packrat/osm-db, and @packrat/overpass to match the same fix already applied to @packrat/schemas. --- packages/api/package.json | 4 ++-- packages/db/package.json | 4 ++-- packages/osm-db/package.json | 4 ++-- packages/overpass/package.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/api/package.json b/packages/api/package.json index 11688447e2..140c186029 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -9,8 +9,8 @@ "default": "./src/index.ts" }, "./*": { - "types": "./src/*", - "default": "./src/*" + "types": "./src/*.ts", + "default": "./src/*.ts" } }, "main": "./src/index.ts", diff --git a/packages/db/package.json b/packages/db/package.json index 5485d4aed7..43aafc6b19 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -9,8 +9,8 @@ "default": "./src/index.ts" }, "./*": { - "types": "./src/*", - "default": "./src/*" + "types": "./src/*.ts", + "default": "./src/*.ts" } }, "scripts": { diff --git a/packages/osm-db/package.json b/packages/osm-db/package.json index 46dc95b72f..a9f88c6eac 100644 --- a/packages/osm-db/package.json +++ b/packages/osm-db/package.json @@ -9,8 +9,8 @@ "default": "./src/index.ts" }, "./*": { - "types": "./src/*", - "default": "./src/*" + "types": "./src/*.ts", + "default": "./src/*.ts" } }, "scripts": { diff --git a/packages/overpass/package.json b/packages/overpass/package.json index f4ad457aef..4b57602d20 100644 --- a/packages/overpass/package.json +++ b/packages/overpass/package.json @@ -9,8 +9,8 @@ "default": "./src/index.ts" }, "./*": { - "types": "./src/*", - "default": "./src/*" + "types": "./src/*.ts", + "default": "./src/*.ts" } }, "scripts": { From 52c289e8e8e9f0804005f80ff53f206504343413 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 13:17:59 -0600 Subject: [PATCH 41/51] refactor: convert admin schemas from TypeBox to Zod, move to @packrat/schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - packages/schemas/src/admin.ts: full Zod conversion of all TypeBox schemas (AdminStats, AdminUser, AdminPack, AdminCatalog, ETL, analytics, trails) - packages/api/src/schemas/admin.ts: deleted (no longer a shim, no longer needed) - Admin routes now import from @packrat/schemas/admin - apps/admin/lib/api.ts: Static → z.infer, import path updated to @packrat/schemas/admin - TrailConditionReportSchema in admin renamed AdminTrailConditionReportSchema to avoid barrel collision with trailConditions.ts variant 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- apps/admin/lib/api.ts | 46 +-- apps/admin/package.json | 1 + .../api/src/routes/admin/analytics/catalog.ts | 14 +- .../src/routes/admin/analytics/platform.ts | 12 +- packages/api/src/routes/admin/index.ts | 10 +- packages/api/src/routes/admin/trails.ts | 4 +- packages/api/src/schemas/admin.ts | 260 ----------------- packages/schemas/src/admin.ts | 272 ++++++++++++++++++ packages/schemas/src/index.ts | 1 + 9 files changed, 317 insertions(+), 303 deletions(-) delete mode 100644 packages/api/src/schemas/admin.ts create mode 100644 packages/schemas/src/admin.ts diff --git a/apps/admin/lib/api.ts b/apps/admin/lib/api.ts index 1df1af5636..48c47051a0 100644 --- a/apps/admin/lib/api.ts +++ b/apps/admin/lib/api.ts @@ -1,10 +1,12 @@ import { treaty } from '@elysiajs/eden'; import type { App } from '@packrat/api'; +import { isObject } from '@packrat/guards'; import type { ActiveUsersSchema, ActivityPointSchema, AdminCatalogItemSchema, AdminPackItemSchema, + AdminTrailConditionReportSchema, AdminUserItemSchema, BrandRowSchema, BreakdownItemSchema, @@ -16,13 +18,11 @@ import type { EtlResponseSchema, GrowthPointSchema, PriceBucketSchema, - TrailConditionReportSchema, TrailGeometrySchema, TrailSearchItemSchema, TrailSearchResultSchema, -} from '@packrat/api/schemas/admin'; -import { isObject } from '@packrat/guards'; -import type { Static } from '@sinclair/typebox'; +} from '@packrat/schemas/admin'; +import type { z } from 'zod'; import { clearToken, getAuthHeader } from './auth'; import { adminEnv } from './env'; @@ -74,7 +74,7 @@ export async function getStats(): Promise { // ─── Users ──────────────────────────────────────────────────────────────────── -export type AdminUser = Static; +export type AdminUser = z.infer; export interface PaginatedResponse { data: T[]; @@ -124,7 +124,7 @@ export async function restoreUser(id: string): Promise<{ success: boolean }> { // ─── Packs ──────────────────────────────────────────────────────────────────── -export type AdminPack = Static; +export type AdminPack = z.infer; export async function getPacks({ limit = 100, @@ -152,7 +152,7 @@ export async function deletePack(id: string): Promise<{ success: boolean }> { // ─── Catalog Items ──────────────────────────────────────────────────────────── -export type AdminCatalogItem = Static; +export type AdminCatalogItem = z.infer; export interface UpdateCatalogItemInput { name?: string; @@ -197,10 +197,10 @@ export async function updateCatalogItem( // ─── Analytics — Platform ───────────────────────────────────────────────────── -export type GrowthPoint = Static; -export type ActivityPoint = Static; -export type BreakdownItem = Static; -export type ActiveUsers = Static; +export type GrowthPoint = z.infer; +export type ActivityPoint = z.infer; +export type BreakdownItem = z.infer; +export type ActiveUsers = z.infer; export type AnalyticsPeriod = 'day' | 'week' | 'month'; export async function getPlatformGrowth( @@ -233,12 +233,12 @@ export async function getPlatformBreakdown(): Promise { // ─── Analytics — Catalog ───────────────────────────────────────────────────── -export type CatalogOverview = Static; -export type BrandRow = Static; -export type PriceBucket = Static; -export type EtlJob = Static; -export type EtlResponse = Static; -export type EmbeddingStats = Static; +export type CatalogOverview = z.infer; +export type BrandRow = z.infer; +export type PriceBucket = z.infer; +export type EtlJob = z.infer; +export type EtlResponse = z.infer; +export type EmbeddingStats = z.infer; export async function getCatalogOverview(): Promise { const { data, error } = await adminClient.analytics.catalog.overview.get(); @@ -276,10 +276,10 @@ export async function getCatalogEmbeddings(): Promise { // ─── Admin Trails ───────────────────────────────────────────────────────────── -export type TrailSearchResult = Static; -export type TrailGeometry = Static; -export type TrailSearchPage = Static; -export type TrailConditionReport = Static; +export type TrailSearchResult = z.infer; +export type TrailGeometry = z.infer; +export type TrailSearchPage = z.infer; +export type TrailConditionReport = z.infer; export async function searchTrails({ q, @@ -345,8 +345,8 @@ export function resetStuckEtlJobs(): Promise<{ reset: number; ids: string[] }> { return adminFetch('/analytics/catalog/etl/reset-stuck', { method: 'POST' }); } -export type EtlFailureSummary = Static; -export type EtlJobFailures = Static; +export type EtlFailureSummary = z.infer; +export type EtlJobFailures = z.infer; export function getEtlFailureSummary(limit = 20): Promise { return adminFetch(`/analytics/catalog/etl/failure-summary?limit=${limit}`); diff --git a/apps/admin/package.json b/apps/admin/package.json index a0026c80e4..e0651d8984 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -13,6 +13,7 @@ "dependencies": { "@elysiajs/eden": "catalog:", "@packrat/api-client": "workspace:*", + "@packrat/schemas": "workspace:*", "@packrat/app": "workspace:*", "@packrat/guards": "workspace:*", "@packrat/web-ui": "workspace:*", diff --git a/packages/api/src/routes/admin/analytics/catalog.ts b/packages/api/src/routes/admin/analytics/catalog.ts index b0d8d0e3ce..d4d6189f7d 100644 --- a/packages/api/src/routes/admin/analytics/catalog.ts +++ b/packages/api/src/routes/admin/analytics/catalog.ts @@ -1,4 +1,7 @@ import { createDb } from '@packrat/api/db'; +import { queueCatalogETL } from '@packrat/api/services/etl/queue'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/db'; import { AdminErrorResponses, BrandRowSchema, @@ -9,12 +12,9 @@ import { EtlResponseSchema, EtlRetrySchema, PriceBucketSchema, -} from '@packrat/api/schemas/admin'; -import { queueCatalogETL } from '@packrat/api/services/etl/queue'; -import { getEnv } from '@packrat/api/utils/env-validation'; -import { catalogItems, etlJobs, invalidItemLogs } from '@packrat/db'; +} from '@packrat/schemas/admin'; import { and, avg, count, desc, eq, gt, isNotNull, lt, max, min, sql } from 'drizzle-orm'; -import { Elysia, status, t } from 'elysia'; +import { Elysia, status } from 'elysia'; import { z } from 'zod'; export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) @@ -133,7 +133,7 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) query: z.object({ limit: z.coerce.number().int().min(1).max(100).optional().default(25), }), - response: { 200: t.Array(BrandRowSchema), ...AdminErrorResponses }, + response: { 200: z.array(BrandRowSchema), ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Top gear brands' }, }, ) @@ -170,7 +170,7 @@ export const catalogAnalyticsRoutes = new Elysia({ prefix: '/catalog' }) } }, { - response: { 200: t.Array(PriceBucketSchema), ...AdminErrorResponses }, + response: { 200: z.array(PriceBucketSchema), ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Price distribution' }, }, ) diff --git a/packages/api/src/routes/admin/analytics/platform.ts b/packages/api/src/routes/admin/analytics/platform.ts index ac193307e5..589ab59497 100644 --- a/packages/api/src/routes/admin/analytics/platform.ts +++ b/packages/api/src/routes/admin/analytics/platform.ts @@ -1,14 +1,14 @@ import { createDb } from '@packrat/api/db'; +import { catalogItems, packs, posts, trailConditionReports, trips, users } from '@packrat/db'; import { ActiveUsersSchema, ActivityPointSchema, AdminErrorResponses, BreakdownItemSchema, GrowthPointSchema, -} from '@packrat/api/schemas/admin'; -import { catalogItems, packs, posts, trailConditionReports, trips, users } from '@packrat/db'; +} from '@packrat/schemas/admin'; import { and, count, desc, eq, gte, sql } from 'drizzle-orm'; -import { Elysia, status, t } from 'elysia'; +import { Elysia, status } from 'elysia'; import { z } from 'zod'; const PeriodSchema = z.object({ @@ -99,7 +99,7 @@ export const platformAnalyticsRoutes = new Elysia({ prefix: '/platform' }) }, { query: PeriodSchema, - response: { 200: t.Array(GrowthPointSchema), ...AdminErrorResponses }, + response: { 200: z.array(GrowthPointSchema), ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Platform growth metrics' }, }, ) @@ -176,7 +176,7 @@ export const platformAnalyticsRoutes = new Elysia({ prefix: '/platform' }) }, { query: PeriodSchema, - response: { 200: t.Array(ActivityPointSchema), ...AdminErrorResponses }, + response: { 200: z.array(ActivityPointSchema), ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'User activity metrics' }, }, ) @@ -231,7 +231,7 @@ export const platformAnalyticsRoutes = new Elysia({ prefix: '/platform' }) } }, { - response: { 200: t.Array(BreakdownItemSchema), ...AdminErrorResponses }, + response: { 200: z.array(BreakdownItemSchema), ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Categorical distribution metrics' }, }, ); diff --git a/packages/api/src/routes/admin/index.ts b/packages/api/src/routes/admin/index.ts index d8e3a6fc93..6329031a2c 100644 --- a/packages/api/src/routes/admin/index.ts +++ b/packages/api/src/routes/admin/index.ts @@ -1,6 +1,10 @@ import { cors } from '@elysiajs/cors'; import { createDb } from '@packrat/api/db'; import { verifyCFAccessRequest } from '@packrat/api/middleware/cfAccess'; +import { timingSafeEqual } from '@packrat/api/utils/auth'; +import { getEnv } from '@packrat/api/utils/env-validation'; +import { catalogItems, packs, users } from '@packrat/db'; +import { assertAllDefined } from '@packrat/guards'; import { AdminCatalogListSchema, AdminErrorResponses, @@ -10,11 +14,7 @@ import { CatalogUpdateSchema, HardDeleteSuccessSchema, SuccessSchema, -} from '@packrat/api/schemas/admin'; -import { timingSafeEqual } from '@packrat/api/utils/auth'; -import { getEnv } from '@packrat/api/utils/env-validation'; -import { catalogItems, packs, users } from '@packrat/db'; -import { assertAllDefined } from '@packrat/guards'; +} from '@packrat/schemas/admin'; import { and, count, desc, eq, ilike, or } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { jwtVerify, SignJWT } from 'jose'; diff --git a/packages/api/src/routes/admin/trails.ts b/packages/api/src/routes/admin/trails.ts index b78b03b9f4..d6d7a870f2 100644 --- a/packages/api/src/routes/admin/trails.ts +++ b/packages/api/src/routes/admin/trails.ts @@ -1,4 +1,5 @@ import { createDb, createOsmDb } from '@packrat/api/db'; +import { trailConditionReports, users } from '@packrat/db'; import { AdminErrorResponses, SuccessSchema, @@ -6,8 +7,7 @@ import { TrailGeometrySchema, TrailSearchItemSchema, TrailSearchResultSchema, -} from '@packrat/api/schemas/admin'; -import { trailConditionReports, users } from '@packrat/db'; +} from '@packrat/schemas/admin'; import { and, count, desc, eq, ilike, or, sql } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; diff --git a/packages/api/src/schemas/admin.ts b/packages/api/src/schemas/admin.ts deleted file mode 100644 index e64ad8655a..0000000000 --- a/packages/api/src/schemas/admin.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { t } from 'elysia'; - -// t.Unsafe keeps the JSON Schema shape for OpenAPI docs while giving -// TypeScript type `any`. ElysiaCustomStatusResponse has T in both covariant -// and contravariant positions (invariant), so a literal error body literal -// can never unify with { error: string } without `any` bypassing invariance. -// biome-ignore lint/suspicious/noExplicitAny: intentional — see comment above -const Err = t.Unsafe(t.Object({ error: t.String() }, { additionalProperties: true })); -export const AdminErrorResponses = { - 400: Err, - 401: Err, - 404: Err, - 409: Err, - 429: Err, - 500: Err, - 503: Err, -} as const; - -// ─── Stats ──────────────────────────────────────────────────────────────────── - -export const AdminStatsSchema = t.Object({ - users: t.Number(), - packs: t.Number(), - items: t.Number(), -}); - -// ─── Users ──────────────────────────────────────────────────────────────────── - -export const AdminUserItemSchema = t.Object({ - id: t.String(), - email: t.String(), - firstName: t.Nullable(t.String()), - lastName: t.Nullable(t.String()), - role: t.Nullable(t.String()), - emailVerified: t.Nullable(t.Boolean()), - avatarUrl: t.Nullable(t.String()), - createdAt: t.Nullable(t.String()), - updatedAt: t.Nullable(t.String()), -}); - -// ─── Packs ──────────────────────────────────────────────────────────────────── - -export const AdminPackItemSchema = t.Object({ - id: t.String(), - name: t.String(), - description: t.Nullable(t.String()), - category: t.String(), - isPublic: t.Nullable(t.Boolean()), - isAIGenerated: t.Boolean(), - tags: t.Nullable(t.Array(t.String())), - image: t.Nullable(t.String()), - createdAt: t.Nullable(t.String()), - updatedAt: t.Nullable(t.String()), - userEmail: t.Nullable(t.String()), -}); - -// ─── Catalog ───────────────────────────────────────────────────────────────── - -export const AdminCatalogItemSchema = t.Object({ - id: t.Number(), - name: t.String(), - description: t.Nullable(t.String()), - categories: t.Nullable(t.Array(t.String())), - brand: t.Nullable(t.String()), - model: t.Nullable(t.String()), - sku: t.String(), - price: t.Nullable(t.Number()), - currency: t.Nullable(t.String()), - weight: t.Nullable(t.Number()), - weightUnit: t.Nullable(t.String()), - availability: t.Nullable(t.String()), - ratingValue: t.Nullable(t.Number()), - reviewCount: t.Nullable(t.Number()), - color: t.Nullable(t.String()), - size: t.Nullable(t.String()), - material: t.Nullable(t.String()), - seller: t.Nullable(t.String()), - productUrl: t.String(), - images: t.Nullable(t.Array(t.String())), - variants: t.Nullable(t.Array(t.Object({ attribute: t.String(), values: t.Array(t.String()) }))), - techs: t.Nullable(t.Record(t.String(), t.String())), - links: t.Nullable(t.Array(t.Object({ title: t.String(), url: t.String() }))), - createdAt: t.Nullable(t.String()), -}); - -// ─── Paginated wrappers ─────────────────────────────────────────────────────── - -const Paginated = >(item: T) => - t.Object({ data: t.Array(item), total: t.Number(), limit: t.Number(), offset: t.Number() }); - -export const AdminUsersListSchema = Paginated(AdminUserItemSchema); -export const AdminPacksListSchema = Paginated(AdminPackItemSchema); -export const AdminCatalogListSchema = Paginated(AdminCatalogItemSchema); - -// ─── Mutations ──────────────────────────────────────────────────────────────── - -export const SuccessSchema = t.Object({ success: t.Literal(true) }); -export const HardDeleteSuccessSchema = t.Object({ - success: t.Literal(true), - purged: t.Literal(true), -}); -export const CatalogUpdateSchema = t.Object({ id: t.Number(), name: t.String() }); - -// ─── Analytics — Platform ───────────────────────────────────────────────────── - -export const GrowthPointSchema = t.Object({ - period: t.String(), - users: t.Number(), - packs: t.Number(), - catalogItems: t.Number(), -}); - -export const ActivityPointSchema = t.Object({ - period: t.String(), - trips: t.Number(), - trailReports: t.Number(), - posts: t.Number(), -}); - -export const ActiveUsersSchema = t.Object({ dau: t.Number(), wau: t.Number(), mau: t.Number() }); - -export const BreakdownItemSchema = t.Object({ category: t.String(), count: t.Number() }); - -// ─── Analytics — Catalog ───────────────────────────────────────────────────── - -export const CatalogOverviewSchema = t.Object({ - totalItems: t.Number(), - totalBrands: t.Number(), - avgPrice: t.Nullable(t.Number()), - minPrice: t.Nullable(t.Number()), - maxPrice: t.Nullable(t.Number()), - embeddingCoverage: t.Object({ total: t.Number(), withEmbedding: t.Number(), pct: t.Number() }), - availability: t.Array(t.Object({ status: t.Nullable(t.String()), count: t.Number() })), - addedLast30Days: t.Number(), -}); - -export const BrandRowSchema = t.Object({ - brand: t.String(), - itemCount: t.Number(), - avgPrice: t.Nullable(t.Number()), - minPrice: t.Nullable(t.Number()), - maxPrice: t.Nullable(t.Number()), - avgRating: t.Nullable(t.Number()), -}); - -export const PriceBucketSchema = t.Object({ bucket: t.String(), count: t.Number() }); - -export const EtlJobSchema = t.Object({ - id: t.String(), - status: t.Union([t.Literal('running'), t.Literal('completed'), t.Literal('failed')]), - source: t.String(), - filename: t.String(), - scraperRevision: t.String(), - startedAt: t.String(), - completedAt: t.Nullable(t.String()), - totalProcessed: t.Nullable(t.Number()), - totalValid: t.Nullable(t.Number()), - totalInvalid: t.Nullable(t.Number()), - successRate: t.Nullable(t.Number()), -}); - -export const EtlResponseSchema = t.Object({ - jobs: t.Array(EtlJobSchema), - summary: t.Object({ - totalRuns: t.Number(), - completed: t.Number(), - failed: t.Number(), - totalItemsIngested: t.Number(), - }), -}); - -export const EmbeddingStatsSchema = t.Object({ - total: t.Number(), - withEmbedding: t.Number(), - pending: t.Number(), - coveragePct: t.Number(), -}); - -const EtlErrorRowSchema = t.Object({ field: t.String(), reason: t.String(), count: t.Number() }); - -export const EtlFailureSummarySchema = t.Object({ - topErrors: t.Array(EtlErrorRowSchema), - totalInvalidItems: t.Number(), -}); - -export const EtlJobFailuresSchema = t.Object({ - jobId: t.String(), - errorBreakdown: t.Array(EtlErrorRowSchema), - samples: t.Array( - t.Object({ - rowIndex: t.Number(), - errors: t.Array( - t.Object({ - field: t.String(), - reason: t.String(), - value: t.Optional(t.Unknown()), - }), - ), - rawData: t.Unknown(), - }), - ), - totalShown: t.Number(), -}); - -export const EtlResetStuckSchema = t.Object({ reset: t.Number(), ids: t.Array(t.String()) }); - -export const EtlRetrySchema = t.Object({ - success: t.Literal(true), - newJobId: t.String(), - objectKey: t.String(), -}); - -// ─── Trails ─────────────────────────────────────────────────────────────────── - -export const TrailSearchItemSchema = t.Object({ - osmId: t.String(), - name: t.Nullable(t.String()), - sport: t.Nullable(t.String()), - network: t.Nullable(t.String()), - distance: t.Nullable(t.String()), - difficulty: t.Nullable(t.String()), - description: t.Nullable(t.String()), - bbox: t.Nullable(t.Unknown()), -}); - -export const TrailSearchResultSchema = t.Object({ - trails: t.Array(TrailSearchItemSchema), - hasMore: t.Boolean(), - offset: t.Number(), - limit: t.Number(), -}); - -export const TrailGeometrySchema = t.Object({ - osmId: t.String(), - name: t.Nullable(t.String()), - sport: t.Nullable(t.String()), - network: t.Nullable(t.String()), - distance: t.Nullable(t.String()), - difficulty: t.Nullable(t.String()), - description: t.Nullable(t.String()), - geometry: t.Nullable(t.Unknown()), -}); - -export const TrailConditionReportSchema = t.Object({ - id: t.String(), - trailName: t.String(), - trailRegion: t.Nullable(t.String()), - surface: t.String(), - overallCondition: t.String(), - hazards: t.Array(t.String()), - waterCrossings: t.Number(), - notes: t.Nullable(t.String()), - deleted: t.Boolean(), - deletedAt: t.Nullable(t.String()), - createdAt: t.String(), - userId: t.Number(), - userEmail: t.Nullable(t.String()), -}); - -export const TrailConditionsListSchema = Paginated(TrailConditionReportSchema); diff --git a/packages/schemas/src/admin.ts b/packages/schemas/src/admin.ts new file mode 100644 index 0000000000..07487e9fa5 --- /dev/null +++ b/packages/schemas/src/admin.ts @@ -0,0 +1,272 @@ +import { z } from 'zod'; + +// ─── Error responses ────────────────────────────────────────────────────────── + +// z.any() mirrors t.Unsafe — Elysia invariance requires the handler return +// type to be assignable to the declared response type, and error bodies frequently +// carry extra fields (e.g. `code`). Using any sidesteps the invariance check the +// same way t.Unsafe did with TypeBox. +const Err = z.any(); +export const AdminErrorResponses = { + 400: Err, + 401: Err, + 404: Err, + 409: Err, + 429: Err, + 500: Err, + 503: Err, +} as const; + +// ─── Stats ──────────────────────────────────────────────────────────────────── + +export const AdminStatsSchema = z.object({ + users: z.number(), + packs: z.number(), + items: z.number(), +}); + +// ─── Users ──────────────────────────────────────────────────────────────────── + +export const AdminUserItemSchema = z.object({ + id: z.string(), + email: z.string(), + firstName: z.string().nullable(), + lastName: z.string().nullable(), + role: z.string().nullable(), + emailVerified: z.boolean().nullable(), + avatarUrl: z.string().nullable(), + createdAt: z.string().nullable(), + updatedAt: z.string().nullable(), +}); + +// ─── Packs ──────────────────────────────────────────────────────────────────── + +export const AdminPackItemSchema = z.object({ + id: z.string(), + name: z.string(), + description: z.string().nullable(), + category: z.string(), + isPublic: z.boolean().nullable(), + isAIGenerated: z.boolean(), + tags: z.array(z.string()).nullable(), + image: z.string().nullable(), + createdAt: z.string().nullable(), + updatedAt: z.string().nullable(), + userEmail: z.string().nullable(), +}); + +// ─── Catalog ───────────────────────────────────────────────────────────────── + +export const AdminCatalogItemSchema = z.object({ + id: z.number(), + name: z.string(), + description: z.string().nullable(), + categories: z.array(z.string()).nullable(), + brand: z.string().nullable(), + model: z.string().nullable(), + sku: z.string(), + price: z.number().nullable(), + currency: z.string().nullable(), + weight: z.number().nullable(), + weightUnit: z.string().nullable(), + availability: z.string().nullable(), + ratingValue: z.number().nullable(), + reviewCount: z.number().nullable(), + color: z.string().nullable(), + size: z.string().nullable(), + material: z.string().nullable(), + seller: z.string().nullable(), + productUrl: z.string(), + images: z.array(z.string()).nullable(), + variants: z.array(z.object({ attribute: z.string(), values: z.array(z.string()) })).nullable(), + techs: z.record(z.string(), z.string()).nullable(), + links: z.array(z.object({ title: z.string(), url: z.string() })).nullable(), + createdAt: z.string().nullable(), +}); + +// ─── Paginated wrappers ─────────────────────────────────────────────────────── + +const paginated = (item: T) => + z.object({ + data: z.array(item), + total: z.number(), + limit: z.number(), + offset: z.number(), + }); + +export const AdminUsersListSchema = paginated(AdminUserItemSchema); +export const AdminPacksListSchema = paginated(AdminPackItemSchema); +export const AdminCatalogListSchema = paginated(AdminCatalogItemSchema); + +// ─── Mutations ──────────────────────────────────────────────────────────────── + +export const SuccessSchema = z.object({ success: z.literal(true) }); +export const HardDeleteSuccessSchema = z.object({ + success: z.literal(true), + purged: z.literal(true), +}); +export const CatalogUpdateSchema = z.object({ id: z.number(), name: z.string() }); + +// ─── Analytics — Platform ───────────────────────────────────────────────────── + +export const GrowthPointSchema = z.object({ + period: z.string(), + users: z.number(), + packs: z.number(), + catalogItems: z.number(), +}); + +export const ActivityPointSchema = z.object({ + period: z.string(), + trips: z.number(), + trailReports: z.number(), + posts: z.number(), +}); + +export const ActiveUsersSchema = z.object({ + dau: z.number(), + wau: z.number(), + mau: z.number(), +}); + +export const BreakdownItemSchema = z.object({ category: z.string(), count: z.number() }); + +// ─── Analytics — Catalog ───────────────────────────────────────────────────── + +export const CatalogOverviewSchema = z.object({ + totalItems: z.number(), + totalBrands: z.number(), + avgPrice: z.number().nullable(), + minPrice: z.number().nullable(), + maxPrice: z.number().nullable(), + embeddingCoverage: z.object({ total: z.number(), withEmbedding: z.number(), pct: z.number() }), + availability: z.array(z.object({ status: z.string().nullable(), count: z.number() })), + addedLast30Days: z.number(), +}); + +export const BrandRowSchema = z.object({ + brand: z.string(), + itemCount: z.number(), + avgPrice: z.number().nullable(), + minPrice: z.number().nullable(), + maxPrice: z.number().nullable(), + avgRating: z.number().nullable(), +}); + +export const PriceBucketSchema = z.object({ bucket: z.string(), count: z.number() }); + +export const EtlJobSchema = z.object({ + id: z.string(), + status: z.union([z.literal('running'), z.literal('completed'), z.literal('failed')]), + source: z.string(), + filename: z.string(), + scraperRevision: z.string(), + startedAt: z.string(), + completedAt: z.string().nullable(), + totalProcessed: z.number().nullable(), + totalValid: z.number().nullable(), + totalInvalid: z.number().nullable(), + successRate: z.number().nullable(), +}); + +export const EtlResponseSchema = z.object({ + jobs: z.array(EtlJobSchema), + summary: z.object({ + totalRuns: z.number(), + completed: z.number(), + failed: z.number(), + totalItemsIngested: z.number(), + }), +}); + +export const EmbeddingStatsSchema = z.object({ + total: z.number(), + withEmbedding: z.number(), + pending: z.number(), + coveragePct: z.number(), +}); + +const EtlErrorRowSchema = z.object({ field: z.string(), reason: z.string(), count: z.number() }); + +export const EtlFailureSummarySchema = z.object({ + topErrors: z.array(EtlErrorRowSchema), + totalInvalidItems: z.number(), +}); + +export const EtlJobFailuresSchema = z.object({ + jobId: z.string(), + errorBreakdown: z.array(EtlErrorRowSchema), + samples: z.array( + z.object({ + rowIndex: z.number(), + errors: z.array( + z.object({ + field: z.string(), + reason: z.string(), + value: z.unknown().optional(), + }), + ), + rawData: z.unknown(), + }), + ), + totalShown: z.number(), +}); + +export const EtlResetStuckSchema = z.object({ reset: z.number(), ids: z.array(z.string()) }); + +export const EtlRetrySchema = z.object({ + success: z.literal(true), + newJobId: z.string(), + objectKey: z.string(), +}); + +// ─── Trails ─────────────────────────────────────────────────────────────────── + +export const TrailSearchItemSchema = z.object({ + osmId: z.string(), + name: z.string().nullable(), + sport: z.string().nullable(), + network: z.string().nullable(), + distance: z.string().nullable(), + difficulty: z.string().nullable(), + description: z.string().nullable(), + bbox: z.unknown().nullable(), +}); + +export const TrailSearchResultSchema = z.object({ + trails: z.array(TrailSearchItemSchema), + hasMore: z.boolean(), + offset: z.number(), + limit: z.number(), +}); + +export const TrailGeometrySchema = z.object({ + osmId: z.string(), + name: z.string().nullable(), + sport: z.string().nullable(), + network: z.string().nullable(), + distance: z.string().nullable(), + difficulty: z.string().nullable(), + description: z.string().nullable(), + geometry: z.unknown().nullable(), +}); + +// Named with "Admin" prefix to avoid collision with the user-facing +// TrailConditionReportSchema in packages/schemas/src/trailConditions.ts. +export const AdminTrailConditionReportSchema = z.object({ + id: z.string(), + trailName: z.string(), + trailRegion: z.string().nullable(), + surface: z.string(), + overallCondition: z.string(), + hazards: z.array(z.string()), + waterCrossings: z.number(), + notes: z.string().nullable(), + deleted: z.boolean(), + deletedAt: z.string().nullable(), + createdAt: z.string(), + userId: z.number(), + userEmail: z.string().nullable(), +}); + +export const TrailConditionsListSchema = paginated(AdminTrailConditionReportSchema); diff --git a/packages/schemas/src/index.ts b/packages/schemas/src/index.ts index 577b959de1..4a37d07d4b 100644 --- a/packages/schemas/src/index.ts +++ b/packages/schemas/src/index.ts @@ -1,3 +1,4 @@ +export * from './admin'; export * from './ai'; export * from './auth'; export * from './catalog'; From 6b574d881c726cb9dd5a8cce3585d5fb0d0e21a4 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 13:18:17 -0600 Subject: [PATCH 42/51] fix: sort apps/admin/package.json --- apps/admin/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/admin/package.json b/apps/admin/package.json index e0651d8984..766d971d75 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -13,9 +13,9 @@ "dependencies": { "@elysiajs/eden": "catalog:", "@packrat/api-client": "workspace:*", - "@packrat/schemas": "workspace:*", "@packrat/app": "workspace:*", "@packrat/guards": "workspace:*", + "@packrat/schemas": "workspace:*", "@packrat/web-ui": "workspace:*", "@radix-ui/react-alert-dialog": "catalog:", "@radix-ui/react-avatar": "catalog:", From 0d84cb62c6fe185dd2f0a90c77561f14ae454eff Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 15:52:11 -0600 Subject: [PATCH 43/51] fix: address CodeRabbit review comments - Extract shared datetimeString helper to packages/schemas/src/utils.ts - Add inferred types to @packrat/schemas/admin so consumers import types directly - Clean up apps/admin/lib/api.ts to re-export from schema types instead of re-inferring - Normalize UserAvatar.tsx to single avatarUrl field; update mockData accordingly - Use typed cast in compute-pack.test.ts instead of `as any` - Remove unused with:{ pack/catalogItem } eager loads in trips and packs routes - Return structured 404 with code field in user and weather routes --- apps/admin/lib/api.ts | 71 ++++++++----------- apps/expo/components/initial/UserAvatar.tsx | 4 +- apps/expo/data/mockData.ts | 22 +++--- .../lib/utils/__tests__/compute-pack.test.ts | 2 +- packages/api/src/routes/packs/index.ts | 2 +- packages/api/src/routes/trips/index.ts | 11 +-- packages/api/src/routes/user/index.ts | 11 +-- packages/api/src/routes/weather.ts | 2 +- packages/schemas/src/admin.ts | 23 ++++++ packages/schemas/src/catalog.ts | 7 +- packages/schemas/src/index.ts | 1 + packages/schemas/src/packs.ts | 7 +- packages/schemas/src/utils.ts | 7 ++ 13 files changed, 87 insertions(+), 83 deletions(-) create mode 100644 packages/schemas/src/utils.ts diff --git a/apps/admin/lib/api.ts b/apps/admin/lib/api.ts index 48c47051a0..4b0b4db8f5 100644 --- a/apps/admin/lib/api.ts +++ b/apps/admin/lib/api.ts @@ -2,27 +2,27 @@ import { treaty } from '@elysiajs/eden'; import type { App } from '@packrat/api'; import { isObject } from '@packrat/guards'; import type { - ActiveUsersSchema, - ActivityPointSchema, - AdminCatalogItemSchema, - AdminPackItemSchema, - AdminTrailConditionReportSchema, - AdminUserItemSchema, - BrandRowSchema, - BreakdownItemSchema, - CatalogOverviewSchema, - EmbeddingStatsSchema, - EtlFailureSummarySchema, - EtlJobFailuresSchema, - EtlJobSchema, - EtlResponseSchema, - GrowthPointSchema, - PriceBucketSchema, - TrailGeometrySchema, - TrailSearchItemSchema, - TrailSearchResultSchema, + ActiveUsers, + ActivityPoint, + AdminCatalogItem, + AdminPackItem, + AdminStats, + AdminTrailConditionReport, + AdminUserItem, + BrandRow, + BreakdownItem, + CatalogOverview, + EmbeddingStats, + EtlFailureSummary, + EtlJob, + EtlJobFailures, + EtlResponse, + GrowthPoint, + PriceBucket, + TrailGeometry, + TrailSearchItem, + TrailSearchResult as TrailSearchResultList, } from '@packrat/schemas/admin'; -import type { z } from 'zod'; import { clearToken, getAuthHeader } from './auth'; import { adminEnv } from './env'; @@ -64,7 +64,7 @@ function unwrap(data: T | null | undefined, name: string): T { // ─── Stats ──────────────────────────────────────────────────────────────────── -export type AdminStats = { users: number; packs: number; items: number }; +export type { AdminStats }; export async function getStats(): Promise { const { data, error } = await adminClient.stats.get(); @@ -74,7 +74,7 @@ export async function getStats(): Promise { // ─── Users ──────────────────────────────────────────────────────────────────── -export type AdminUser = z.infer; +export type AdminUser = AdminUserItem; export interface PaginatedResponse { data: T[]; @@ -124,7 +124,7 @@ export async function restoreUser(id: string): Promise<{ success: boolean }> { // ─── Packs ──────────────────────────────────────────────────────────────────── -export type AdminPack = z.infer; +export type AdminPack = AdminPackItem; export async function getPacks({ limit = 100, @@ -152,7 +152,7 @@ export async function deletePack(id: string): Promise<{ success: boolean }> { // ─── Catalog Items ──────────────────────────────────────────────────────────── -export type AdminCatalogItem = z.infer; +export type { AdminCatalogItem }; export interface UpdateCatalogItemInput { name?: string; @@ -197,10 +197,7 @@ export async function updateCatalogItem( // ─── Analytics — Platform ───────────────────────────────────────────────────── -export type GrowthPoint = z.infer; -export type ActivityPoint = z.infer; -export type BreakdownItem = z.infer; -export type ActiveUsers = z.infer; +export type { GrowthPoint, ActivityPoint, BreakdownItem, ActiveUsers }; export type AnalyticsPeriod = 'day' | 'week' | 'month'; export async function getPlatformGrowth( @@ -233,12 +230,7 @@ export async function getPlatformBreakdown(): Promise { // ─── Analytics — Catalog ───────────────────────────────────────────────────── -export type CatalogOverview = z.infer; -export type BrandRow = z.infer; -export type PriceBucket = z.infer; -export type EtlJob = z.infer; -export type EtlResponse = z.infer; -export type EmbeddingStats = z.infer; +export type { CatalogOverview, BrandRow, PriceBucket, EtlJob, EtlResponse, EmbeddingStats }; export async function getCatalogOverview(): Promise { const { data, error } = await adminClient.analytics.catalog.overview.get(); @@ -276,10 +268,10 @@ export async function getCatalogEmbeddings(): Promise { // ─── Admin Trails ───────────────────────────────────────────────────────────── -export type TrailSearchResult = z.infer; -export type TrailGeometry = z.infer; -export type TrailSearchPage = z.infer; -export type TrailConditionReport = z.infer; +export type TrailSearchResult = TrailSearchItem; +export type TrailSearchPage = TrailSearchResultList; +export type { TrailGeometry }; +export type TrailConditionReport = AdminTrailConditionReport; export async function searchTrails({ q, @@ -345,8 +337,7 @@ export function resetStuckEtlJobs(): Promise<{ reset: number; ids: string[] }> { return adminFetch('/analytics/catalog/etl/reset-stuck', { method: 'POST' }); } -export type EtlFailureSummary = z.infer; -export type EtlJobFailures = z.infer; +export type { EtlFailureSummary, EtlJobFailures }; export function getEtlFailureSummary(limit = 20): Promise { return adminFetch(`/analytics/catalog/etl/failure-summary?limit=${limit}`); diff --git a/apps/expo/components/initial/UserAvatar.tsx b/apps/expo/components/initial/UserAvatar.tsx index dd070a664f..15ca32dfb5 100644 --- a/apps/expo/components/initial/UserAvatar.tsx +++ b/apps/expo/components/initial/UserAvatar.tsx @@ -2,8 +2,6 @@ import { Image, Text, View } from 'react-native'; type UserLike = { name: string; - avatar?: string | null; - image?: string | null; avatarUrl?: string | null; }; @@ -26,7 +24,7 @@ export function UserAvatar({ user, size = 'md', showName = false }: UserAvatarPr lg: 'text-base', }[size]; - const avatarUri = user.avatar ?? user.image ?? user.avatarUrl; + const avatarUri = user.avatarUrl || null; return ( diff --git a/apps/expo/data/mockData.ts b/apps/expo/data/mockData.ts index 8f8be24b68..1dcd6fff43 100644 --- a/apps/expo/data/mockData.ts +++ b/apps/expo/data/mockData.ts @@ -2,7 +2,7 @@ export type MockUser = { id: string; name: string; email: string; - avatar: string; + avatarUrl: string; experience: string; joinedAt: string; bio: string; @@ -14,7 +14,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '1', name: 'Alex Hiker', email: 'alex@example.com', - avatar: 'https://i.pravatar.cc/150?img=1', + avatarUrl: 'https://i.pravatar.cc/150?img=1', experience: 'expert', joinedAt: '2023-01-15T00:00:00.000Z', bio: 'Thru-hiker with 5,000+ miles under my feet. PCT, AT, and CDT completed.', @@ -23,7 +23,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '2', name: 'Sam Backpacker', email: 'sam@example.com', - avatar: 'https://i.pravatar.cc/150?img=2', + avatarUrl: 'https://i.pravatar.cc/150?img=2', experience: 'intermediate', joinedAt: '2023-03-22T00:00:00.000Z', bio: 'Weekend warrior trying to lighten my load.', @@ -32,7 +32,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '3', name: 'Jamie Newbie', email: 'jamie@example.com', - avatar: 'https://i.pravatar.cc/150?img=3', + avatarUrl: 'https://i.pravatar.cc/150?img=3', experience: 'beginner', joinedAt: '2023-06-10T00:00:00.000Z', bio: 'Just getting started with hiking and camping.', @@ -41,7 +41,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '4', name: 'Taylor Trailblazer', email: 'taylor@example.com', - avatar: 'https://i.pravatar.cc/150?img=4', + avatarUrl: 'https://i.pravatar.cc/150?img=4', experience: 'expert', joinedAt: '2022-11-05T00:00:00.000Z', bio: 'Explorer with a passion for the unbeaten path.', @@ -50,7 +50,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '5', name: 'Chris Camper', email: 'chris@example.com', - avatar: 'https://i.pravatar.cc/150?img=5', + avatarUrl: 'https://i.pravatar.cc/150?img=5', experience: 'intermediate', joinedAt: '2023-02-28T00:00:00.000Z', bio: 'Camping enthusiast and nature lover.', @@ -59,7 +59,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '6', name: 'Morgan Mountaineer', email: 'morgan@example.com', - avatar: 'https://i.pravatar.cc/150?img=6', + avatarUrl: 'https://i.pravatar.cc/150?img=6', experience: 'expert', joinedAt: '2023-01-20T00:00:00.000Z', bio: 'Scaling peaks and chasing horizons.', @@ -68,7 +68,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '7', name: 'Jordan Explorer', email: 'jordan@example.com', - avatar: 'https://i.pravatar.cc/150?img=7', + avatarUrl: 'https://i.pravatar.cc/150?img=7', experience: 'beginner', joinedAt: '2023-04-15T00:00:00.000Z', bio: 'New to outdoor adventures, learning every step.', @@ -77,7 +77,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '8', name: 'Riley Ranger', email: 'riley@example.com', - avatar: 'https://i.pravatar.cc/150?img=8', + avatarUrl: 'https://i.pravatar.cc/150?img=8', experience: 'intermediate', joinedAt: '2023-03-30T00:00:00.000Z', bio: 'Always ready for a spontaneous trip.', @@ -86,7 +86,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '9', name: 'Casey Climber', email: 'casey@example.com', - avatar: 'https://i.pravatar.cc/150?img=9', + avatarUrl: 'https://i.pravatar.cc/150?img=9', experience: 'expert', joinedAt: '2022-12-10T00:00:00.000Z', bio: 'Rock climbing is life.', @@ -95,7 +95,7 @@ export const mockUsers: [MockUser, ...MockUser[]] = [ id: '10', name: 'Peyton Paddler', email: 'peyton@example.com', - avatar: 'https://i.pravatar.cc/150?img=10', + avatarUrl: 'https://i.pravatar.cc/150?img=10', experience: 'intermediate', joinedAt: '2023-05-01T00:00:00.000Z', bio: 'Loves exploring rivers and lakes.', diff --git a/apps/expo/lib/utils/__tests__/compute-pack.test.ts b/apps/expo/lib/utils/__tests__/compute-pack.test.ts index fb881e4ee9..903b3ac8d9 100644 --- a/apps/expo/lib/utils/__tests__/compute-pack.test.ts +++ b/apps/expo/lib/utils/__tests__/compute-pack.test.ts @@ -68,7 +68,7 @@ describe('computePackWeights', () => { it('throws when items property is null/undefined', () => { const pack = makePack(); // Force missing items - (pack as any).items = undefined; + (pack as unknown as { items: undefined }).items = undefined; expect(() => computePackWeights(pack)).toThrow('Pack with ID pack-1 has no items'); }); diff --git a/packages/api/src/routes/packs/index.ts b/packages/api/src/routes/packs/index.ts index 8425e453a8..313d5c85e0 100644 --- a/packages/api/src/routes/packs/index.ts +++ b/packages/api/src/routes/packs/index.ts @@ -671,7 +671,7 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s const db = createDb(); const item = await db.query.packItems.findFirst({ where: eq(packItems.id, params.itemId), - with: { catalogItem: true, pack: true }, + with: { pack: true }, }); if (!item) throw new NotFoundError('Item not found'); diff --git a/packages/api/src/routes/trips/index.ts b/packages/api/src/routes/trips/index.ts index c93d9c996d..3960e28fe3 100644 --- a/packages/api/src/routes/trips/index.ts +++ b/packages/api/src/routes/trips/index.ts @@ -18,7 +18,6 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) try { const allTrips = await db.query.trips.findMany({ where: and(eq(trips.userId, user.userId), eq(trips.deleted, false)), - with: { pack: true }, orderBy: (t) => t.createdAt, }); @@ -67,14 +66,7 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) if (!newTrip) throw new Error('Failed to create trip'); - const tripWithPack = data.packId - ? await db.query.trips.findFirst({ - where: eq(trips.id, newTrip.id), - with: { pack: true }, - }) - : newTrip; - - return TripSchema.parse(tripWithPack ?? newTrip); + return TripSchema.parse(newTrip); } catch (error) { console.error('Error creating trip:', error); throw error; @@ -101,7 +93,6 @@ export const tripsRoutes = new Elysia({ prefix: '/trips' }) const trip = await db.query.trips.findFirst({ where: and(eq(trips.id, tripId), eq(trips.userId, user.userId)), - with: { pack: true }, }); if (!trip) throw new NotFoundError('Trip not found'); return TripSchema.parse(trip); diff --git a/packages/api/src/routes/user/index.ts b/packages/api/src/routes/user/index.ts index 9189709139..2f26285b54 100644 --- a/packages/api/src/routes/user/index.ts +++ b/packages/api/src/routes/user/index.ts @@ -7,7 +7,10 @@ import { UserProfileSchema, } from '@packrat/schemas/users'; import { eq } from 'drizzle-orm'; -import { Elysia, NotFoundError, status } from 'elysia'; +import { Elysia, status } from 'elysia'; +import { z } from 'zod'; + +const UserNotFoundSchema = z.object({ error: z.string(), code: z.string() }); export const userRoutes = new Elysia({ prefix: '/user' }) .use(authPlugin) @@ -35,7 +38,7 @@ export const userRoutes = new Elysia({ prefix: '/user' }) .limit(1); if (!userRecord) { - throw new NotFoundError('User not found'); + return status(404, { error: 'User not found', code: 'USER_NOT_FOUND' }); } return UserProfileSchema.parse({ @@ -52,7 +55,7 @@ export const userRoutes = new Elysia({ prefix: '/user' }) } }, { - response: { 200: UserProfileSchema }, + response: { 200: UserProfileSchema, 404: UserNotFoundSchema }, isAuthenticated: true, detail: { tags: ['Users'], summary: 'Get user profile', security: [{ bearerAuth: [] }] }, }, @@ -96,7 +99,7 @@ export const userRoutes = new Elysia({ prefix: '/user' }) .returning(); if (!updatedUser) { - throw new NotFoundError('User not found'); + return status(404, { error: 'User not found', code: 'USER_NOT_FOUND' }); } const message = email diff --git a/packages/api/src/routes/weather.ts b/packages/api/src/routes/weather.ts index 7752d1a9bd..c6c5af2db1 100644 --- a/packages/api/src/routes/weather.ts +++ b/packages/api/src/routes/weather.ts @@ -154,7 +154,7 @@ export const weatherRoutes = new Elysia({ prefix: '/weather' }) }); } catch (error) { console.error('Error fetching weather forecast:', error); - throw error; + return status(500, { error: 'Internal server error', code: 'WEATHER_FORECAST_ERROR' }); } }, { diff --git a/packages/schemas/src/admin.ts b/packages/schemas/src/admin.ts index 07487e9fa5..c38b3ed0c5 100644 --- a/packages/schemas/src/admin.ts +++ b/packages/schemas/src/admin.ts @@ -24,6 +24,7 @@ export const AdminStatsSchema = z.object({ packs: z.number(), items: z.number(), }); +export type AdminStats = z.infer; // ─── Users ──────────────────────────────────────────────────────────────────── @@ -38,6 +39,7 @@ export const AdminUserItemSchema = z.object({ createdAt: z.string().nullable(), updatedAt: z.string().nullable(), }); +export type AdminUserItem = z.infer; // ─── Packs ──────────────────────────────────────────────────────────────────── @@ -54,6 +56,7 @@ export const AdminPackItemSchema = z.object({ updatedAt: z.string().nullable(), userEmail: z.string().nullable(), }); +export type AdminPackItem = z.infer; // ─── Catalog ───────────────────────────────────────────────────────────────── @@ -97,6 +100,7 @@ const paginated = (item: T) => export const AdminUsersListSchema = paginated(AdminUserItemSchema); export const AdminPacksListSchema = paginated(AdminPackItemSchema); export const AdminCatalogListSchema = paginated(AdminCatalogItemSchema); +export type AdminCatalogItem = z.infer; // ─── Mutations ──────────────────────────────────────────────────────────────── @@ -115,6 +119,7 @@ export const GrowthPointSchema = z.object({ packs: z.number(), catalogItems: z.number(), }); +export type GrowthPoint = z.infer; export const ActivityPointSchema = z.object({ period: z.string(), @@ -270,3 +275,21 @@ export const AdminTrailConditionReportSchema = z.object({ }); export const TrailConditionsListSchema = paginated(AdminTrailConditionReportSchema); + +// ─── Inferred types ─────────────────────────────────────────────────────────── + +export type ActivityPoint = z.infer; +export type BreakdownItem = z.infer; +export type ActiveUsers = z.infer; +export type CatalogOverview = z.infer; +export type BrandRow = z.infer; +export type PriceBucket = z.infer; +export type EtlJob = z.infer; +export type EtlResponse = z.infer; +export type EmbeddingStats = z.infer; +export type EtlFailureSummary = z.infer; +export type EtlJobFailures = z.infer; +export type TrailSearchItem = z.infer; +export type TrailSearchResult = z.infer; +export type TrailGeometry = z.infer; +export type AdminTrailConditionReport = z.infer; diff --git a/packages/schemas/src/catalog.ts b/packages/schemas/src/catalog.ts index f692781f2a..03ee4e0be0 100644 --- a/packages/schemas/src/catalog.ts +++ b/packages/schemas/src/catalog.ts @@ -1,12 +1,7 @@ import { isString } from '@packrat/guards'; import { z } from 'zod'; import { WEIGHT_UNITS } from './constants'; - -// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); +import { datetimeString } from './utils'; export const CatalogItemSchema = z.object({ id: z.number().int().positive(), diff --git a/packages/schemas/src/index.ts b/packages/schemas/src/index.ts index 4a37d07d4b..8430a96b10 100644 --- a/packages/schemas/src/index.ts +++ b/packages/schemas/src/index.ts @@ -15,5 +15,6 @@ export * from './trailConditions'; export * from './trips'; export * from './upload'; export * from './users'; +export * from './utils'; export * from './validation'; export * from './weather'; diff --git a/packages/schemas/src/packs.ts b/packages/schemas/src/packs.ts index f56b3e9b7c..d848a15465 100644 --- a/packages/schemas/src/packs.ts +++ b/packages/schemas/src/packs.ts @@ -1,11 +1,6 @@ import { z } from 'zod'; import { PACK_CATEGORIES, WEIGHT_UNITS } from './constants'; - -// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); +import { datetimeString } from './utils'; export const PackItemSchema = z.object({ id: z.string(), diff --git a/packages/schemas/src/utils.ts b/packages/schemas/src/utils.ts new file mode 100644 index 0000000000..147fe47eae --- /dev/null +++ b/packages/schemas/src/utils.ts @@ -0,0 +1,7 @@ +import { z } from 'zod'; + +// Accepts Date objects from Drizzle at runtime and coerces to ISO string for the wire. +export const datetimeString = z.preprocess( + (v) => (v instanceof Date ? v.toISOString() : v), + z.string().datetime(), +); From 1b17b8302fb4ae84990e54627935235d87442173 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 15:56:06 -0600 Subject: [PATCH 44/51] refactor: delete env.ts shim, export Env alias from env-validation directly - Add `export type Env = ValidatedEnv` to env-validation.ts - Delete packages/api/src/types/env.ts (was a pure re-export shim) - Update all 13 consumers to import Env from @packrat/api/utils/env-validation - Remove ValidationError re-export from etl.ts; consumers import from @packrat/schemas/validation --- packages/api/src/containers/AppContainer.ts | 2 +- packages/api/src/index.ts | 2 +- packages/api/src/services/aiService.ts | 2 +- packages/api/src/services/catalogService.ts | 2 +- packages/api/src/services/embeddingService.ts | 2 +- packages/api/src/services/etl/CatalogItemValidator.ts | 3 ++- packages/api/src/services/etl/processCatalogEtl.ts | 2 +- packages/api/src/services/etl/processLogsBatch.ts | 2 +- packages/api/src/services/etl/processValidItemsBatch.ts | 2 +- packages/api/src/services/etl/queue.ts | 2 +- packages/api/src/services/etl/updateEtlJobProgress.ts | 2 +- packages/api/src/services/r2-bucket.ts | 2 +- packages/api/src/types/env.ts | 1 - packages/api/src/types/etl.ts | 2 -- packages/api/src/utils/ai/logging.ts | 2 +- packages/api/src/utils/ai/provider.ts | 2 +- packages/api/src/utils/env-validation.ts | 2 ++ 17 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 packages/api/src/types/env.ts diff --git a/packages/api/src/containers/AppContainer.ts b/packages/api/src/containers/AppContainer.ts index 85b2892592..96c6489e65 100644 --- a/packages/api/src/containers/AppContainer.ts +++ b/packages/api/src/containers/AppContainer.ts @@ -1,6 +1,6 @@ import { env } from 'cloudflare:workers'; import { Container } from '@cloudflare/containers'; -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; const typedEnv = env as unknown as Env; // safe-cast: Cloudflare Durable Object constructor — module-level env from 'cloudflare:workers' is injected by the runtime with the correct Env shape diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index baeb3fd4b6..8b8afb651c 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -13,7 +13,7 @@ import { AppContainer } from '@packrat/api/containers'; import { routes } from '@packrat/api/routes'; import { CatalogService } from '@packrat/api/services'; import { processQueueBatch } from '@packrat/api/services/etl/queue'; -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; import { getEnv, setWorkerEnv } from '@packrat/api/utils/env-validation'; import { packratOpenApi } from '@packrat/api/utils/openapi'; import { Elysia } from 'elysia'; diff --git a/packages/api/src/services/aiService.ts b/packages/api/src/services/aiService.ts index ac000542a5..c2093f2bce 100644 --- a/packages/api/src/services/aiService.ts +++ b/packages/api/src/services/aiService.ts @@ -1,6 +1,6 @@ import { createPerplexity } from '@ai-sdk/perplexity'; -import type { Env } from '@packrat/api/types/env'; import { DEFAULT_MODELS } from '@packrat/api/utils/ai/models'; +import type { Env } from '@packrat/api/utils/env-validation'; import { getEnv } from '@packrat/api/utils/env-validation'; import { isFunction } from '@packrat/guards'; import { generateText } from 'ai'; diff --git a/packages/api/src/services/catalogService.ts b/packages/api/src/services/catalogService.ts index 6fe722ab70..30d5c9c96b 100644 --- a/packages/api/src/services/catalogService.ts +++ b/packages/api/src/services/catalogService.ts @@ -1,6 +1,6 @@ import { createDb, createDbClient } from '@packrat/api/db'; import { generateEmbedding, generateManyEmbeddings } from '@packrat/api/services/embeddingService'; -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; import { getEnv } from '@packrat/api/utils/env-validation'; import { type CatalogItem, diff --git a/packages/api/src/services/embeddingService.ts b/packages/api/src/services/embeddingService.ts index 4af4c63a53..dcb738eeee 100644 --- a/packages/api/src/services/embeddingService.ts +++ b/packages/api/src/services/embeddingService.ts @@ -1,6 +1,6 @@ -import type { Env } from '@packrat/api/types/env'; import { DEFAULT_MODELS } from '@packrat/api/utils/ai/models'; import { type AIProvider, createAIProvider } from '@packrat/api/utils/ai/provider'; +import type { Env } from '@packrat/api/utils/env-validation'; import { embed, embedMany } from 'ai'; // ── Embedding text normalization ────────────────────────────────────── diff --git a/packages/api/src/services/etl/CatalogItemValidator.ts b/packages/api/src/services/etl/CatalogItemValidator.ts index eba2a49ea8..b700d74120 100644 --- a/packages/api/src/services/etl/CatalogItemValidator.ts +++ b/packages/api/src/services/etl/CatalogItemValidator.ts @@ -1,6 +1,7 @@ -import type { ValidatedCatalogItem, ValidationError } from '@packrat/api/types/etl'; +import type { ValidatedCatalogItem } from '@packrat/api/types/etl'; import type { NewCatalogItem } from '@packrat/db'; import { isNumber, isString } from '@packrat/guards'; +import type { ValidationError } from '@packrat/schemas/validation'; export class CatalogItemValidator { validateItem(item: Partial): ValidatedCatalogItem { diff --git a/packages/api/src/services/etl/processCatalogEtl.ts b/packages/api/src/services/etl/processCatalogEtl.ts index 1a779a3bcc..77f7b088d4 100644 --- a/packages/api/src/services/etl/processCatalogEtl.ts +++ b/packages/api/src/services/etl/processCatalogEtl.ts @@ -1,6 +1,6 @@ import { createDbClient } from '@packrat/api/db'; -import type { Env } from '@packrat/api/types/env'; import { mapCsvRowToItem } from '@packrat/api/utils/csv-utils'; +import type { Env } from '@packrat/api/utils/env-validation'; import { etlJobs, type NewCatalogItem, type NewInvalidItemLog } from '@packrat/db'; import { parse } from 'csv-parse'; import { eq } from 'drizzle-orm'; diff --git a/packages/api/src/services/etl/processLogsBatch.ts b/packages/api/src/services/etl/processLogsBatch.ts index f98e3e1fbd..cfab66517a 100644 --- a/packages/api/src/services/etl/processLogsBatch.ts +++ b/packages/api/src/services/etl/processLogsBatch.ts @@ -1,4 +1,4 @@ -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; import { invalidItemLogs, type NewInvalidItemLog } from '@packrat/db'; import { createDbClient } from '../../db'; import { updateEtlJobProgress } from './updateEtlJobProgress'; diff --git a/packages/api/src/services/etl/processValidItemsBatch.ts b/packages/api/src/services/etl/processValidItemsBatch.ts index 9ad00297e7..354d777c10 100644 --- a/packages/api/src/services/etl/processValidItemsBatch.ts +++ b/packages/api/src/services/etl/processValidItemsBatch.ts @@ -1,5 +1,5 @@ -import type { Env } from '@packrat/api/types/env'; import { getEmbeddingText } from '@packrat/api/utils/embeddingHelper'; +import type { Env } from '@packrat/api/utils/env-validation'; import type { NewCatalogItem } from '@packrat/db'; import { CatalogService } from '../catalogService'; import { generateManyEmbeddings } from '../embeddingService'; diff --git a/packages/api/src/services/etl/queue.ts b/packages/api/src/services/etl/queue.ts index cbb4730c29..94c0ede465 100644 --- a/packages/api/src/services/etl/queue.ts +++ b/packages/api/src/services/etl/queue.ts @@ -1,5 +1,5 @@ import type { MessageBatch, Queue } from '@cloudflare/workers-types'; -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; import { processCatalogETL } from './processCatalogEtl'; import type { CatalogETLMessage } from './types'; diff --git a/packages/api/src/services/etl/updateEtlJobProgress.ts b/packages/api/src/services/etl/updateEtlJobProgress.ts index cc7aa7ba84..2f1d3257c2 100644 --- a/packages/api/src/services/etl/updateEtlJobProgress.ts +++ b/packages/api/src/services/etl/updateEtlJobProgress.ts @@ -1,5 +1,5 @@ import { createDbClient } from '@packrat/api/db'; -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; import { etlJobs } from '@packrat/db'; import { eq, sql } from 'drizzle-orm'; diff --git a/packages/api/src/services/r2-bucket.ts b/packages/api/src/services/r2-bucket.ts index 1a7c0fb624..6349776c08 100644 --- a/packages/api/src/services/r2-bucket.ts +++ b/packages/api/src/services/r2-bucket.ts @@ -12,7 +12,7 @@ import { S3Client, UploadPartCommand, } from '@aws-sdk/client-s3'; -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; import { isDate, isFunction, isNumber, isObject, isString } from '@packrat/guards'; // ── ETag normalization ──────────────────────────────────────────────── diff --git a/packages/api/src/types/env.ts b/packages/api/src/types/env.ts deleted file mode 100644 index d84c25ecad..0000000000 --- a/packages/api/src/types/env.ts +++ /dev/null @@ -1 +0,0 @@ -export type { ValidatedEnv as Env } from '@packrat/api/utils/env-validation'; diff --git a/packages/api/src/types/etl.ts b/packages/api/src/types/etl.ts index 4e23f2ba2e..5e588cc69e 100644 --- a/packages/api/src/types/etl.ts +++ b/packages/api/src/types/etl.ts @@ -1,8 +1,6 @@ import type { NewCatalogItem } from '@packrat/db'; import type { ValidationError } from '@packrat/schemas/validation'; -export type { ValidationError } from '@packrat/schemas/validation'; - export interface ValidatedCatalogItem { item: Partial; isValid: boolean; diff --git a/packages/api/src/utils/ai/logging.ts b/packages/api/src/utils/ai/logging.ts index a93e57b87d..1e22af7b8e 100644 --- a/packages/api/src/utils/ai/logging.ts +++ b/packages/api/src/utils/ai/logging.ts @@ -1,4 +1,4 @@ -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; import { extractCloudflareLogId } from './provider'; export interface AIRequestLog { diff --git a/packages/api/src/utils/ai/provider.ts b/packages/api/src/utils/ai/provider.ts index c7d4cfcccb..476fa4a5b0 100644 --- a/packages/api/src/utils/ai/provider.ts +++ b/packages/api/src/utils/ai/provider.ts @@ -1,6 +1,6 @@ import type { OpenAIProvider } from '@ai-sdk/openai'; import { createOpenAI } from '@ai-sdk/openai'; -import type { Env } from '@packrat/api/types/env'; +import type { Env } from '@packrat/api/utils/env-validation'; // import type { createWorkersAI } from 'workers-ai-provider'; export type AIProvider = 'openai' | 'cloudflare-workers-ai'; diff --git a/packages/api/src/utils/env-validation.ts b/packages/api/src/utils/env-validation.ts index 93b8cba0d0..b223804554 100644 --- a/packages/api/src/utils/env-validation.ts +++ b/packages/api/src/utils/env-validation.ts @@ -140,6 +140,8 @@ export type ValidatedEnv = Omit< AUTH_KV: KVNamespace; }; +export type Env = ValidatedEnv; + // Cache for validated envs keyed by the raw env reference. const envCache = new WeakMap(); From 7b7ae2c074260ba9358e38eba706f501b1e24676 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 16:00:22 -0600 Subject: [PATCH 45/51] fix: UserAvatar uses Pick, correct userId string type, remove dead middleware aliases - UserAvatar prop type uses Pick instead of local duplicate - variables.ts: userId was number, should be string (users.id is string via Better Auth) - Delete adminMiddleware.ts and apiKeyAuth.ts (re-export alias files with no importers) --- apps/expo/components/initial/UserAvatar.tsx | 8 ++------ packages/api/src/middleware/adminMiddleware.ts | 1 - packages/api/src/middleware/apiKeyAuth.ts | 1 - packages/api/src/types/variables.ts | 2 +- 4 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 packages/api/src/middleware/adminMiddleware.ts delete mode 100644 packages/api/src/middleware/apiKeyAuth.ts diff --git a/apps/expo/components/initial/UserAvatar.tsx b/apps/expo/components/initial/UserAvatar.tsx index 15ca32dfb5..3991d18e16 100644 --- a/apps/expo/components/initial/UserAvatar.tsx +++ b/apps/expo/components/initial/UserAvatar.tsx @@ -1,12 +1,8 @@ +import type { MockUser } from 'expo-app/data/mockData'; import { Image, Text, View } from 'react-native'; -type UserLike = { - name: string; - avatarUrl?: string | null; -}; - type UserAvatarProps = { - user: UserLike; + user: Pick; size?: 'sm' | 'md' | 'lg'; showName?: boolean; }; diff --git a/packages/api/src/middleware/adminMiddleware.ts b/packages/api/src/middleware/adminMiddleware.ts deleted file mode 100644 index 4ff784e907..0000000000 --- a/packages/api/src/middleware/adminMiddleware.ts +++ /dev/null @@ -1 +0,0 @@ -export { adminAuthPlugin as adminMiddleware } from './auth'; diff --git a/packages/api/src/middleware/apiKeyAuth.ts b/packages/api/src/middleware/apiKeyAuth.ts deleted file mode 100644 index f378d8743d..0000000000 --- a/packages/api/src/middleware/apiKeyAuth.ts +++ /dev/null @@ -1 +0,0 @@ -export { apiKeyAuthPlugin as apiKeyAuthMiddleware } from './auth'; diff --git a/packages/api/src/types/variables.ts b/packages/api/src/types/variables.ts index 80d6ade8b9..dad411514b 100644 --- a/packages/api/src/types/variables.ts +++ b/packages/api/src/types/variables.ts @@ -1,6 +1,6 @@ export type Variables = { user: { - userId: number; + userId: string; role: 'USER' | 'ADMIN'; }; }; From 4a07c0a9120e25a4ecc9298d0d02839f4b332173 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 16:16:08 -0600 Subject: [PATCH 46/51] refactor(types): make @packrat/types a pure z.infer layer; fix status code regressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - @packrat/types now re-exports z.infer<> types from @packrat/schemas instead of raw Drizzle InferSelectModel types (wire-safe ISO strings vs Date objects) - Added type exports (Pack, PackItem, PackWithItems, Trip, User, CatalogItem) directly to their schema files - Added PackWithItemsSchema with required items array to packs.ts - Removed re-export block from schemas/constants.ts; packs.ts and catalog.ts now import WEIGHT_UNITS/PACK_CATEGORIES directly from @packrat/constants - Fixed duplicate Trip type export in trips.ts - Fixed status code regressions in packs and catalog routes: throw new Error() → return status(400/500, ...) for client/server errors - Added ApiErrorSchema to affected route response maps so Elysia types resolve - Fixed test fixture: createdAt/updatedAt now use ISO strings not Date objects --- .../lib/utils/__tests__/compute-pack.test.ts | 2 +- packages/api/src/routes/catalog/index.ts | 16 +++--- packages/api/src/routes/packs/index.ts | 12 +++-- packages/schemas/src/catalog.ts | 4 +- packages/schemas/src/constants.ts | 16 +----- packages/schemas/src/packs.ts | 11 ++++- packages/schemas/src/trips.ts | 9 ++-- packages/schemas/src/users.ts | 2 + packages/types/package.json | 3 +- packages/types/src/index.ts | 49 ++----------------- 10 files changed, 43 insertions(+), 81 deletions(-) diff --git a/apps/expo/lib/utils/__tests__/compute-pack.test.ts b/apps/expo/lib/utils/__tests__/compute-pack.test.ts index 903b3ac8d9..811605c56d 100644 --- a/apps/expo/lib/utils/__tests__/compute-pack.test.ts +++ b/apps/expo/lib/utils/__tests__/compute-pack.test.ts @@ -5,7 +5,7 @@ import { computePacksWeights, computePackWeights } from '../compute-pack'; // --------------------------------------------------------------------------- // Minimal factory helpers // --------------------------------------------------------------------------- -const NOW = new Date(); +const NOW = new Date().toISOString(); function makePackItem( overrides: Partial & Pick, diff --git a/packages/api/src/routes/catalog/index.ts b/packages/api/src/routes/catalog/index.ts index a67ce32b45..8743d7124f 100644 --- a/packages/api/src/routes/catalog/index.ts +++ b/packages/api/src/routes/catalog/index.ts @@ -40,6 +40,8 @@ const catalogETLSchema = z.object({ scraperRevision: z.string().min(1, 'Scraper revision ID is required'), }); +const ApiErrorSchema = z.object({ error: z.string() }); + export const catalogRoutes = new Elysia({ prefix: '/catalog' }) .use(authPlugin) .use(apiKeyAuthPlugin) @@ -247,16 +249,16 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) const db = createDb(); const data = body; if (!data.name || data.weight === undefined || data.weight === null || !data.weightUnit) { - throw new Error('name, weight, and weightUnit are required'); + return status(400, { error: 'name, weight, and weightUnit are required' }); } if (data.weight <= 0) { - throw new Error('weight must be a positive number'); + return status(400, { error: 'weight must be a positive number' }); } const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); if (!OPENAI_API_KEY) { - throw new Error('OpenAI API key not configured'); + return status(500, { error: 'OpenAI API key not configured' }); } const embeddingText = getEmbeddingText(data); @@ -303,7 +305,7 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) }, { body: CreateCatalogItemRequestSchema, - response: { 200: CatalogItemSchema }, + response: { 200: CatalogItemSchema, 400: ApiErrorSchema, 500: ApiErrorSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], @@ -440,13 +442,13 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) } const data = body; if (data.weight !== undefined && data.weight !== null && data.weight <= 0) { - throw new Error('weight must be a positive number'); + return status(400, { error: 'weight must be a positive number' }); } const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); if (!OPENAI_API_KEY) { - throw new Error('OpenAI API key not configured'); + return status(500, { error: 'OpenAI API key not configured' }); } const existingItem = await db.query.catalogItems.findFirst({ @@ -487,7 +489,7 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) { params: z.object({ id: z.string() }), body: UpdateCatalogItemRequestSchema, - response: { 200: CatalogItemSchema }, + response: { 200: CatalogItemSchema, 400: ApiErrorSchema, 500: ApiErrorSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], diff --git a/packages/api/src/routes/packs/index.ts b/packages/api/src/routes/packs/index.ts index 313d5c85e0..85d7c69efb 100644 --- a/packages/api/src/routes/packs/index.ts +++ b/packages/api/src/routes/packs/index.ts @@ -42,6 +42,8 @@ import { import { Elysia, NotFoundError, status } from 'elysia'; import { z } from 'zod'; +const ApiErrorSchema = z.object({ error: z.string() }); + const CreatePackBodySchema = CreatePackRequestSchema.extend({ id: z.string(), localCreatedAt: z.string(), @@ -94,7 +96,7 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) const data = body; const packId = data.id as string; - if (!packId) throw new Error('Pack ID is required'); + if (!packId) return status(400, { error: 'Pack ID is required' }); // Zod validates all fields at runtime; cast through the Standard Schema // inference gap so drizzle's insert accepts the values. @@ -114,14 +116,14 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) } as typeof packs.$inferInsert) .returning(); - if (!newPack) throw new Error('Failed to create pack'); + if (!newPack) return status(500, { error: 'Failed to create pack' }); const packWithItems: PackWithItems = { ...newPack, items: [] }; return PackWithWeightsSchema.parse(computePackWeights(packWithItems)); }, { body: CreatePackBodySchema, - response: { 200: PackWithWeightsSchema }, + response: { 200: PackWithWeightsSchema, 400: ApiErrorSchema, 500: ApiErrorSchema }, isAuthenticated: true, detail: { tags: ['Packs'], summary: 'Create new pack', security: [{ bearerAuth: [] }] }, }, @@ -704,7 +706,7 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s const { OPENAI_API_KEY, AI_PROVIDER, CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_AI_GATEWAY_ID, AI } = getEnv(); - if (!OPENAI_API_KEY) throw new Error('OpenAI API key not configured'); + if (!OPENAI_API_KEY) return status(500, { error: 'OpenAI API key not configured' }); const existingItem = await db.query.packItems.findFirst({ where: and(eq(packItems.id, itemId), eq(packItems.userId, user.userId)), @@ -773,7 +775,7 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s { params: z.object({ itemId: z.string() }), body: UpdatePackItemRequestSchema, - response: { 200: PackItemSchema }, + response: { 200: PackItemSchema, 500: ApiErrorSchema }, isAuthenticated: true, detail: { tags: ['Pack Items'], diff --git a/packages/schemas/src/catalog.ts b/packages/schemas/src/catalog.ts index 03ee4e0be0..6137f6773f 100644 --- a/packages/schemas/src/catalog.ts +++ b/packages/schemas/src/catalog.ts @@ -1,6 +1,6 @@ +import { WEIGHT_UNITS } from '@packrat/constants'; import { isString } from '@packrat/guards'; import { z } from 'zod'; -import { WEIGHT_UNITS } from './constants'; import { datetimeString } from './utils'; export const CatalogItemSchema = z.object({ @@ -110,6 +110,8 @@ const SortSchema = z.object({ order: z.enum(['asc', 'desc']), }); +export type CatalogItem = z.infer; + export const CatalogItemsQuerySchema = z.object({ page: z.coerce.number().int().positive().optional().default(1), limit: z.coerce.number().int().min(1).max(100).optional().default(20), diff --git a/packages/schemas/src/constants.ts b/packages/schemas/src/constants.ts index d90093f6a3..cc4f3efd60 100644 --- a/packages/schemas/src/constants.ts +++ b/packages/schemas/src/constants.ts @@ -1,24 +1,10 @@ -import { z } from 'zod'; - -export { - AVAILABILITY_VALUES, - type Availability, - ITEM_CATEGORIES, - type ItemCategory, - type ItemLink, - type ItemReview, - PACK_CATEGORIES, - type PackCategory, - WEIGHT_UNITS, - type WeightUnit, -} from '@packrat/constants'; - import { AVAILABILITY_VALUES, ITEM_CATEGORIES, PACK_CATEGORIES, WEIGHT_UNITS, } from '@packrat/constants'; +import { z } from 'zod'; export const PackCategorySchema = z.enum(PACK_CATEGORIES); export const ItemCategorySchema = z.enum(ITEM_CATEGORIES); diff --git a/packages/schemas/src/packs.ts b/packages/schemas/src/packs.ts index d848a15465..efa43f7229 100644 --- a/packages/schemas/src/packs.ts +++ b/packages/schemas/src/packs.ts @@ -1,5 +1,5 @@ +import { PACK_CATEGORIES, WEIGHT_UNITS } from '@packrat/constants'; import { z } from 'zod'; -import { PACK_CATEGORIES, WEIGHT_UNITS } from './constants'; import { datetimeString } from './utils'; export const PackItemSchema = z.object({ @@ -43,11 +43,20 @@ export const PackSchema = z.object({ items: z.array(PackItemSchema).optional(), }); +export const PackWithItemsSchema = PackSchema.extend({ + items: z.array(PackItemSchema), +}); + export const PackWithWeightsSchema = PackSchema.extend({ totalWeight: z.number(), baseWeight: z.number(), }); +export type PackItem = z.infer; +export type Pack = z.infer; +export type PackWithItems = z.infer; +export type PackWithWeights = z.infer; + export const CreatePackRequestSchema = z.object({ name: z.string().min(1).max(255), description: z.string().optional(), diff --git a/packages/schemas/src/trips.ts b/packages/schemas/src/trips.ts index 9e4e301dc1..3e63266c05 100644 --- a/packages/schemas/src/trips.ts +++ b/packages/schemas/src/trips.ts @@ -1,9 +1,5 @@ import { z } from 'zod'; - -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); +import { datetimeString } from './utils'; const nullableDateString = z.preprocess( (v) => (v instanceof Date ? v.toISOString() : v), @@ -33,6 +29,8 @@ export const TripSchema = z.object({ updatedAt: datetimeString.optional(), }); +export type Trip = z.infer; + export const CreateTripBodySchema = z.object({ id: z.string(), name: z.string().min(1).max(255), @@ -58,4 +56,3 @@ export const UpdateTripBodySchema = z.object({ }); export type TripLocation = z.infer; -export type Trip = z.infer; diff --git a/packages/schemas/src/users.ts b/packages/schemas/src/users.ts index a1c1f15bba..3c227220fd 100644 --- a/packages/schemas/src/users.ts +++ b/packages/schemas/src/users.ts @@ -19,6 +19,8 @@ export const UserProfileSchema = z.object({ user: UserSchema, }); +export type User = z.infer; + // Update user request schema export const UpdateUserRequestSchema = z.object({ firstName: z.string().optional(), diff --git a/packages/types/package.json b/packages/types/package.json index f59daf7bd1..236c2df0a4 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -12,7 +12,8 @@ "check-types": "tsc --noEmit" }, "dependencies": { - "@packrat/db": "workspace:*" + "@packrat/constants": "workspace:*", + "@packrat/schemas": "workspace:*" }, "devDependencies": { "typescript": "catalog:" diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index b91301d644..ef145c9afb 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,6 +1,3 @@ -// Model types from @packrat/db - -// Constant types from @packrat/constants export type { Availability, ItemCategory, @@ -9,44 +6,8 @@ export type { PackCategory, WeightUnit, } from '@packrat/constants'; -export type { - Account, - CatalogItem, - CommentLike, - ETLJob, - InvalidItemLog, - Jwks, - NewAccount, - NewCatalogItem, - NewCommentLike, - NewETLJob, - NewInvalidItemLog, - NewJwks, - NewPack, - NewPackItem, - NewPackTemplate, - NewPackTemplateItem, - NewPost, - NewPostComment, - NewPostLike, - NewReportedContent, - NewSession, - NewTrip, - NewUser, - NewVerification, - Pack, - PackItem, - PackTemplate, - PackTemplateItem, - PackTemplateWithItems, - PackWithItems, - Post, - PostComment, - PostLike, - ReportedContent, - Session, - TrailConditionReport, - Trip, - User, - Verification, -} from '@packrat/db'; +export type { CatalogItem } from '@packrat/schemas/catalog'; +export type { Pack, PackItem, PackWithItems, PackWithWeights } from '@packrat/schemas/packs'; +export type { TrailConditionReport } from '@packrat/schemas/trailConditions'; +export type { Trip } from '@packrat/schemas/trips'; +export type { User } from '@packrat/schemas/users'; From 41e287c59de2c7ef64057129b7b5515be92ec7fe Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Thu, 14 May 2026 16:31:10 -0600 Subject: [PATCH 47/51] refactor(schemas): centralize all route/service schemas in @packrat/schemas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every named Zod schema now lives in packages/schemas/src/ — route and service files only import, never define. New schema files: - packages/schemas/src/trails.ts — OsmMemberSchema, RouteBaseRowSchema, RouteSearchRowSchema, RouteDetailRowSchema - packages/schemas/src/wildlife.ts — WildlifeIdentifyRequestSchema Schema additions to existing files: - trailConditions: CreateTrailConditionReportRequestSchema, UpdateTrailConditionReportRequestSchema - admin: AnalyticsPeriodSchema - catalog: CatalogETLSchema - packs: AddPackItemBodySchema - packTemplates: AIPackAnalysisSchema, AIPackAnalysisItemSchema; replace local datetimeString with import from ./utils Route/service cleanup: - All top-level const XxxSchema = z. removed from routes/ and services/ - 10 route files and services/trails.ts updated to import from @packrat/schemas Enforcement: - packages/checks/src/check-route-schemas.ts — new script that fails if any named schema is defined at module level in routes/ or services/ (4 internal AI-parsing files allowlisted) - Wired as check:route-schemas:strict in pre-push clean-checks --- lefthook.yml | 1 + .../src/routes/admin/analytics/platform.ts | 10 +- packages/api/src/routes/admin/trails.ts | 12 +-- packages/api/src/routes/catalog/index.ts | 17 +--- .../api/src/routes/packTemplates/index.ts | 30 +----- packages/api/src/routes/packs/index.ts | 21 +---- .../api/src/routes/trailConditions/reports.ts | 29 ++---- packages/api/src/routes/trails/index.ts | 28 +----- packages/api/src/routes/user/index.ts | 6 +- packages/api/src/routes/wildlife/index.ts | 8 +- packages/api/src/services/trails.ts | 9 +- packages/checks/package.json | 4 +- packages/checks/src/check-route-schemas.ts | 92 +++++++++++++++++++ packages/schemas/src/admin.ts | 5 + packages/schemas/src/catalog.ts | 7 ++ packages/schemas/src/index.ts | 2 + packages/schemas/src/packTemplates.ts | 33 ++++++- packages/schemas/src/packs.ts | 4 + packages/schemas/src/trailConditions.ts | 23 +++++ packages/schemas/src/trails.ts | 30 ++++++ packages/schemas/src/wildlife.ts | 5 + 21 files changed, 229 insertions(+), 147 deletions(-) create mode 100644 packages/checks/src/check-route-schemas.ts create mode 100644 packages/schemas/src/trails.ts create mode 100644 packages/schemas/src/wildlife.ts diff --git a/lefthook.yml b/lefthook.yml index 97c1d3aa56..02dc451700 100644 --- a/lefthook.yml +++ b/lefthook.yml @@ -23,5 +23,6 @@ pre-push: bun scripts/lint/no-duplicate-guards.ts && bun scripts/lint/no-unauth-routes.ts && bun scripts/format/sort-package-json.ts --check && + bun run --cwd packages/checks check:route-schemas:strict && bun check:casts:strict fail_text: "Pre-push checks failed! Run `bun check:all` for the full picture." diff --git a/packages/api/src/routes/admin/analytics/platform.ts b/packages/api/src/routes/admin/analytics/platform.ts index 589ab59497..b08da4c1e1 100644 --- a/packages/api/src/routes/admin/analytics/platform.ts +++ b/packages/api/src/routes/admin/analytics/platform.ts @@ -4,6 +4,7 @@ import { ActiveUsersSchema, ActivityPointSchema, AdminErrorResponses, + AnalyticsPeriodSchema, BreakdownItemSchema, GrowthPointSchema, } from '@packrat/schemas/admin'; @@ -11,11 +12,6 @@ import { and, count, desc, eq, gte, sql } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; -const PeriodSchema = z.object({ - period: z.enum(['day', 'week', 'month']).optional().default('month'), - range: z.coerce.number().int().min(1).max(365).optional().default(12), -}); - function getStartDate(period: 'day' | 'week' | 'month', range: number): Date { const d = new Date(); if (period === 'day') d.setDate(d.getDate() - range); @@ -98,7 +94,7 @@ export const platformAnalyticsRoutes = new Elysia({ prefix: '/platform' }) } }, { - query: PeriodSchema, + query: AnalyticsPeriodSchema, response: { 200: z.array(GrowthPointSchema), ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'Platform growth metrics' }, }, @@ -175,7 +171,7 @@ export const platformAnalyticsRoutes = new Elysia({ prefix: '/platform' }) } }, { - query: PeriodSchema, + query: AnalyticsPeriodSchema, response: { 200: z.array(ActivityPointSchema), ...AdminErrorResponses }, detail: { tags: ['Admin'], summary: 'User activity metrics' }, }, diff --git a/packages/api/src/routes/admin/trails.ts b/packages/api/src/routes/admin/trails.ts index d6d7a870f2..5a845ec045 100644 --- a/packages/api/src/routes/admin/trails.ts +++ b/packages/api/src/routes/admin/trails.ts @@ -8,21 +8,11 @@ import { TrailSearchItemSchema, TrailSearchResultSchema, } from '@packrat/schemas/admin'; +import { RouteSearchRowSchema } from '@packrat/schemas/trails'; import { and, count, desc, eq, ilike, or, sql } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; -const RouteSearchRowSchema = z.object({ - osm_id: z.string(), - name: z.string().nullable(), - sport: z.string().nullable(), - network: z.string().nullable(), - distance: z.string().nullable(), - difficulty: z.string().nullable(), - description: z.string().nullable(), - bbox: z.string().nullable(), -}); - export const adminTrailsRoutes = new Elysia({ prefix: '/trails' }) /** diff --git a/packages/api/src/routes/catalog/index.ts b/packages/api/src/routes/catalog/index.ts index 8743d7124f..1d67c44bf1 100644 --- a/packages/api/src/routes/catalog/index.ts +++ b/packages/api/src/routes/catalog/index.ts @@ -10,6 +10,7 @@ import { catalogItems, etlJobs, packItems } from '@packrat/db'; import { isString } from '@packrat/guards'; import { CatalogCategoriesResponseSchema, + CatalogETLSchema, CatalogItemSchema, CatalogItemsQuerySchema, CatalogItemsResponseSchema, @@ -17,6 +18,7 @@ import { UpdateCatalogItemRequestSchema, VectorSearchQuerySchema, } from '@packrat/schemas/catalog'; +import { ErrorResponseSchema } from '@packrat/schemas/shared'; import { and, cosineDistance, @@ -33,15 +35,6 @@ import { import { Elysia, NotFoundError, status } from 'elysia'; import { z } from 'zod'; -const catalogETLSchema = z.object({ - filename: z.string().min(1, 'Filename is required'), - chunks: z.array(z.string()).min(1, 'At least one object key is required'), - source: z.string().min(1, 'Source name is required'), - scraperRevision: z.string().min(1, 'Scraper revision ID is required'), -}); - -const ApiErrorSchema = z.object({ error: z.string() }); - export const catalogRoutes = new Elysia({ prefix: '/catalog' }) .use(authPlugin) .use(apiKeyAuthPlugin) @@ -219,7 +212,7 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) }; }, { - body: catalogETLSchema, + body: CatalogETLSchema, isValidApiKey: true, detail: { tags: ['Catalog'], @@ -305,7 +298,7 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) }, { body: CreateCatalogItemRequestSchema, - response: { 200: CatalogItemSchema, 400: ApiErrorSchema, 500: ApiErrorSchema }, + response: { 200: CatalogItemSchema, 400: ErrorResponseSchema, 500: ErrorResponseSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], @@ -489,7 +482,7 @@ export const catalogRoutes = new Elysia({ prefix: '/catalog' }) { params: z.object({ id: z.string() }), body: UpdateCatalogItemRequestSchema, - response: { 200: CatalogItemSchema, 400: ApiErrorSchema, 500: ApiErrorSchema }, + response: { 200: CatalogItemSchema, 400: ErrorResponseSchema, 500: ErrorResponseSchema }, isAuthenticated: true, detail: { tags: ['Catalog'], diff --git a/packages/api/src/routes/packTemplates/index.ts b/packages/api/src/routes/packTemplates/index.ts index 2c0cf039e5..6b932e0ee7 100644 --- a/packages/api/src/routes/packTemplates/index.ts +++ b/packages/api/src/routes/packTemplates/index.ts @@ -7,6 +7,7 @@ import { getEnv } from '@packrat/api/utils/env-validation'; import { type PackTemplate, packTemplateItems, packTemplates } from '@packrat/db'; import { assertDefined } from '@packrat/guards'; import { + AIPackAnalysisSchema, CreatePackTemplateItemRequestSchema, CreatePackTemplateRequestSchema, GenerateFromOnlineContentRequestSchema, @@ -107,33 +108,6 @@ function getYouTubeId(url: string): string | null { } } -const analysisSchema = z.object({ - templateName: z.string(), - templateCategory: z.enum([ - 'hiking', - 'backpacking', - 'camping', - 'climbing', - 'winter', - 'desert', - 'custom', - 'water sports', - 'skiing', - ]), - templateDescription: z.string(), - items: z.array( - z.object({ - name: z.string(), - description: z.string(), - quantity: z.number().int().positive().default(1), - category: z.string(), - weightGrams: z.number().nonnegative().default(0), - consumable: z.boolean().default(false), - worn: z.boolean().default(false), - }), - ), -}); - // --------------------------------------------------------------------------- // Routes // --------------------------------------------------------------------------- @@ -313,7 +287,7 @@ export const packTemplatesRoutes = new Elysia({ prefix: '/pack-templates' }) const { object: analysis } = await generateObject({ model: google('gemini-3-flash-preview'), - schema: analysisSchema, + schema: AIPackAnalysisSchema, system: SYSTEM_PROMPT, prompt: [{ role: 'user', content: contentParts }], temperature: 0.2, diff --git a/packages/api/src/routes/packs/index.ts b/packages/api/src/routes/packs/index.ts index 85d7c69efb..0177a91fbe 100644 --- a/packages/api/src/routes/packs/index.ts +++ b/packages/api/src/routes/packs/index.ts @@ -19,14 +19,15 @@ import { } from '@packrat/db'; import { AnalyzeImageRequestSchema } from '@packrat/schemas/imageDetection'; import { - CreatePackItemRequestSchema, - CreatePackRequestSchema, + AddPackItemBodySchema, + CreatePackBodySchema, GapAnalysisRequestSchema, PackItemSchema, PackWithWeightsSchema, UpdatePackItemRequestSchema, UpdatePackRequestSchema, } from '@packrat/schemas/packs'; +import { ErrorResponseSchema } from '@packrat/schemas/shared'; import { and, cosineDistance, @@ -42,18 +43,6 @@ import { import { Elysia, NotFoundError, status } from 'elysia'; import { z } from 'zod'; -const ApiErrorSchema = z.object({ error: z.string() }); - -const CreatePackBodySchema = CreatePackRequestSchema.extend({ - id: z.string(), - localCreatedAt: z.string(), - localUpdatedAt: z.string(), -}); - -const AddPackItemBodySchema = CreatePackItemRequestSchema.extend({ - id: z.string(), -}); - export const packsRoutes = new Elysia({ prefix: '/packs' }) .use(authPlugin) .use(adminAuthPlugin) @@ -123,7 +112,7 @@ export const packsRoutes = new Elysia({ prefix: '/packs' }) }, { body: CreatePackBodySchema, - response: { 200: PackWithWeightsSchema, 400: ApiErrorSchema, 500: ApiErrorSchema }, + response: { 200: PackWithWeightsSchema, 400: ErrorResponseSchema, 500: ErrorResponseSchema }, isAuthenticated: true, detail: { tags: ['Packs'], summary: 'Create new pack', security: [{ bearerAuth: [] }] }, }, @@ -775,7 +764,7 @@ Limit to maximum 6 recommendations, prioritizing the most important gaps. Only s { params: z.object({ itemId: z.string() }), body: UpdatePackItemRequestSchema, - response: { 200: PackItemSchema, 500: ApiErrorSchema }, + response: { 200: PackItemSchema, 500: ErrorResponseSchema }, isAuthenticated: true, detail: { tags: ['Pack Items'], diff --git a/packages/api/src/routes/trailConditions/reports.ts b/packages/api/src/routes/trailConditions/reports.ts index 58dfc683d9..be3556bbec 100644 --- a/packages/api/src/routes/trailConditions/reports.ts +++ b/packages/api/src/routes/trailConditions/reports.ts @@ -2,6 +2,10 @@ import { createDb } from '@packrat/api/db'; import { authPlugin } from '@packrat/api/middleware/auth'; import type { NewTrailConditionReport } from '@packrat/db'; import { trailConditionReports } from '@packrat/db'; +import { + CreateTrailConditionReportRequestSchema, + UpdateTrailConditionReportRequestSchema, +} from '@packrat/schemas/trailConditions'; import { and, desc, eq, gte, ilike, type SQL } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; @@ -11,27 +15,6 @@ const LIKE_ESCAPE_BACKSLASH = /\\/g; const LIKE_ESCAPE_PERCENT = /%/g; const LIKE_ESCAPE_UNDERSCORE = /_/g; -const CreateReportRequestSchema = z.object({ - id: z.string().describe('Client-generated report ID'), - trailName: z.string().min(1), - trailRegion: z.string().optional().nullable(), - surface: z.enum(['paved', 'gravel', 'dirt', 'rocky', 'snow', 'mud']), - overallCondition: z.enum(['excellent', 'good', 'fair', 'poor']), - hazards: z.array(z.string()).optional().default([]), - waterCrossings: z.number().int().min(0).max(20).optional().default(0), - waterCrossingDifficulty: z.enum(['easy', 'moderate', 'difficult']).optional().nullable(), - notes: z.string().optional().nullable(), - photos: z.array(z.string()).optional().default([]), - tripId: z.string().optional().nullable(), - localCreatedAt: z.string().datetime(), - localUpdatedAt: z.string().datetime(), -}); - -const UpdateReportRequestSchema = CreateReportRequestSchema.omit({ - id: true, - localCreatedAt: true, -}).partial(); - function toReportResponse(row: Record): Record { return { ...row, @@ -143,7 +126,7 @@ export const trailConditionRoutes = new Elysia() } }, { - body: CreateReportRequestSchema, + body: CreateTrailConditionReportRequestSchema, isAuthenticated: true, detail: { tags: ['Trail Conditions'], @@ -236,7 +219,7 @@ export const trailConditionRoutes = new Elysia() }, { params: z.object({ reportId: z.string() }), - body: UpdateReportRequestSchema, + body: UpdateTrailConditionReportRequestSchema, isAuthenticated: true, detail: { tags: ['Trail Conditions'], diff --git a/packages/api/src/routes/trails/index.ts b/packages/api/src/routes/trails/index.ts index 9229623482..b33e7a721c 100644 --- a/packages/api/src/routes/trails/index.ts +++ b/packages/api/src/routes/trails/index.ts @@ -1,37 +1,11 @@ import { createOsmDb } from '@packrat/api/db'; import { authPlugin } from '@packrat/api/middleware/auth'; import { stitchRouteGeometry } from '@packrat/api/services/trails'; +import { RouteDetailRowSchema, RouteSearchRowSchema } from '@packrat/schemas/trails'; import { sql } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; import { z } from 'zod'; -// ── Zod schemas ───────────────────────────────────────────────────────────── - -const OsmMemberSchema = z.object({ - type: z.string(), - ref: z.coerce.bigint(), - role: z.string(), -}); - -const RouteBaseRowSchema = z.object({ - osm_id: z.string(), - name: z.string().nullable(), - sport: z.string().nullable(), - network: z.string().nullable(), - distance: z.string().nullable(), - difficulty: z.string().nullable(), - description: z.string().nullable(), -}); - -const RouteSearchRowSchema = RouteBaseRowSchema.extend({ - bbox: z.string().nullable(), -}); - -const RouteDetailRowSchema = RouteBaseRowSchema.extend({ - members: z.array(OsmMemberSchema).nullable(), - geojson: z.string().nullable(), -}); - // ── Routes ───────────────────────────────────────────────────────────────── export const trailsRoutes = new Elysia({ prefix: '/trails' }) diff --git a/packages/api/src/routes/user/index.ts b/packages/api/src/routes/user/index.ts index 2f26285b54..6a097b22e1 100644 --- a/packages/api/src/routes/user/index.ts +++ b/packages/api/src/routes/user/index.ts @@ -1,6 +1,7 @@ import { createDb } from '@packrat/api/db'; import { authPlugin } from '@packrat/api/middleware/auth'; import { users } from '@packrat/db'; +import { ErrorResponseSchema } from '@packrat/schemas/shared'; import { UpdateUserRequestSchema, UpdateUserResponseSchema, @@ -8,9 +9,6 @@ import { } from '@packrat/schemas/users'; import { eq } from 'drizzle-orm'; import { Elysia, status } from 'elysia'; -import { z } from 'zod'; - -const UserNotFoundSchema = z.object({ error: z.string(), code: z.string() }); export const userRoutes = new Elysia({ prefix: '/user' }) .use(authPlugin) @@ -55,7 +53,7 @@ export const userRoutes = new Elysia({ prefix: '/user' }) } }, { - response: { 200: UserProfileSchema, 404: UserNotFoundSchema }, + response: { 200: UserProfileSchema, 404: ErrorResponseSchema }, isAuthenticated: true, detail: { tags: ['Users'], summary: 'Get user profile', security: [{ bearerAuth: [] }] }, }, diff --git a/packages/api/src/routes/wildlife/index.ts b/packages/api/src/routes/wildlife/index.ts index 13af169724..a896af25fd 100644 --- a/packages/api/src/routes/wildlife/index.ts +++ b/packages/api/src/routes/wildlife/index.ts @@ -3,17 +3,13 @@ import { authPlugin } from '@packrat/api/middleware/auth'; import { WildlifeIdentificationService } from '@packrat/api/services/wildlifeIdentificationService'; import { getEnv } from '@packrat/api/utils/env-validation'; import { getPresignedUrl } from '@packrat/api/utils/getPresignedUrl'; +import { WildlifeIdentifyRequestSchema } from '@packrat/schemas/wildlife'; import { Elysia, status } from 'elysia'; -import { z } from 'zod'; // ── Slug normalization patterns ─────────────────────────────────────── const SPACES_AND_DOTS = /[\s.]+/g; const NON_SLUG_CHARS = /[^a-z0-9-]/g; -const IdentifyRequestSchema = z.object({ - image: z.string().describe('Uploaded image key in R2'), -}); - export const wildlifeRoutes = new Elysia({ prefix: '/wildlife' }).use(authPlugin).post( '/identify', async ({ body, user }) => { @@ -93,7 +89,7 @@ export const wildlifeRoutes = new Elysia({ prefix: '/wildlife' }).use(authPlugin return { results }; }, { - body: IdentifyRequestSchema, + body: WildlifeIdentifyRequestSchema, isAuthenticated: true, detail: { tags: ['Wildlife'], diff --git a/packages/api/src/services/trails.ts b/packages/api/src/services/trails.ts index 4dfc8b0cb7..6841220d35 100644 --- a/packages/api/src/services/trails.ts +++ b/packages/api/src/services/trails.ts @@ -1,14 +1,9 @@ import type { createOsmDb } from '@packrat/api/db'; +import type { OsmMember } from '@packrat/schemas/trails'; import { sql } from 'drizzle-orm'; import { z } from 'zod'; -const OsmMemberSchema = z.object({ - type: z.string(), - ref: z.coerce.bigint(), - role: z.string(), -}); - -export type OsmMember = z.infer; +export type { OsmMember }; /** * Stitches a MultiLineString geometry from member way IDs using ST_LineMerge. diff --git a/packages/checks/package.json b/packages/checks/package.json index 10c105548f..726bacb9b4 100644 --- a/packages/checks/package.json +++ b/packages/checks/package.json @@ -6,6 +6,8 @@ "scripts": { "check:casts": "bun ./src/check-type-casts.ts", "check:casts:strict": "bun ./src/check-type-casts.ts --strict", - "check:magic-strings": "bun ./src/check-magic-strings.ts" + "check:magic-strings": "bun ./src/check-magic-strings.ts", + "check:route-schemas": "bun ./src/check-route-schemas.ts", + "check:route-schemas:strict": "bun ./src/check-route-schemas.ts --strict" } } diff --git a/packages/checks/src/check-route-schemas.ts b/packages/checks/src/check-route-schemas.ts new file mode 100644 index 0000000000..8a66d00ca6 --- /dev/null +++ b/packages/checks/src/check-route-schemas.ts @@ -0,0 +1,92 @@ +#!/usr/bin/env bun +/** + * check-route-schemas.ts — enforces that no named Zod schema is defined + * inside packages/api/src/routes/ or packages/api/src/services/. + * + * All schemas must live in @packrat/schemas (packages/schemas/src/). + * Route files must only import — never define. + * + * A "named schema" is a top-level const at column 0: + * const FooSchema = z. ← VIOLATION + * + * Inline anonymous schemas in route config are fine: + * body: z.object({...}) ← OK (indented) + * + * Run: bun ./src/check-route-schemas.ts + * Strict mode: bun ./src/check-route-schemas.ts --strict + */ + +import { readdirSync, readFileSync, statSync } from 'node:fs'; +import { join } from 'node:path'; + +const ROOT = join(import.meta.dir, '..', '..', '..'); +const SCAN_ROOTS = ['packages/api/src/routes', 'packages/api/src/services']; +const EXCLUDED_DIRS = new Set(['node_modules', 'dist', 'build', '__tests__']); +const TARGET_EXTENSIONS = new Set(['.ts', '.tsx']); + +// Top-level named schema: starts at column 0, no leading whitespace +const NAMED_SCHEMA_PATTERN = /^const \w+Schema\s*=\s*z\./; + +// Legitimate exceptions: AI response parsing schemas and auth middleware internals +// that validate private service boundaries, not API wire types. +const ALLOWED_FILES = new Set([ + 'packages/api/src/services/packService.ts', + 'packages/api/src/services/imageDetectionService.ts', + 'packages/api/src/services/wildlifeIdentificationService.ts', + 'packages/api/src/middleware/cfAccess.ts', +]); + +interface Violation { + file: string; + line: number; + text: string; +} + +function collectFiles(dir: string): string[] { + const files: string[] = []; + for (const entry of readdirSync(dir)) { + if (EXCLUDED_DIRS.has(entry)) continue; + const full = join(dir, entry); + const stat = statSync(full); + if (stat.isDirectory()) { + files.push(...collectFiles(full)); + } else if (TARGET_EXTENSIONS.has(entry.slice(entry.lastIndexOf('.')))) { + files.push(full); + } + } + return files; +} + +const violations: Violation[] = []; + +for (const root of SCAN_ROOTS) { + const absRoot = join(ROOT, root); + for (const file of collectFiles(absRoot)) { + const rel = file.slice(ROOT.length + 1); + if (ALLOWED_FILES.has(rel)) continue; + const lines = readFileSync(file, 'utf-8').split('\n'); + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (line && NAMED_SCHEMA_PATTERN.test(line)) { + violations.push({ file: rel, line: i + 1, text: line.trim() }); + } + } + } +} + +if (violations.length === 0) { + console.log('✓ No inline route/service schemas found.'); + process.exit(0); +} + +console.log(`Found ${violations.length} inline schema definition(s) in route/service files.\n`); +console.log('All Zod schemas must live in packages/schemas/src/.\n'); +for (const v of violations) { + console.log(` ${v.file}:${v.line}`); + console.log(` ${v.text}\n`); +} + +const strict = process.argv.includes('--strict'); +if (strict) { + process.exit(1); +} diff --git a/packages/schemas/src/admin.ts b/packages/schemas/src/admin.ts index c38b3ed0c5..e024c0eecf 100644 --- a/packages/schemas/src/admin.ts +++ b/packages/schemas/src/admin.ts @@ -113,6 +113,11 @@ export const CatalogUpdateSchema = z.object({ id: z.number(), name: z.string() } // ─── Analytics — Platform ───────────────────────────────────────────────────── +export const AnalyticsPeriodSchema = z.object({ + period: z.enum(['day', 'week', 'month']).optional().default('month'), + range: z.coerce.number().int().min(1).max(365).optional().default(12), +}); + export const GrowthPointSchema = z.object({ period: z.string(), users: z.number(), diff --git a/packages/schemas/src/catalog.ts b/packages/schemas/src/catalog.ts index 6137f6773f..0b91cafbc8 100644 --- a/packages/schemas/src/catalog.ts +++ b/packages/schemas/src/catalog.ts @@ -328,3 +328,10 @@ export const VectorSearchResponseSchema = z.object({ offset: z.number(), nextOffset: z.number(), }); + +export const CatalogETLSchema = z.object({ + filename: z.string().min(1, 'Filename is required'), + chunks: z.array(z.string()).min(1, 'At least one object key is required'), + source: z.string().min(1, 'Source name is required'), + scraperRevision: z.string().min(1, 'Scraper revision ID is required'), +}); diff --git a/packages/schemas/src/index.ts b/packages/schemas/src/index.ts index 8430a96b10..6d315b0ba2 100644 --- a/packages/schemas/src/index.ts +++ b/packages/schemas/src/index.ts @@ -12,9 +12,11 @@ export * from './packTemplates'; export * from './seasonSuggestions'; export * from './shared'; export * from './trailConditions'; +export * from './trails'; export * from './trips'; export * from './upload'; export * from './users'; export * from './utils'; export * from './validation'; export * from './weather'; +export * from './wildlife'; diff --git a/packages/schemas/src/packTemplates.ts b/packages/schemas/src/packTemplates.ts index 7ff78add00..ed88b58022 100644 --- a/packages/schemas/src/packTemplates.ts +++ b/packages/schemas/src/packTemplates.ts @@ -1,9 +1,5 @@ import { z } from 'zod'; - -const datetimeString = z.preprocess( - (v) => (v instanceof Date ? v.toISOString() : v), - z.string().datetime(), -); +import { datetimeString } from './utils'; export const PackTemplateErrorResponseSchema = z.object({ error: z.string(), @@ -110,3 +106,30 @@ export const GenerateFromOnlineContentRequestSchema = z.object({ }); export const GenerateFromOnlineContentResponseSchema = PackTemplateWithItemsSchema; + +export const AIPackAnalysisItemSchema = z.object({ + name: z.string(), + description: z.string(), + quantity: z.number().int().positive().default(1), + category: z.string(), + weightGrams: z.number().nonnegative().default(0), + consumable: z.boolean().default(false), + worn: z.boolean().default(false), +}); + +export const AIPackAnalysisSchema = z.object({ + templateName: z.string(), + templateCategory: z.enum([ + 'hiking', + 'backpacking', + 'camping', + 'climbing', + 'winter', + 'desert', + 'custom', + 'water sports', + 'skiing', + ]), + templateDescription: z.string(), + items: z.array(AIPackAnalysisItemSchema), +}); diff --git a/packages/schemas/src/packs.ts b/packages/schemas/src/packs.ts index efa43f7229..dfda24ac32 100644 --- a/packages/schemas/src/packs.ts +++ b/packages/schemas/src/packs.ts @@ -155,6 +155,10 @@ export const CreatePackBodySchema = CreatePackRequestSchema.extend({ localUpdatedAt: z.string().datetime(), }); +export const AddPackItemBodySchema = CreatePackItemRequestSchema.extend({ + id: z.string(), +}); + export const UpdatePackBodySchema = UpdatePackRequestSchema.extend({ localUpdatedAt: z.string().datetime().optional(), }); diff --git a/packages/schemas/src/trailConditions.ts b/packages/schemas/src/trailConditions.ts index 1dc4254775..4d3828da30 100644 --- a/packages/schemas/src/trailConditions.ts +++ b/packages/schemas/src/trailConditions.ts @@ -30,3 +30,26 @@ export const TrailConditionReportSchema = z.object({ }); export type TrailConditionReport = z.infer; + +export const CreateTrailConditionReportRequestSchema = z.object({ + id: z.string().describe('Client-generated report ID'), + trailName: z.string().min(1), + trailRegion: z.string().optional().nullable(), + surface: TrailSurfaceSchema, + overallCondition: OverallConditionSchema, + hazards: z.array(z.string()).optional().default([]), + waterCrossings: z.number().int().min(0).max(20).optional().default(0), + waterCrossingDifficulty: WaterCrossingDifficultySchema.optional().nullable(), + notes: z.string().optional().nullable(), + photos: z.array(z.string()).optional().default([]), + tripId: z.string().optional().nullable(), + localCreatedAt: z.string().datetime(), + localUpdatedAt: z.string().datetime(), +}); + +export const UpdateTrailConditionReportRequestSchema = CreateTrailConditionReportRequestSchema.omit( + { + id: true, + localCreatedAt: true, + }, +).partial(); diff --git a/packages/schemas/src/trails.ts b/packages/schemas/src/trails.ts new file mode 100644 index 0000000000..317762edf2 --- /dev/null +++ b/packages/schemas/src/trails.ts @@ -0,0 +1,30 @@ +import { z } from 'zod'; + +export const OsmMemberSchema = z.object({ + type: z.string(), + ref: z.coerce.bigint(), + role: z.string(), +}); + +export const RouteBaseRowSchema = z.object({ + osm_id: z.string(), + name: z.string().nullable(), + sport: z.string().nullable(), + network: z.string().nullable(), + distance: z.string().nullable(), + difficulty: z.string().nullable(), + description: z.string().nullable(), +}); + +export const RouteSearchRowSchema = RouteBaseRowSchema.extend({ + bbox: z.string().nullable(), +}); + +export const RouteDetailRowSchema = RouteBaseRowSchema.extend({ + members: z.array(OsmMemberSchema).nullable(), + geojson: z.string().nullable(), +}); + +export type OsmMember = z.infer; +export type RouteSearchRow = z.infer; +export type RouteDetailRow = z.infer; diff --git a/packages/schemas/src/wildlife.ts b/packages/schemas/src/wildlife.ts new file mode 100644 index 0000000000..cc511a907f --- /dev/null +++ b/packages/schemas/src/wildlife.ts @@ -0,0 +1,5 @@ +import { z } from 'zod'; + +export const WildlifeIdentifyRequestSchema = z.object({ + image: z.string().describe('Uploaded image key in R2'), +}); From 26f4f601e2d43284f0265b511dc3ad6e327ca7b5 Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Sat, 16 May 2026 15:29:45 -0600 Subject: [PATCH 48/51] fix(merge): update imports broken by db/schemas extraction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After merging development, four files imported from paths the PR removed: - packages/api/src/routes/passwordReset.ts: @packrat/api/schemas/auth → @packrat/schemas/auth - packages/api/src/services/passwordResetService.ts: @packrat/api/db/schema → @packrat/db - apps/trails/lib/apiClient.ts: authClient → trailsAuthClient (PR renamed) - apps/trails/components/AuthGate.tsx: pass required email prop to bun check-types now passes with 0 errors. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/trails/components/AuthGate.tsx | 2 +- apps/trails/lib/apiClient.ts | 2 +- packages/api/src/routes/passwordReset.ts | 2 +- packages/api/src/services/passwordResetService.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/trails/components/AuthGate.tsx b/apps/trails/components/AuthGate.tsx index be1718de6d..a82791e74c 100644 --- a/apps/trails/components/AuthGate.tsx +++ b/apps/trails/components/AuthGate.tsx @@ -108,7 +108,7 @@ export function AuthGate() { {pendingEmail ? ( - + ) : ( Date: Sat, 16 May 2026 16:35:44 -0600 Subject: [PATCH 49/51] fix(lint): organize imports in passwordReset.ts CI biome check caught alphabetical ordering after the merge import fix. Local pre-push hook missed it because it runs biome with --no-errors-on-unmatched. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/api/src/routes/passwordReset.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/routes/passwordReset.ts b/packages/api/src/routes/passwordReset.ts index b6b2d338df..094e9d8272 100644 --- a/packages/api/src/routes/passwordReset.ts +++ b/packages/api/src/routes/passwordReset.ts @@ -1,8 +1,8 @@ -import { ForgotPasswordRequestSchema, ResetPasswordRequestSchema } from '@packrat/schemas/auth'; import { requestPasswordReset, verifyOtpAndResetPassword, } from '@packrat/api/services/passwordResetService'; +import { ForgotPasswordRequestSchema, ResetPasswordRequestSchema } from '@packrat/schemas/auth'; import { Elysia, status } from 'elysia'; export const passwordResetRoutes = new Elysia({ prefix: '/password-reset' }) From 3d1cfe1a932ef3a00bfdb332e53a445ff748898c Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Sat, 16 May 2026 18:23:40 -0600 Subject: [PATCH 50/51] fix(lint): organize imports in passwordResetService.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same root cause as 73c755ab4 — moving an import path from @packrat/api/db/schema to @packrat/db re-sorts after the other @packrat/api imports. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/api/src/services/passwordResetService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/services/passwordResetService.ts b/packages/api/src/services/passwordResetService.ts index b6404bd222..cae2003ba6 100644 --- a/packages/api/src/services/passwordResetService.ts +++ b/packages/api/src/services/passwordResetService.ts @@ -1,8 +1,8 @@ import { hashPassword } from '@better-auth/utils/password'; import { createDb } from '@packrat/api/db'; -import { account, users, verification } from '@packrat/db'; import { timingSafeEqual } from '@packrat/api/utils/auth'; import { sendPasswordResetEmail } from '@packrat/api/utils/email'; +import { account, users, verification } from '@packrat/db'; import { and, eq, gt } from 'drizzle-orm'; const OTP_LENGTH = 6; From cb4181c206874e512d6d5af9ec1bf5bc6d32210c Mon Sep 17 00:00:00 2001 From: Andrew Bierman Date: Sat, 16 May 2026 19:47:22 -0600 Subject: [PATCH 51/51] chore(ci): retrigger CF Pages after transient build failure