diff --git a/docs/MANADECK_POSTGRES_MIGRATION.md b/docs/MANADECK_POSTGRES_MIGRATION.md new file mode 100644 index 000000000..e53697753 --- /dev/null +++ b/docs/MANADECK_POSTGRES_MIGRATION.md @@ -0,0 +1,840 @@ +# ManaDeck: Migration zu PostgreSQL + Drizzle ORM + +## Übersicht + +Dieses Dokument beschreibt die Migration von ManaDeck von Supabase zu einer selbst-gehosteten PostgreSQL-Datenbank mit Drizzle ORM. + +--- + +## Aktuelle Architektur + +``` +┌─────────────────┐ ┌─────────────────┐ +│ ManaDeck Web │ │ ManaDeck Mobile │ +│ (SvelteKit) │ │ (Expo) │ +└────────┬────────┘ └────────┬────────┘ + │ │ + └───────────┬───────────┘ + │ + ┌───────────▼───────────┐ + │ ManaDeck Backend │ + │ (NestJS) │ + └───────────┬───────────┘ + │ + ┌────────────────┼────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌────────┐ ┌──────────┐ ┌──────────┐ +│Supabase│ │Mana Core │ │ OpenAI │ +│ DB │ │ (Auth) │ │ API │ +└────────┘ └──────────┘ └──────────┘ +``` + +--- + +## Ziel-Architektur + +``` +┌─────────────────┐ ┌─────────────────┐ +│ ManaDeck Web │ │ ManaDeck Mobile │ +│ (SvelteKit) │ │ (Expo) │ +└────────┬────────┘ └────────┬────────┘ + │ │ + └───────────┬───────────┘ + │ + ┌───────────▼───────────┐ + │ ManaDeck Backend │ + │ (NestJS + Drizzle) │ + └───────────┬───────────┘ + │ + ┌────────────────┼────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌────────┐ ┌──────────┐ ┌──────────┐ +│ PostgreSQL │ │Mana Core │ │ OpenAI │ +│ (Self) │ │ (Auth) │ │ API │ +└────────────┘ └──────────┘ └──────────┘ +``` + +--- + +## Datenbank-Schema + +### Tabellen + +#### 1. `decks` +```sql +CREATE TABLE decks ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL, -- Mana Core User ID + title VARCHAR(255) NOT NULL, + description TEXT, + cover_image_url TEXT, + is_public BOOLEAN DEFAULT false, + is_featured BOOLEAN DEFAULT false, + featured_at TIMESTAMPTZ, + settings JSONB DEFAULT '{}', + tags TEXT[] DEFAULT '{}', + metadata JSONB DEFAULT '{}', + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_decks_user_id ON decks(user_id); +CREATE INDEX idx_decks_is_public ON decks(is_public); +CREATE INDEX idx_decks_is_featured ON decks(is_featured); +``` + +#### 2. `cards` +```sql +CREATE TABLE cards ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + deck_id UUID NOT NULL REFERENCES decks(id) ON DELETE CASCADE, + position INTEGER NOT NULL DEFAULT 0, + title VARCHAR(255), + content JSONB NOT NULL, + card_type VARCHAR(20) NOT NULL CHECK (card_type IN ('text', 'flashcard', 'quiz', 'mixed')), + ai_model VARCHAR(100), + ai_prompt TEXT, + version INTEGER DEFAULT 1, + is_favorite BOOLEAN DEFAULT false, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_cards_deck_id ON cards(deck_id); +CREATE INDEX idx_cards_position ON cards(deck_id, position); +``` + +#### 3. `study_sessions` +```sql +CREATE TABLE study_sessions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + deck_id UUID NOT NULL REFERENCES decks(id) ON DELETE CASCADE, + user_id UUID NOT NULL, + mode VARCHAR(20) NOT NULL CHECK (mode IN ('all', 'new', 'review', 'favorites', 'random')), + total_cards INTEGER NOT NULL DEFAULT 0, + completed_cards INTEGER NOT NULL DEFAULT 0, + correct_cards INTEGER NOT NULL DEFAULT 0, + started_at TIMESTAMPTZ DEFAULT NOW(), + completed_at TIMESTAMPTZ, + time_spent_seconds INTEGER DEFAULT 0 +); + +CREATE INDEX idx_study_sessions_user_id ON study_sessions(user_id); +CREATE INDEX idx_study_sessions_deck_id ON study_sessions(deck_id); +``` + +#### 4. `card_progress` +```sql +CREATE TABLE card_progress ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL, + card_id UUID NOT NULL REFERENCES cards(id) ON DELETE CASCADE, + ease_factor DECIMAL(4,2) DEFAULT 2.5, + interval INTEGER DEFAULT 0, + repetitions INTEGER DEFAULT 0, + last_reviewed TIMESTAMPTZ, + next_review TIMESTAMPTZ, + status VARCHAR(20) DEFAULT 'new' CHECK (status IN ('new', 'learning', 'review', 'relearning')), + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW(), + UNIQUE(user_id, card_id) +); + +CREATE INDEX idx_card_progress_user_id ON card_progress(user_id); +CREATE INDEX idx_card_progress_next_review ON card_progress(next_review); +``` + +#### 5. `deck_templates` +```sql +CREATE TABLE deck_templates ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + title VARCHAR(255) NOT NULL, + description TEXT, + category VARCHAR(100), + template_data JSONB NOT NULL, + is_active BOOLEAN DEFAULT true, + is_public BOOLEAN DEFAULT true, + popularity INTEGER DEFAULT 0, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_deck_templates_category ON deck_templates(category); +CREATE INDEX idx_deck_templates_is_active ON deck_templates(is_active); +``` + +#### 6. `ai_generations` +```sql +CREATE TABLE ai_generations ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL, + deck_id UUID REFERENCES decks(id) ON DELETE SET NULL, + function_name VARCHAR(100) NOT NULL, + prompt TEXT NOT NULL, + model VARCHAR(100), + status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'completed', 'failed')), + metadata JSONB DEFAULT '{}', + completed_at TIMESTAMPTZ, + created_at TIMESTAMPTZ DEFAULT NOW() +); + +CREATE INDEX idx_ai_generations_user_id ON ai_generations(user_id); +CREATE INDEX idx_ai_generations_status ON ai_generations(status); +``` + +#### 7. `user_stats` (für Leaderboard) +```sql +CREATE TABLE user_stats ( + user_id UUID PRIMARY KEY, + total_wins INTEGER DEFAULT 0, + total_sessions INTEGER DEFAULT 0, + total_cards_studied INTEGER DEFAULT 0, + total_time_seconds INTEGER DEFAULT 0, + average_accuracy DECIMAL(5,2) DEFAULT 0, + streak_days INTEGER DEFAULT 0, + last_study_date DATE, + created_at TIMESTAMPTZ DEFAULT NOW(), + updated_at TIMESTAMPTZ DEFAULT NOW() +); +``` + +--- + +## Drizzle Schema + +### Dateistruktur + +``` +manadeck/ +├── packages/ +│ └── database/ # Neues shared database package +│ ├── src/ +│ │ ├── schema/ +│ │ │ ├── decks.ts +│ │ │ ├── cards.ts +│ │ │ ├── studySessions.ts +│ │ │ ├── cardProgress.ts +│ │ │ ├── deckTemplates.ts +│ │ │ ├── aiGenerations.ts +│ │ │ ├── userStats.ts +│ │ │ └── index.ts +│ │ ├── client.ts +│ │ ├── migrate.ts +│ │ └── index.ts +│ ├── drizzle/ +│ │ └── migrations/ +│ ├── drizzle.config.ts +│ ├── package.json +│ └── tsconfig.json +``` + +### Schema-Definitionen (Drizzle) + +#### `schema/decks.ts` +```typescript +import { pgTable, uuid, varchar, text, boolean, timestamp, jsonb } from 'drizzle-orm/pg-core'; + +export const decks = pgTable('decks', { + id: uuid('id').primaryKey().defaultRandom(), + userId: uuid('user_id').notNull(), + title: varchar('title', { length: 255 }).notNull(), + description: text('description'), + coverImageUrl: text('cover_image_url'), + isPublic: boolean('is_public').default(false), + isFeatured: boolean('is_featured').default(false), + featuredAt: timestamp('featured_at', { withTimezone: true }), + settings: jsonb('settings').default({}), + tags: text('tags').array().default([]), + metadata: jsonb('metadata').default({}), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), +}); + +export type Deck = typeof decks.$inferSelect; +export type NewDeck = typeof decks.$inferInsert; +``` + +#### `schema/cards.ts` +```typescript +import { pgTable, uuid, varchar, text, integer, boolean, timestamp, jsonb } from 'drizzle-orm/pg-core'; +import { decks } from './decks'; + +export const cardTypeEnum = pgEnum('card_type', ['text', 'flashcard', 'quiz', 'mixed']); + +export const cards = pgTable('cards', { + id: uuid('id').primaryKey().defaultRandom(), + deckId: uuid('deck_id').notNull().references(() => decks.id, { onDelete: 'cascade' }), + position: integer('position').notNull().default(0), + title: varchar('title', { length: 255 }), + content: jsonb('content').notNull(), + cardType: cardTypeEnum('card_type').notNull(), + aiModel: varchar('ai_model', { length: 100 }), + aiPrompt: text('ai_prompt'), + version: integer('version').default(1), + isFavorite: boolean('is_favorite').default(false), + createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(), + updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(), +}); + +export type Card = typeof cards.$inferSelect; +export type NewCard = typeof cards.$inferInsert; +``` + +#### `schema/index.ts` +```typescript +export * from './decks'; +export * from './cards'; +export * from './studySessions'; +export * from './cardProgress'; +export * from './deckTemplates'; +export * from './aiGenerations'; +export * from './userStats'; + +// Relations +export { decksRelations } from './decks'; +export { cardsRelations } from './cards'; +``` + +### Client Setup + +#### `client.ts` +```typescript +import { drizzle } from 'drizzle-orm/postgres-js'; +import postgres from 'postgres'; +import * as schema from './schema'; + +const connectionString = process.env.DATABASE_URL!; + +// For connection pooling in serverless environments +const client = postgres(connectionString, { + max: 10, + idle_timeout: 20, + connect_timeout: 10, +}); + +export const db = drizzle(client, { schema }); +export type Database = typeof db; +``` + +--- + +## Migrationsschritte + +### Phase 1: Setup (Tag 1-2) + +#### 1.1 PostgreSQL Server aufsetzen +```bash +# Option A: Railway.app (empfohlen für Staging) +# Erstelle neues Projekt auf railway.app +# Füge PostgreSQL Plugin hinzu + +# Option B: Docker lokal +docker run -d \ + --name manadeck-postgres \ + -e POSTGRES_USER=manadeck \ + -e POSTGRES_PASSWORD=secure_password \ + -e POSTGRES_DB=manadeck \ + -p 5432:5432 \ + postgres:16-alpine + +# Option C: Neon.tech (Serverless) +# Erstelle neues Projekt auf neon.tech +``` + +#### 1.2 Database Package erstellen +```bash +cd /Users/tillschneider/Documents/__00__Code/manacore-monorepo +mkdir -p packages/manadeck-database +cd packages/manadeck-database +pnpm init +``` + +#### 1.3 Dependencies installieren +```bash +pnpm add drizzle-orm postgres +pnpm add -D drizzle-kit typescript @types/node +``` + +### Phase 2: Schema & Migration (Tag 2-3) + +#### 2.1 Drizzle Schema erstellen +- Alle Tabellen wie oben definiert +- Relations definieren +- Indexes definieren + +#### 2.2 Initial Migration generieren +```bash +pnpm drizzle-kit generate +pnpm drizzle-kit migrate +``` + +#### 2.3 Daten von Supabase exportieren +```bash +# In Supabase Dashboard: SQL Editor +# Export alle Tabellen als CSV oder pg_dump + +# Oder via CLI: +pg_dump -h db.your-project.supabase.co \ + -U postgres \ + -d postgres \ + --data-only \ + --table=decks \ + --table=cards \ + --table=study_sessions \ + --table=card_progress \ + --table=deck_templates \ + --table=ai_generations \ + --table=user_stats \ + > manadeck_data.sql +``` + +### Phase 3: Backend Migration (Tag 3-5) + +#### 3.1 Repository Pattern implementieren + +```typescript +// repositories/deck.repository.ts +import { db } from '@manadeck/database'; +import { decks, cards } from '@manadeck/database/schema'; +import { eq, and, or, desc } from 'drizzle-orm'; + +export class DeckRepository { + async findAllByUser(userId: string) { + return db.query.decks.findMany({ + where: eq(decks.userId, userId), + orderBy: desc(decks.updatedAt), + with: { + cards: true, + }, + }); + } + + async findById(id: string) { + return db.query.decks.findFirst({ + where: eq(decks.id, id), + with: { + cards: { + orderBy: (cards, { asc }) => [asc(cards.position)], + }, + }, + }); + } + + async findPublicAndUserDecks(userId: string) { + return db.query.decks.findMany({ + where: or( + eq(decks.userId, userId), + and(eq(decks.isPublic, true), eq(decks.isFeatured, true)) + ), + orderBy: desc(decks.updatedAt), + }); + } + + async create(data: NewDeck) { + const [deck] = await db.insert(decks).values(data).returning(); + return deck; + } + + async update(id: string, userId: string, data: Partial) { + const [deck] = await db + .update(decks) + .set({ ...data, updatedAt: new Date() }) + .where(and(eq(decks.id, id), eq(decks.userId, userId))) + .returning(); + return deck; + } + + async delete(id: string, userId: string) { + await db + .delete(decks) + .where(and(eq(decks.id, id), eq(decks.userId, userId))); + } +} +``` + +#### 3.2 Service Layer aktualisieren + +```typescript +// services/deck.service.ts +import { DeckRepository } from '../repositories/deck.repository'; + +export class DeckService { + constructor(private deckRepo = new DeckRepository()) {} + + async getUserDecks(userId: string) { + return this.deckRepo.findPublicAndUserDecks(userId); + } + + async getDeck(id: string) { + return this.deckRepo.findById(id); + } + + async createDeck(userId: string, data: CreateDeckInput) { + return this.deckRepo.create({ + ...data, + userId, + }); + } + + async updateDeck(id: string, userId: string, data: UpdateDeckInput) { + return this.deckRepo.update(id, userId, data); + } + + async deleteDeck(id: string, userId: string) { + return this.deckRepo.delete(id, userId); + } +} +``` + +### Phase 4: Frontend Migration (Tag 5-7) + +#### 4.1 API Client erstellen (ersetzt Supabase SDK) + +```typescript +// lib/api/client.ts +import { getToken } from '$lib/auth'; + +const API_URL = import.meta.env.VITE_API_URL; + +async function fetchApi( + endpoint: string, + options: RequestInit = {} +): Promise { + const token = getToken(); + + const response = await fetch(`${API_URL}${endpoint}`, { + ...options, + headers: { + 'Content-Type': 'application/json', + ...(token && { Authorization: `Bearer ${token}` }), + ...options.headers, + }, + }); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || 'API Error'); + } + + return response.json(); +} + +export const api = { + get: (endpoint: string) => fetchApi(endpoint), + post: (endpoint: string, data: unknown) => + fetchApi(endpoint, { method: 'POST', body: JSON.stringify(data) }), + put: (endpoint: string, data: unknown) => + fetchApi(endpoint, { method: 'PUT', body: JSON.stringify(data) }), + delete: (endpoint: string) => + fetchApi(endpoint, { method: 'DELETE' }), +}; +``` + +#### 4.2 Deck Store migrieren + +```typescript +// stores/deckStore.svelte.ts +import { api } from '$lib/api/client'; +import type { Deck, CreateDeckInput, UpdateDeckInput } from '$lib/types/deck'; + +function createDeckStore() { + let decks = $state([]); + let currentDeck = $state(null); + let loading = $state(false); + let error = $state(null); + + return { + get decks() { return decks; }, + get currentDeck() { return currentDeck; }, + get loading() { return loading; }, + get error() { return error; }, + + async fetchDecks() { + loading = true; + error = null; + try { + decks = await api.get('/api/decks'); + } catch (e) { + error = e instanceof Error ? e.message : 'Failed to fetch decks'; + } finally { + loading = false; + } + }, + + async fetchDeck(id: string) { + loading = true; + error = null; + try { + currentDeck = await api.get(`/api/decks/${id}`); + } catch (e) { + error = e instanceof Error ? e.message : 'Failed to fetch deck'; + } finally { + loading = false; + } + }, + + async createDeck(data: CreateDeckInput) { + loading = true; + error = null; + try { + const deck = await api.post('/api/decks', data); + decks = [deck, ...decks]; + return deck; + } catch (e) { + error = e instanceof Error ? e.message : 'Failed to create deck'; + throw e; + } finally { + loading = false; + } + }, + + async updateDeck(id: string, data: UpdateDeckInput) { + loading = true; + error = null; + try { + const deck = await api.put(`/api/decks/${id}`, data); + decks = decks.map(d => d.id === id ? deck : d); + if (currentDeck?.id === id) currentDeck = deck; + return deck; + } catch (e) { + error = e instanceof Error ? e.message : 'Failed to update deck'; + throw e; + } finally { + loading = false; + } + }, + + async deleteDeck(id: string) { + loading = true; + error = null; + try { + await api.delete(`/api/decks/${id}`); + decks = decks.filter(d => d.id !== id); + if (currentDeck?.id === id) currentDeck = null; + } catch (e) { + error = e instanceof Error ? e.message : 'Failed to delete deck'; + throw e; + } finally { + loading = false; + } + }, + }; +} + +export const deckStore = createDeckStore(); +``` + +### Phase 5: Testing & Cutover (Tag 7-10) + +#### 5.1 Integration Tests +```typescript +// tests/deck.integration.test.ts +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import { db } from '@manadeck/database'; +import { decks } from '@manadeck/database/schema'; +import { DeckService } from '../services/deck.service'; + +describe('DeckService', () => { + const service = new DeckService(); + const testUserId = 'test-user-id'; + + afterAll(async () => { + await db.delete(decks).where(eq(decks.userId, testUserId)); + }); + + it('should create a deck', async () => { + const deck = await service.createDeck(testUserId, { + title: 'Test Deck', + description: 'A test deck', + }); + + expect(deck.id).toBeDefined(); + expect(deck.title).toBe('Test Deck'); + }); + + it('should fetch user decks', async () => { + const userDecks = await service.getUserDecks(testUserId); + expect(userDecks.length).toBeGreaterThan(0); + }); +}); +``` + +#### 5.2 Datenmigrations-Script +```typescript +// scripts/migrate-data.ts +import { db as newDb } from '@manadeck/database'; +import { createClient } from '@supabase/supabase-js'; +import { decks, cards } from '@manadeck/database/schema'; + +const supabase = createClient( + process.env.SUPABASE_URL!, + process.env.SUPABASE_SERVICE_KEY! +); + +async function migrateDecks() { + console.log('Migrating decks...'); + + const { data: supabaseDecks, error } = await supabase + .from('decks') + .select('*'); + + if (error) throw error; + + for (const deck of supabaseDecks) { + await newDb.insert(decks).values({ + id: deck.id, + userId: deck.user_id, + title: deck.title, + description: deck.description, + coverImageUrl: deck.cover_image_url, + isPublic: deck.is_public, + isFeatured: deck.is_featured, + featuredAt: deck.featured_at, + settings: deck.settings, + tags: deck.tags, + metadata: deck.metadata, + createdAt: deck.created_at, + updatedAt: deck.updated_at, + }).onConflictDoNothing(); + } + + console.log(`Migrated ${supabaseDecks.length} decks`); +} + +async function migrateCards() { + console.log('Migrating cards...'); + + const { data: supabaseCards, error } = await supabase + .from('cards') + .select('*'); + + if (error) throw error; + + for (const card of supabaseCards) { + await newDb.insert(cards).values({ + id: card.id, + deckId: card.deck_id, + position: card.position, + title: card.title, + content: card.content, + cardType: card.card_type, + aiModel: card.ai_model, + aiPrompt: card.ai_prompt, + version: card.version, + isFavorite: card.is_favorite, + createdAt: card.created_at, + updatedAt: card.updated_at, + }).onConflictDoNothing(); + } + + console.log(`Migrated ${supabaseCards.length} cards`); +} + +async function main() { + try { + await migrateDecks(); + await migrateCards(); + // ... andere Tabellen + console.log('Migration completed successfully!'); + } catch (error) { + console.error('Migration failed:', error); + process.exit(1); + } +} + +main(); +``` + +--- + +## Zeitplan + +| Phase | Beschreibung | Dauer | Status | +|-------|--------------|-------|--------| +| 1 | Setup (PostgreSQL, Package) | 1-2 Tage | ⬜ Pending | +| 2 | Schema & Migration | 1-2 Tage | ⬜ Pending | +| 3 | Backend Migration | 2-3 Tage | ⬜ Pending | +| 4 | Frontend Migration | 2-3 Tage | ⬜ Pending | +| 5 | Testing & Cutover | 2-3 Tage | ⬜ Pending | + +**Gesamtdauer: ~10-13 Tage** + +--- + +## Checkliste + +### Pre-Migration +- [ ] PostgreSQL Server aufgesetzt +- [ ] Database Package erstellt +- [ ] Drizzle Schema definiert +- [ ] Initial Migration durchgeführt +- [ ] Supabase Daten exportiert + +### Backend +- [ ] Repository Pattern implementiert +- [ ] DeckRepository +- [ ] CardRepository +- [ ] StudySessionRepository +- [ ] CardProgressRepository +- [ ] DeckTemplateRepository +- [ ] AIGenerationRepository +- [ ] UserStatsRepository +- [ ] Service Layer aktualisiert +- [ ] Controller aktualisiert +- [ ] Authorization Middleware (ersetzt RLS) + +### Frontend +- [ ] API Client erstellt +- [ ] deckStore migriert +- [ ] Supabase imports entfernt +- [ ] Environment Variables aktualisiert + +### Testing +- [ ] Unit Tests für Repositories +- [ ] Integration Tests für Services +- [ ] E2E Tests für API +- [ ] Manual Testing aller Features + +### Cutover +- [ ] Daten migriert (Script ausgeführt) +- [ ] DNS/Environment umgestellt +- [ ] Rollback Plan dokumentiert +- [ ] Monitoring eingerichtet + +--- + +## Rollback Plan + +Falls kritische Probleme auftreten: + +1. **Environment Variables zurücksetzen** auf Supabase +2. **Feature Flag** für alte Implementierung aktivieren +3. **DNS** auf alten Service zeigen lassen + +Supabase-Daten bleiben während der Migration unberührt. + +--- + +## Kosten nach Migration + +| Service | Geschätzte Kosten | +|---------|-------------------| +| PostgreSQL (Railway) | ~$5-20/Monat | +| PostgreSQL (Neon Free) | $0/Monat | +| Backup (optional) | ~$5/Monat | +| **Gesamt** | **$5-25/Monat** | + +vs. Supabase Pro: $25-300/Monat + +--- + +## Nächste Schritte + +1. **Entscheidung**: PostgreSQL Hosting (Railway vs Neon vs Self-hosted) +2. **Setup**: Database Package im Monorepo erstellen +3. **Schema**: Drizzle Schema implementieren +4. **Start**: Phase 1 beginnen + +Bereit zum Start? diff --git a/maerchenzauber/apps/web/src/lib/i18n/index.ts b/maerchenzauber/apps/web/src/lib/i18n/index.ts index 3036ebce3..48d6d2300 100644 --- a/maerchenzauber/apps/web/src/lib/i18n/index.ts +++ b/maerchenzauber/apps/web/src/lib/i18n/index.ts @@ -4,9 +4,12 @@ import { init, register, locale, waitLocale } from 'svelte-i18n'; // Register all available locales register('de', () => import('./locales/de.json')); register('en', () => import('./locales/en.json')); +register('it', () => import('./locales/it.json')); +register('fr', () => import('./locales/fr.json')); +register('es', () => import('./locales/es.json')); // List of supported locales -export const supportedLocales = ['de', 'en'] as const; +export const supportedLocales = ['de', 'en', 'it', 'fr', 'es'] as const; export type SupportedLocale = (typeof supportedLocales)[number]; // Default locale diff --git a/maerchenzauber/apps/web/src/routes/(auth)/login/+page.svelte b/maerchenzauber/apps/web/src/routes/(auth)/login/+page.svelte index 71a18771d..f5f5eb993 100644 --- a/maerchenzauber/apps/web/src/routes/(auth)/login/+page.svelte +++ b/maerchenzauber/apps/web/src/routes/(auth)/login/+page.svelte @@ -1,11 +1,16 @@ @@ -121,7 +164,7 @@ class="mb-6 text-center text-xl font-semibold" style="color: {isDark ? 'rgba(255, 255, 255, 0.9)' : 'rgba(0, 0, 0, 0.9)'};" > - {mode === 'form' ? 'Reset Password' : 'Email Sent'} + {mode === 'form' ? t.titleForm : t.titleSuccess} @@ -144,14 +187,14 @@ class="mb-4 text-sm" style="color: {isDark ? 'rgba(255, 255, 255, 0.7)' : 'rgba(0, 0, 0, 0.7)'};" > - Enter your email address and we'll send you a link to reset your password. + {t.description}

- {loading ? 'Sending...' : 'Send Reset Link'} + {loading ? t.sending : t.sendResetLinkButton} @@ -177,7 +220,7 @@ style="color: {isDark ? '#ffffff' : '#000000'};" > - Back to Login + {t.backToLogin}
@@ -196,8 +239,7 @@ class="text-sm text-center px-2" style="color: {isDark ? 'rgba(255, 255, 255, 0.7)' : 'rgba(0, 0, 0, 0.7)'};" > - We've sent a password reset link to {resetEmail}. Please check your - inbox. + {@html getSuccessMessage(resetEmail).replace(resetEmail, `${resetEmail}`)}

@@ -208,7 +250,7 @@ style="background-color: {primaryColor}60; border-color: {primaryColor}; color: {isDark ? '#ffffff' : '#000000'};" > - Back to Login + {t.backToLogin} diff --git a/packages/shared-auth-ui/src/pages/LoginPage.svelte b/packages/shared-auth-ui/src/pages/LoginPage.svelte index ae34928b8..91afa4dcd 100644 --- a/packages/shared-auth-ui/src/pages/LoginPage.svelte +++ b/packages/shared-auth-ui/src/pages/LoginPage.svelte @@ -5,6 +5,60 @@ import GoogleSignInButton from '../components/GoogleSignInButton.svelte'; import AppleSignInButton from '../components/AppleSignInButton.svelte'; + /** Translation strings for the login page */ + export interface LoginTranslations { + title: string; + subtitle: string; + emailPlaceholder: string; + passwordPlaceholder: string; + rememberMe: string; + forgotPassword: string; + signInButton: string; + signingIn: string; + success: string; + orDivider: string; + noAccount: string; + createAccount: string; + skipToForm: string; + showPassword: string; + hidePassword: string; + // Error messages + emailRequired: string; + emailInvalid: string; + passwordRequired: string; + signInFailed: string; + googleSignInFailed: string; + // Success messages + signInSuccess: string; + googleSignInSuccess: string; + } + + /** Default English translations */ + const defaultTranslations: LoginTranslations = { + title: 'Sign In', + subtitle: 'Sign in with your Mana account', + emailPlaceholder: 'Email', + passwordPlaceholder: 'Password', + rememberMe: 'Remember me', + forgotPassword: 'Forgot password?', + signInButton: 'Sign In', + signingIn: 'Signing in...', + success: 'Success!', + orDivider: 'or', + noAccount: "Don't have an account?", + createAccount: 'Create one', + skipToForm: 'Skip to login form', + showPassword: 'Show password', + hidePassword: 'Hide password', + emailRequired: 'Email is required', + emailInvalid: 'Please enter a valid email address', + passwordRequired: 'Password is required', + signInFailed: 'Sign in failed', + googleSignInFailed: 'Google sign in failed', + signInSuccess: 'Successfully signed in. Redirecting...', + googleSignInSuccess: 'Successfully signed in with Google. Redirecting...' + }; + interface Props { /** App name */ appName: string; @@ -38,6 +92,8 @@ appSlider?: Snippet; /** Header snippet for controls like theme toggle and language selector */ headerControls?: Snippet; + /** Translations for i18n support */ + translations?: Partial; } let { @@ -56,9 +112,13 @@ lightBackground = '#f5f5f5', darkBackground = '#121212', appSlider, - headerControls + headerControls, + translations = {} }: Props = $props(); + // Merge provided translations with defaults + const t = $derived({ ...defaultTranslations, ...translations }); + let loading = $state(false); let error = $state(null); let errorField = $state<'email' | 'password' | 'general' | null>(null); @@ -135,19 +195,19 @@ clearError(); if (!email) { - setError('Email is required', 'email'); + setError(t.emailRequired, 'email'); loading = false; return; } if (!isValidEmail(email)) { - setError('Please enter a valid email address', 'email'); + setError(t.emailInvalid, 'email'); loading = false; return; } if (!password) { - setError('Password is required', 'password'); + setError(t.passwordRequired, 'password'); loading = false; return; } @@ -159,12 +219,12 @@ if (result.success) { // Show success feedback before redirect showSuccess = true; - successAnnouncement = 'Successfully signed in. Redirecting...'; + successAnnouncement = t.signInSuccess; setTimeout(() => { goto(successRedirect); }, 600); } else { - setError(result.error || 'Sign in failed', 'general'); + setError(result.error || t.signInFailed, 'general'); } } @@ -179,12 +239,12 @@ if (result.success) { showSuccess = true; - successAnnouncement = 'Successfully signed in with Google. Redirecting...'; + successAnnouncement = t.googleSignInSuccess; setTimeout(() => { goto(successRedirect); }, 600); } else { - setError(result.error || 'Google sign in failed', 'general'); + setError(result.error || t.googleSignInFailed, 'general'); } } @@ -285,7 +345,7 @@ onclick={skipToForm} type="button" > - Skip to login form + {t.skipToForm} @@ -340,13 +400,13 @@ class="text-center text-xl font-semibold" style="color: {isDark ? 'rgba(255, 255, 255, 0.9)' : 'rgba(0, 0, 0, 0.9)'};" > - Sign In + {t.title}

- Sign in with your Mana account + {t.subtitle}

@@ -375,13 +435,13 @@ >
- +
- + (showPassword = !showPassword)} class="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-lg hover:bg-black/10 dark:hover:bg-white/10 transition-colors touch-target flex items-center justify-center" - aria-label={showPassword ? 'Hide password' : 'Show password'} + aria-label={showPassword ? t.hidePassword : t.showPassword} aria-pressed={showPassword} - title={showPassword ? 'Hide password' : 'Show password'} + title={showPassword ? t.hidePassword : t.showPassword} > - Remember me + {t.rememberMe} @@ -446,7 +506,7 @@ class="text-sm font-medium transition-opacity hover:opacity-70 touch-target flex items-center justify-center px-2" style="color: {primaryColor};" > - Forgot password? + {t.forgotPassword}
@@ -470,13 +530,13 @@ - Signing in... + {t.signingIn} {:else if showSuccess} - Success! + {t.success} {:else} - Sign In + {t.signInButton} {/if} @@ -485,7 +545,7 @@ {#if enableGoogle || enableApple} @@ -505,14 +565,14 @@ class="text-sm" style="color: {isDark ? 'rgba(255, 255, 255, 0.6)' : 'rgba(0, 0, 0, 0.6)'};" > - Don't have an account? + {t.noAccount}

diff --git a/packages/shared-auth-ui/src/pages/RegisterPage.svelte b/packages/shared-auth-ui/src/pages/RegisterPage.svelte index f86bad4ca..cb9be2713 100644 --- a/packages/shared-auth-ui/src/pages/RegisterPage.svelte +++ b/packages/shared-auth-ui/src/pages/RegisterPage.svelte @@ -5,6 +5,52 @@ import type { Snippet } from 'svelte'; + /** Translation strings for the register page */ + export interface RegisterTranslations { + title: string; + emailPlaceholder: string; + passwordPlaceholder: string; + confirmPasswordPlaceholder: string; + passwordRequirements: string; + createAccountButton: string; + creatingAccount: string; + backToLogin: string; + showPassword: string; + hidePassword: string; + // Error messages + emailRequired: string; + passwordRequired: string; + confirmPasswordRequired: string; + passwordsDoNotMatch: string; + passwordTooShort: string; + passwordStrengthError: string; + registrationFailed: string; + // Success messages + accountCreated: string; + } + + /** Default English translations */ + const defaultTranslations: RegisterTranslations = { + title: 'Create Account', + emailPlaceholder: 'Email', + passwordPlaceholder: 'Password', + confirmPasswordPlaceholder: 'Confirm Password', + passwordRequirements: 'Password must be at least 8 characters with lowercase, uppercase, number, and special character.', + createAccountButton: 'Create Account', + creatingAccount: 'Creating Account...', + backToLogin: 'Back to Login', + showPassword: 'Show password', + hidePassword: 'Hide password', + emailRequired: 'Email is required', + passwordRequired: 'Password is required', + confirmPasswordRequired: 'Please confirm your password', + passwordsDoNotMatch: 'Passwords do not match', + passwordTooShort: 'Password must be at least 8 characters', + passwordStrengthError: 'Password must include lowercase, uppercase, number, and special character', + registrationFailed: 'Registration failed', + accountCreated: 'Account created! Please check your email to verify your account.' + }; + interface Props { /** App name */ appName: string; @@ -26,6 +72,8 @@ darkBackground?: string; /** App slider snippet */ appSlider?: Snippet; + /** Translations for i18n support */ + translations?: Partial; } let { @@ -38,9 +86,13 @@ loginPath = '/login', lightBackground = '#f5f5f5', darkBackground = '#121212', - appSlider + appSlider, + translations = {} }: Props = $props(); + // Merge provided translations with defaults + const t = $derived({ ...defaultTranslations, ...translations }); + let loading = $state(false); let error = $state(null); let success = $state(false); @@ -98,31 +150,31 @@ // Validation if (!email) { - error = 'Email is required'; + error = t.emailRequired; loading = false; return; } if (!password) { - error = 'Password is required'; + error = t.passwordRequired; loading = false; return; } if (!confirmPassword) { - error = 'Please confirm your password'; + error = t.confirmPasswordRequired; loading = false; return; } if (password !== confirmPassword) { - error = 'Passwords do not match'; + error = t.passwordsDoNotMatch; loading = false; return; } if (password.length < 8) { - error = 'Password must be at least 8 characters'; + error = t.passwordTooShort; loading = false; return; } @@ -134,7 +186,7 @@ const hasSymbol = /[^a-zA-Z0-9]/.test(password); if (!hasLowercase || !hasUppercase || !hasDigit || !hasSymbol) { - error = 'Password must include lowercase, uppercase, number, and special character'; + error = t.passwordStrengthError; loading = false; return; } @@ -153,7 +205,7 @@ goto(successRedirect); } } else { - error = result.error || 'Registration failed'; + error = result.error || t.registrationFailed; } } @@ -192,7 +244,7 @@ class="mb-6 text-center text-xl font-semibold" style="color: {isDark ? 'rgba(255, 255, 255, 0.9)' : 'rgba(0, 0, 0, 0.9)'};" > - Create Account + {t.title} @@ -206,7 +258,7 @@ {#if success && needsVerification}

- Account created! Please check your email to verify your account. + {t.accountCreated}

{/if} @@ -223,7 +275,7 @@ (showPassword = !showPassword)} class="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-lg hover:bg-black/10 dark:hover:bg-white/10 transition-colors" + aria-label={showPassword ? t.hidePassword : t.showPassword} > (showConfirmPassword = !showConfirmPassword)} class="absolute right-3 top-1/2 -translate-y-1/2 p-2 rounded-lg hover:bg-black/10 dark:hover:bg-white/10 transition-colors" + aria-label={showConfirmPassword ? t.hidePassword : t.showPassword} > - Password must be at least 8 characters with lowercase, uppercase, number, and special - character. + {t.passwordRequirements}

@@ -304,7 +357,7 @@ style="color: {isDark ? '#ffffff' : '#000000'};" > - Back to Login + {t.backToLogin} diff --git a/packages/shared-i18n/src/index.ts b/packages/shared-i18n/src/index.ts index 288aeb1e7..787c3a846 100644 --- a/packages/shared-i18n/src/index.ts +++ b/packages/shared-i18n/src/index.ts @@ -45,5 +45,21 @@ export { mergeWithCommon, } from './translations/common'; +// Auth translations +export { + en as authTranslationsEn, + de as authTranslationsDe, + it as authTranslationsIt, + fr as authTranslationsFr, + es as authTranslationsEs, + type AuthTranslations, + type AuthLocale, + authTranslations, + getAuthTranslations, + getLoginTranslations, + getRegisterTranslations, + getForgotPasswordTranslations, +} from './translations/auth'; + // Components export { LanguageSelector } from './components'; diff --git a/packages/shared-i18n/src/translations/auth/de.json b/packages/shared-i18n/src/translations/auth/de.json new file mode 100644 index 000000000..a6de162c7 --- /dev/null +++ b/packages/shared-i18n/src/translations/auth/de.json @@ -0,0 +1,59 @@ +{ + "login": { + "title": "Anmelden", + "subtitle": "Melde dich mit deinem Mana-Konto an", + "emailPlaceholder": "E-Mail", + "passwordPlaceholder": "Passwort", + "rememberMe": "Angemeldet bleiben", + "forgotPassword": "Passwort vergessen?", + "signInButton": "Anmelden", + "signingIn": "Anmeldung...", + "success": "Erfolgreich!", + "orDivider": "oder", + "noAccount": "Noch kein Konto?", + "createAccount": "Jetzt erstellen", + "skipToForm": "Zum Anmeldeformular springen", + "showPassword": "Passwort anzeigen", + "hidePassword": "Passwort verbergen", + "emailRequired": "E-Mail ist erforderlich", + "emailInvalid": "Bitte gib eine gültige E-Mail-Adresse ein", + "passwordRequired": "Passwort ist erforderlich", + "signInFailed": "Anmeldung fehlgeschlagen", + "googleSignInFailed": "Google-Anmeldung fehlgeschlagen", + "signInSuccess": "Erfolgreich angemeldet. Weiterleitung...", + "googleSignInSuccess": "Erfolgreich mit Google angemeldet. Weiterleitung..." + }, + "register": { + "title": "Konto erstellen", + "emailPlaceholder": "E-Mail", + "passwordPlaceholder": "Passwort", + "confirmPasswordPlaceholder": "Passwort bestätigen", + "passwordRequirements": "Das Passwort muss mindestens 8 Zeichen mit Kleinbuchstaben, Großbuchstaben, Zahl und Sonderzeichen enthalten.", + "createAccountButton": "Konto erstellen", + "creatingAccount": "Konto wird erstellt...", + "backToLogin": "Zurück zur Anmeldung", + "showPassword": "Passwort anzeigen", + "hidePassword": "Passwort verbergen", + "emailRequired": "E-Mail ist erforderlich", + "passwordRequired": "Passwort ist erforderlich", + "confirmPasswordRequired": "Bitte bestätige dein Passwort", + "passwordsDoNotMatch": "Passwörter stimmen nicht überein", + "passwordTooShort": "Passwort muss mindestens 8 Zeichen lang sein", + "passwordStrengthError": "Passwort muss Kleinbuchstaben, Großbuchstaben, Zahl und Sonderzeichen enthalten", + "registrationFailed": "Registrierung fehlgeschlagen", + "accountCreated": "Konto erstellt! Bitte überprüfe deine E-Mails, um dein Konto zu bestätigen." + }, + "forgotPassword": { + "titleForm": "Passwort zurücksetzen", + "titleSuccess": "E-Mail gesendet", + "description": "Gib deine E-Mail-Adresse ein und wir senden dir einen Link zum Zurücksetzen deines Passworts.", + "emailPlaceholder": "E-Mail", + "sendResetLinkButton": "Link senden", + "sending": "Wird gesendet...", + "backToLogin": "Zurück zur Anmeldung", + "resendEmail": "E-Mail erneut senden", + "successMessage": "Wir haben einen Link zum Zurücksetzen des Passworts an {email} gesendet. Bitte überprüfe deinen Posteingang.", + "emailRequired": "E-Mail ist erforderlich", + "sendFailed": "E-Mail konnte nicht gesendet werden" + } +} diff --git a/packages/shared-i18n/src/translations/auth/en.json b/packages/shared-i18n/src/translations/auth/en.json new file mode 100644 index 000000000..c7a6130a1 --- /dev/null +++ b/packages/shared-i18n/src/translations/auth/en.json @@ -0,0 +1,59 @@ +{ + "login": { + "title": "Sign In", + "subtitle": "Sign in with your Mana account", + "emailPlaceholder": "Email", + "passwordPlaceholder": "Password", + "rememberMe": "Remember me", + "forgotPassword": "Forgot password?", + "signInButton": "Sign In", + "signingIn": "Signing in...", + "success": "Success!", + "orDivider": "or", + "noAccount": "Don't have an account?", + "createAccount": "Create one", + "skipToForm": "Skip to login form", + "showPassword": "Show password", + "hidePassword": "Hide password", + "emailRequired": "Email is required", + "emailInvalid": "Please enter a valid email address", + "passwordRequired": "Password is required", + "signInFailed": "Sign in failed", + "googleSignInFailed": "Google sign in failed", + "signInSuccess": "Successfully signed in. Redirecting...", + "googleSignInSuccess": "Successfully signed in with Google. Redirecting..." + }, + "register": { + "title": "Create Account", + "emailPlaceholder": "Email", + "passwordPlaceholder": "Password", + "confirmPasswordPlaceholder": "Confirm Password", + "passwordRequirements": "Password must be at least 8 characters with lowercase, uppercase, number, and special character.", + "createAccountButton": "Create Account", + "creatingAccount": "Creating Account...", + "backToLogin": "Back to Login", + "showPassword": "Show password", + "hidePassword": "Hide password", + "emailRequired": "Email is required", + "passwordRequired": "Password is required", + "confirmPasswordRequired": "Please confirm your password", + "passwordsDoNotMatch": "Passwords do not match", + "passwordTooShort": "Password must be at least 8 characters", + "passwordStrengthError": "Password must include lowercase, uppercase, number, and special character", + "registrationFailed": "Registration failed", + "accountCreated": "Account created! Please check your email to verify your account." + }, + "forgotPassword": { + "titleForm": "Reset Password", + "titleSuccess": "Email Sent", + "description": "Enter your email address and we'll send you a link to reset your password.", + "emailPlaceholder": "Email", + "sendResetLinkButton": "Send Reset Link", + "sending": "Sending...", + "backToLogin": "Back to Login", + "resendEmail": "Resend Email", + "successMessage": "We've sent a password reset link to {email}. Please check your inbox.", + "emailRequired": "Email is required", + "sendFailed": "Failed to send reset email" + } +} diff --git a/packages/shared-i18n/src/translations/auth/es.json b/packages/shared-i18n/src/translations/auth/es.json new file mode 100644 index 000000000..8bbd9f24a --- /dev/null +++ b/packages/shared-i18n/src/translations/auth/es.json @@ -0,0 +1,59 @@ +{ + "login": { + "title": "Iniciar Sesión", + "subtitle": "Inicia sesión con tu cuenta Mana", + "emailPlaceholder": "Correo electrónico", + "passwordPlaceholder": "Contraseña", + "rememberMe": "Recordarme", + "forgotPassword": "¿Olvidaste tu contraseña?", + "signInButton": "Iniciar Sesión", + "signingIn": "Iniciando sesión...", + "success": "¡Éxito!", + "orDivider": "o", + "noAccount": "¿No tienes cuenta?", + "createAccount": "Crear una", + "skipToForm": "Ir al formulario de inicio de sesión", + "showPassword": "Mostrar contraseña", + "hidePassword": "Ocultar contraseña", + "emailRequired": "El correo electrónico es obligatorio", + "emailInvalid": "Por favor ingresa un correo electrónico válido", + "passwordRequired": "La contraseña es obligatoria", + "signInFailed": "Error al iniciar sesión", + "googleSignInFailed": "Error al iniciar sesión con Google", + "signInSuccess": "Sesión iniciada correctamente. Redirigiendo...", + "googleSignInSuccess": "Sesión iniciada con Google correctamente. Redirigiendo..." + }, + "register": { + "title": "Crear Cuenta", + "emailPlaceholder": "Correo electrónico", + "passwordPlaceholder": "Contraseña", + "confirmPasswordPlaceholder": "Confirmar Contraseña", + "passwordRequirements": "La contraseña debe tener al menos 8 caracteres con minúsculas, mayúsculas, números y caracteres especiales.", + "createAccountButton": "Crear Cuenta", + "creatingAccount": "Creando cuenta...", + "backToLogin": "Volver al inicio de sesión", + "showPassword": "Mostrar contraseña", + "hidePassword": "Ocultar contraseña", + "emailRequired": "El correo electrónico es obligatorio", + "passwordRequired": "La contraseña es obligatoria", + "confirmPasswordRequired": "Por favor confirma tu contraseña", + "passwordsDoNotMatch": "Las contraseñas no coinciden", + "passwordTooShort": "La contraseña debe tener al menos 8 caracteres", + "passwordStrengthError": "La contraseña debe incluir minúsculas, mayúsculas, números y caracteres especiales", + "registrationFailed": "Error en el registro", + "accountCreated": "¡Cuenta creada! Por favor revisa tu correo para verificar tu cuenta." + }, + "forgotPassword": { + "titleForm": "Restablecer Contraseña", + "titleSuccess": "Correo Enviado", + "description": "Ingresa tu correo electrónico y te enviaremos un enlace para restablecer tu contraseña.", + "emailPlaceholder": "Correo electrónico", + "sendResetLinkButton": "Enviar Enlace", + "sending": "Enviando...", + "backToLogin": "Volver al inicio de sesión", + "resendEmail": "Reenviar correo", + "successMessage": "Hemos enviado un enlace de restablecimiento a {email}. Por favor revisa tu bandeja de entrada.", + "emailRequired": "El correo electrónico es obligatorio", + "sendFailed": "No se pudo enviar el correo" + } +} diff --git a/packages/shared-i18n/src/translations/auth/fr.json b/packages/shared-i18n/src/translations/auth/fr.json new file mode 100644 index 000000000..7e666ffaf --- /dev/null +++ b/packages/shared-i18n/src/translations/auth/fr.json @@ -0,0 +1,59 @@ +{ + "login": { + "title": "Connexion", + "subtitle": "Connectez-vous avec votre compte Mana", + "emailPlaceholder": "Email", + "passwordPlaceholder": "Mot de passe", + "rememberMe": "Se souvenir de moi", + "forgotPassword": "Mot de passe oublié ?", + "signInButton": "Se connecter", + "signingIn": "Connexion...", + "success": "Succès !", + "orDivider": "ou", + "noAccount": "Pas encore de compte ?", + "createAccount": "Créer un compte", + "skipToForm": "Aller au formulaire de connexion", + "showPassword": "Afficher le mot de passe", + "hidePassword": "Masquer le mot de passe", + "emailRequired": "L'email est requis", + "emailInvalid": "Veuillez entrer une adresse email valide", + "passwordRequired": "Le mot de passe est requis", + "signInFailed": "Échec de la connexion", + "googleSignInFailed": "Échec de la connexion Google", + "signInSuccess": "Connexion réussie. Redirection...", + "googleSignInSuccess": "Connexion Google réussie. Redirection..." + }, + "register": { + "title": "Créer un compte", + "emailPlaceholder": "Email", + "passwordPlaceholder": "Mot de passe", + "confirmPasswordPlaceholder": "Confirmer le mot de passe", + "passwordRequirements": "Le mot de passe doit contenir au moins 8 caractères avec des minuscules, majuscules, chiffres et caractères spéciaux.", + "createAccountButton": "Créer un compte", + "creatingAccount": "Création du compte...", + "backToLogin": "Retour à la connexion", + "showPassword": "Afficher le mot de passe", + "hidePassword": "Masquer le mot de passe", + "emailRequired": "L'email est requis", + "passwordRequired": "Le mot de passe est requis", + "confirmPasswordRequired": "Veuillez confirmer votre mot de passe", + "passwordsDoNotMatch": "Les mots de passe ne correspondent pas", + "passwordTooShort": "Le mot de passe doit contenir au moins 8 caractères", + "passwordStrengthError": "Le mot de passe doit contenir des minuscules, majuscules, chiffres et caractères spéciaux", + "registrationFailed": "Échec de l'inscription", + "accountCreated": "Compte créé ! Veuillez vérifier votre email pour activer votre compte." + }, + "forgotPassword": { + "titleForm": "Réinitialiser le mot de passe", + "titleSuccess": "Email envoyé", + "description": "Entrez votre adresse email et nous vous enverrons un lien pour réinitialiser votre mot de passe.", + "emailPlaceholder": "Email", + "sendResetLinkButton": "Envoyer le lien", + "sending": "Envoi en cours...", + "backToLogin": "Retour à la connexion", + "resendEmail": "Renvoyer l'email", + "successMessage": "Nous avons envoyé un lien de réinitialisation à {email}. Veuillez vérifier votre boîte de réception.", + "emailRequired": "L'email est requis", + "sendFailed": "Impossible d'envoyer l'email" + } +} diff --git a/packages/shared-i18n/src/translations/auth/index.ts b/packages/shared-i18n/src/translations/auth/index.ts new file mode 100644 index 000000000..df67a0943 --- /dev/null +++ b/packages/shared-i18n/src/translations/auth/index.ts @@ -0,0 +1,123 @@ +/** + * Auth translations exports + */ + +import en from './en.json'; +import de from './de.json'; +import it from './it.json'; +import fr from './fr.json'; +import es from './es.json'; + +export { en, de, it, fr, es }; + +/** + * Auth translations type structure + */ +export interface AuthTranslations { + login: { + title: string; + subtitle: string; + emailPlaceholder: string; + passwordPlaceholder: string; + rememberMe: string; + forgotPassword: string; + signInButton: string; + signingIn: string; + success: string; + orDivider: string; + noAccount: string; + createAccount: string; + skipToForm: string; + showPassword: string; + hidePassword: string; + emailRequired: string; + emailInvalid: string; + passwordRequired: string; + signInFailed: string; + googleSignInFailed: string; + signInSuccess: string; + googleSignInSuccess: string; + }; + register: { + title: string; + emailPlaceholder: string; + passwordPlaceholder: string; + confirmPasswordPlaceholder: string; + passwordRequirements: string; + createAccountButton: string; + creatingAccount: string; + backToLogin: string; + showPassword: string; + hidePassword: string; + emailRequired: string; + passwordRequired: string; + confirmPasswordRequired: string; + passwordsDoNotMatch: string; + passwordTooShort: string; + passwordStrengthError: string; + registrationFailed: string; + accountCreated: string; + }; + forgotPassword: { + titleForm: string; + titleSuccess: string; + description: string; + emailPlaceholder: string; + sendResetLinkButton: string; + sending: string; + backToLogin: string; + resendEmail: string; + successMessage: string; + emailRequired: string; + sendFailed: string; + }; +} + +/** + * Supported auth locales + */ +export type AuthLocale = 'en' | 'de' | 'it' | 'fr' | 'es'; + +/** + * All auth translations by locale + */ +export const authTranslations: Record = { + en, + de, + it, + fr, + es +}; + +/** + * Get auth translations by locale + */ +export function getAuthTranslations(locale: string): AuthTranslations { + const supportedLocale = locale as AuthLocale; + if (supportedLocale in authTranslations) { + return authTranslations[supportedLocale]; + } + // Default to English + return authTranslations.en; +} + +/** + * Get login translations for a specific locale + */ +export function getLoginTranslations(locale: string): AuthTranslations['login'] { + return getAuthTranslations(locale).login; +} + +/** + * Get register translations for a specific locale + */ +export function getRegisterTranslations(locale: string): AuthTranslations['register'] { + return getAuthTranslations(locale).register; +} + +/** + * Get forgot password translations for a specific locale + */ +export function getForgotPasswordTranslations(locale: string): AuthTranslations['forgotPassword'] { + return getAuthTranslations(locale).forgotPassword; +} diff --git a/packages/shared-i18n/src/translations/auth/it.json b/packages/shared-i18n/src/translations/auth/it.json new file mode 100644 index 000000000..64d8a30fb --- /dev/null +++ b/packages/shared-i18n/src/translations/auth/it.json @@ -0,0 +1,59 @@ +{ + "login": { + "title": "Accedi", + "subtitle": "Accedi con il tuo account Mana", + "emailPlaceholder": "Email", + "passwordPlaceholder": "Password", + "rememberMe": "Ricordami", + "forgotPassword": "Password dimenticata?", + "signInButton": "Accedi", + "signingIn": "Accesso in corso...", + "success": "Successo!", + "orDivider": "oppure", + "noAccount": "Non hai un account?", + "createAccount": "Creane uno", + "skipToForm": "Vai al modulo di accesso", + "showPassword": "Mostra password", + "hidePassword": "Nascondi password", + "emailRequired": "L'email è obbligatoria", + "emailInvalid": "Inserisci un indirizzo email valido", + "passwordRequired": "La password è obbligatoria", + "signInFailed": "Accesso fallito", + "googleSignInFailed": "Accesso con Google fallito", + "signInSuccess": "Accesso effettuato. Reindirizzamento...", + "googleSignInSuccess": "Accesso con Google effettuato. Reindirizzamento..." + }, + "register": { + "title": "Crea Account", + "emailPlaceholder": "Email", + "passwordPlaceholder": "Password", + "confirmPasswordPlaceholder": "Conferma Password", + "passwordRequirements": "La password deve contenere almeno 8 caratteri con minuscole, maiuscole, numeri e caratteri speciali.", + "createAccountButton": "Crea Account", + "creatingAccount": "Creazione account...", + "backToLogin": "Torna al login", + "showPassword": "Mostra password", + "hidePassword": "Nascondi password", + "emailRequired": "L'email è obbligatoria", + "passwordRequired": "La password è obbligatoria", + "confirmPasswordRequired": "Conferma la tua password", + "passwordsDoNotMatch": "Le password non corrispondono", + "passwordTooShort": "La password deve contenere almeno 8 caratteri", + "passwordStrengthError": "La password deve contenere minuscole, maiuscole, numeri e caratteri speciali", + "registrationFailed": "Registrazione fallita", + "accountCreated": "Account creato! Controlla la tua email per verificare il tuo account." + }, + "forgotPassword": { + "titleForm": "Reimposta Password", + "titleSuccess": "Email Inviata", + "description": "Inserisci il tuo indirizzo email e ti invieremo un link per reimpostare la password.", + "emailPlaceholder": "Email", + "sendResetLinkButton": "Invia Link", + "sending": "Invio in corso...", + "backToLogin": "Torna al login", + "resendEmail": "Invia di nuovo", + "successMessage": "Abbiamo inviato un link per reimpostare la password a {email}. Controlla la tua casella di posta.", + "emailRequired": "L'email è obbligatoria", + "sendFailed": "Impossibile inviare l'email" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f4b0544f0..93b89199e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -617,7 +617,7 @@ importers: version: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-router: specifier: ~6.0.15 - version: 6.0.15(xryufifz5wj5kwzsjwy243gnce) + version: 6.0.15(6vtqpixpsaohn2qohrl6z3hsuy) expo-status-bar: specifier: ~3.0.8 version: 3.0.8(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) @@ -742,6 +742,9 @@ importers: '@supabase/supabase-js': specifier: ^2.81.1 version: 2.81.1 + svelte-i18n: + specifier: ^4.0.0 + version: 4.0.1(svelte@5.43.14) devDependencies: '@playwright/test': specifier: ^1.51.0 @@ -877,7 +880,7 @@ importers: version: 8.0.9(expo@54.0.13)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-router: specifier: ~6.0.10 - version: 6.0.15(f27ak5avlguc2jker6h5375iqq) + version: 6.0.15(h2kortjfz5ptrdu2vyrckew36u) expo-secure-store: specifier: ^15.0.7 version: 15.0.7(expo@54.0.13) @@ -1123,7 +1126,7 @@ importers: version: 9.39.1 '@nestjs/cli': specifier: ^11.0.0 - version: 11.0.12(@types/node@22.19.1) + version: 11.0.12(@types/node@22.19.1)(esbuild@0.25.12) '@nestjs/schematics': specifier: ^11.0.0 version: 11.0.9(chokidar@4.0.3)(typescript@5.9.3) @@ -1156,7 +1159,7 @@ importers: version: 16.5.0 jest: specifier: ^30.0.0 - version: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + version: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) prettier: specifier: ^3.4.2 version: 3.6.2 @@ -1168,10 +1171,10 @@ importers: version: 7.1.4 ts-jest: specifier: ^29.2.5 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3) ts-loader: specifier: ^9.5.2 - version: 9.5.4(typescript@5.9.3)(webpack@5.100.2) + version: 9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.25.12)) ts-node: specifier: ^10.9.2 version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) @@ -1346,7 +1349,7 @@ importers: version: 18.2.0(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) expo-router: specifier: ~6.0.8 - version: 6.0.15(5rdkykxvwgevjabrjzqisvsnwm) + version: 6.0.15(fhrhlvzxrnwl4ouzxvkehjwypa) expo-secure-store: specifier: ~15.0.7 version: 15.0.7(expo@54.0.25) @@ -1581,6 +1584,34 @@ importers: specifier: ^7.1.7 version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.20.6)(yaml@2.8.1) + packages/manadeck-database: + dependencies: + drizzle-orm: + specifier: ^0.36.0 + version: 0.36.4(@types/react@19.2.7)(postgres@3.4.7)(react@19.1.0) + postgres: + specifier: ^3.4.5 + version: 3.4.7 + devDependencies: + '@supabase/supabase-js': + specifier: ^2.81.1 + version: 2.81.1 + '@types/node': + specifier: ^22.10.0 + version: 22.19.1 + dotenv-cli: + specifier: ^7.4.0 + version: 7.4.4 + drizzle-kit: + specifier: ^0.28.0 + version: 0.28.1 + tsx: + specifier: ^4.19.0 + version: 4.20.6 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + packages/shared-auth: dependencies: base64-js: @@ -2447,6 +2478,9 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} + '@drizzle-team/brocli@0.10.2': + resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} + '@egjs/hammerjs@2.0.17': resolution: {integrity: sha512-XQsZgjm2EcVUiZQf11UBJQfmZeEmOW8DpI1gsFeln6w0ae0ii4dMQEQ0kjl6DspdWX1aGY1/loyXnP0JS06e/A==} engines: {node: '>=0.8.0'} @@ -2482,6 +2516,14 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@esbuild-kit/core-utils@3.3.2': + resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' + + '@esbuild-kit/esm-loader@2.6.5': + resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' + '@esbuild/aix-ppc64@0.19.12': resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} engines: {node: '>=12'} @@ -2500,6 +2542,12 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm64@0.19.12': resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} engines: {node: '>=12'} @@ -2518,6 +2566,12 @@ packages: cpu: [arm64] os: [android] + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + '@esbuild/android-arm@0.19.12': resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} engines: {node: '>=12'} @@ -2536,6 +2590,12 @@ packages: cpu: [arm] os: [android] + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + '@esbuild/android-x64@0.19.12': resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} engines: {node: '>=12'} @@ -2554,6 +2614,12 @@ packages: cpu: [x64] os: [android] + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-arm64@0.19.12': resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} engines: {node: '>=12'} @@ -2572,6 +2638,12 @@ packages: cpu: [arm64] os: [darwin] + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + '@esbuild/darwin-x64@0.19.12': resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} engines: {node: '>=12'} @@ -2590,6 +2662,12 @@ packages: cpu: [x64] os: [darwin] + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-arm64@0.19.12': resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} engines: {node: '>=12'} @@ -2608,6 +2686,12 @@ packages: cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + '@esbuild/freebsd-x64@0.19.12': resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} engines: {node: '>=12'} @@ -2626,6 +2710,12 @@ packages: cpu: [x64] os: [freebsd] + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm64@0.19.12': resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} engines: {node: '>=12'} @@ -2644,6 +2734,12 @@ packages: cpu: [arm64] os: [linux] + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + '@esbuild/linux-arm@0.19.12': resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} engines: {node: '>=12'} @@ -2662,6 +2758,12 @@ packages: cpu: [arm] os: [linux] + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-ia32@0.19.12': resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} engines: {node: '>=12'} @@ -2680,6 +2782,12 @@ packages: cpu: [ia32] os: [linux] + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-loong64@0.19.12': resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} engines: {node: '>=12'} @@ -2698,6 +2806,12 @@ packages: cpu: [loong64] os: [linux] + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-mips64el@0.19.12': resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} engines: {node: '>=12'} @@ -2716,6 +2830,12 @@ packages: cpu: [mips64el] os: [linux] + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-ppc64@0.19.12': resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} engines: {node: '>=12'} @@ -2734,6 +2854,12 @@ packages: cpu: [ppc64] os: [linux] + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-riscv64@0.19.12': resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} engines: {node: '>=12'} @@ -2752,6 +2878,12 @@ packages: cpu: [riscv64] os: [linux] + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-s390x@0.19.12': resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} engines: {node: '>=12'} @@ -2770,6 +2902,12 @@ packages: cpu: [s390x] os: [linux] + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + '@esbuild/linux-x64@0.19.12': resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} engines: {node: '>=12'} @@ -2794,6 +2932,12 @@ packages: cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + '@esbuild/netbsd-x64@0.19.12': resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} engines: {node: '>=12'} @@ -2818,6 +2962,12 @@ packages: cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + '@esbuild/openbsd-x64@0.19.12': resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} engines: {node: '>=12'} @@ -2842,6 +2992,12 @@ packages: cpu: [arm64] os: [openharmony] + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + '@esbuild/sunos-x64@0.19.12': resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} engines: {node: '>=12'} @@ -2860,6 +3016,12 @@ packages: cpu: [x64] os: [sunos] + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-arm64@0.19.12': resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} engines: {node: '>=12'} @@ -2878,6 +3040,12 @@ packages: cpu: [arm64] os: [win32] + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-ia32@0.19.12': resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} engines: {node: '>=12'} @@ -2896,6 +3064,12 @@ packages: cpu: [ia32] os: [win32] + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + '@esbuild/win32-x64@0.19.12': resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} engines: {node: '>=12'} @@ -7062,6 +7236,14 @@ packages: dot-case@3.0.4: resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dotenv-cli@7.4.4: + resolution: {integrity: sha512-XkBYCG0tPIes+YZr4SpfFv76SQrV/LeCE8CI7JSEMi3VR9MvTihCGTOtbIexD6i2mXF+6px7trb1imVCXSNMDw==} + hasBin: true + + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + dotenv-expand@11.0.7: resolution: {integrity: sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==} engines: {node: '>=12'} @@ -7078,6 +7260,102 @@ packages: resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} + drizzle-kit@0.28.1: + resolution: {integrity: sha512-JimOV+ystXTWMgZkLHYHf2w3oS28hxiH1FR0dkmJLc7GHzdGJoJAQtQS5DRppnabsRZwE2U1F6CuezVBgmsBBQ==} + hasBin: true + + drizzle-orm@0.36.4: + resolution: {integrity: sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA==} + peerDependencies: + '@aws-sdk/client-rds-data': '>=3' + '@cloudflare/workers-types': '>=3' + '@electric-sql/pglite': '>=0.2.0' + '@libsql/client': '>=0.10.0' + '@libsql/client-wasm': '>=0.10.0' + '@neondatabase/serverless': '>=0.10.0' + '@op-engineering/op-sqlite': '>=2' + '@opentelemetry/api': ^1.4.1 + '@planetscale/database': '>=1' + '@prisma/client': '*' + '@tidbcloud/serverless': '*' + '@types/better-sqlite3': '*' + '@types/pg': '*' + '@types/react': '>=18' + '@types/sql.js': '*' + '@vercel/postgres': '>=0.8.0' + '@xata.io/client': '*' + better-sqlite3: '>=7' + bun-types: '*' + expo-sqlite: '>=14.0.0' + knex: '*' + kysely: '*' + mysql2: '>=2' + pg: '>=8' + postgres: '>=3' + prisma: '*' + react: '>=18' + sql.js: '>=1' + sqlite3: '>=5' + peerDependenciesMeta: + '@aws-sdk/client-rds-data': + optional: true + '@cloudflare/workers-types': + optional: true + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + '@libsql/client-wasm': + optional: true + '@neondatabase/serverless': + optional: true + '@op-engineering/op-sqlite': + optional: true + '@opentelemetry/api': + optional: true + '@planetscale/database': + optional: true + '@prisma/client': + optional: true + '@tidbcloud/serverless': + optional: true + '@types/better-sqlite3': + optional: true + '@types/pg': + optional: true + '@types/react': + optional: true + '@types/sql.js': + optional: true + '@vercel/postgres': + optional: true + '@xata.io/client': + optional: true + better-sqlite3: + optional: true + bun-types: + optional: true + expo-sqlite: + optional: true + knex: + optional: true + kysely: + optional: true + mysql2: + optional: true + pg: + optional: true + postgres: + optional: true + prisma: + optional: true + react: + optional: true + sql.js: + optional: true + sqlite3: + optional: true + dset@3.1.4: resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} engines: {node: '>=4'} @@ -7211,6 +7489,16 @@ packages: esast-util-from-js@2.0.1: resolution: {integrity: sha512-8Ja+rNJ0Lt56Pcf3TAmpBZjmx8ZcK5Ts4cAzIOjsjevg9oSXJnl6SUQ2EevU8tv3h6ZLWmoKL5H4fgWvdvfETw==} + esbuild-register@3.6.0: + resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==} + peerDependencies: + esbuild: '>=0.12 <1' + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -10699,6 +10987,10 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + postgres@3.4.7: + resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + engines: {node: '>=12'} + posthog-js@1.297.2: resolution: {integrity: sha512-pDtCKHpKegV1D5Yk9PBmkFwI9FMnLJm0TsBO5c5/PhPq5Om4y/t+1qqbNcLCdLajkuYl2px9UlRTzycQ6W7Vmw==} @@ -14344,6 +14636,8 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 + '@drizzle-team/brocli@0.10.2': {} + '@egjs/hammerjs@2.0.17': dependencies: '@types/hammerjs': 2.0.46 @@ -14387,6 +14681,16 @@ snapshots: tslib: 2.8.1 optional: true + '@esbuild-kit/core-utils@3.3.2': + dependencies: + esbuild: 0.18.20 + source-map-support: 0.5.21 + + '@esbuild-kit/esm-loader@2.6.5': + dependencies: + '@esbuild-kit/core-utils': 3.3.2 + get-tsconfig: 4.13.0 + '@esbuild/aix-ppc64@0.19.12': optional: true @@ -14396,6 +14700,9 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true + '@esbuild/android-arm64@0.18.20': + optional: true + '@esbuild/android-arm64@0.19.12': optional: true @@ -14405,6 +14712,9 @@ snapshots: '@esbuild/android-arm64@0.25.12': optional: true + '@esbuild/android-arm@0.18.20': + optional: true + '@esbuild/android-arm@0.19.12': optional: true @@ -14414,6 +14724,9 @@ snapshots: '@esbuild/android-arm@0.25.12': optional: true + '@esbuild/android-x64@0.18.20': + optional: true + '@esbuild/android-x64@0.19.12': optional: true @@ -14423,6 +14736,9 @@ snapshots: '@esbuild/android-x64@0.25.12': optional: true + '@esbuild/darwin-arm64@0.18.20': + optional: true + '@esbuild/darwin-arm64@0.19.12': optional: true @@ -14432,6 +14748,9 @@ snapshots: '@esbuild/darwin-arm64@0.25.12': optional: true + '@esbuild/darwin-x64@0.18.20': + optional: true + '@esbuild/darwin-x64@0.19.12': optional: true @@ -14441,6 +14760,9 @@ snapshots: '@esbuild/darwin-x64@0.25.12': optional: true + '@esbuild/freebsd-arm64@0.18.20': + optional: true + '@esbuild/freebsd-arm64@0.19.12': optional: true @@ -14450,6 +14772,9 @@ snapshots: '@esbuild/freebsd-arm64@0.25.12': optional: true + '@esbuild/freebsd-x64@0.18.20': + optional: true + '@esbuild/freebsd-x64@0.19.12': optional: true @@ -14459,6 +14784,9 @@ snapshots: '@esbuild/freebsd-x64@0.25.12': optional: true + '@esbuild/linux-arm64@0.18.20': + optional: true + '@esbuild/linux-arm64@0.19.12': optional: true @@ -14468,6 +14796,9 @@ snapshots: '@esbuild/linux-arm64@0.25.12': optional: true + '@esbuild/linux-arm@0.18.20': + optional: true + '@esbuild/linux-arm@0.19.12': optional: true @@ -14477,6 +14808,9 @@ snapshots: '@esbuild/linux-arm@0.25.12': optional: true + '@esbuild/linux-ia32@0.18.20': + optional: true + '@esbuild/linux-ia32@0.19.12': optional: true @@ -14486,6 +14820,9 @@ snapshots: '@esbuild/linux-ia32@0.25.12': optional: true + '@esbuild/linux-loong64@0.18.20': + optional: true + '@esbuild/linux-loong64@0.19.12': optional: true @@ -14495,6 +14832,9 @@ snapshots: '@esbuild/linux-loong64@0.25.12': optional: true + '@esbuild/linux-mips64el@0.18.20': + optional: true + '@esbuild/linux-mips64el@0.19.12': optional: true @@ -14504,6 +14844,9 @@ snapshots: '@esbuild/linux-mips64el@0.25.12': optional: true + '@esbuild/linux-ppc64@0.18.20': + optional: true + '@esbuild/linux-ppc64@0.19.12': optional: true @@ -14513,6 +14856,9 @@ snapshots: '@esbuild/linux-ppc64@0.25.12': optional: true + '@esbuild/linux-riscv64@0.18.20': + optional: true + '@esbuild/linux-riscv64@0.19.12': optional: true @@ -14522,6 +14868,9 @@ snapshots: '@esbuild/linux-riscv64@0.25.12': optional: true + '@esbuild/linux-s390x@0.18.20': + optional: true + '@esbuild/linux-s390x@0.19.12': optional: true @@ -14531,6 +14880,9 @@ snapshots: '@esbuild/linux-s390x@0.25.12': optional: true + '@esbuild/linux-x64@0.18.20': + optional: true + '@esbuild/linux-x64@0.19.12': optional: true @@ -14543,6 +14895,9 @@ snapshots: '@esbuild/netbsd-arm64@0.25.12': optional: true + '@esbuild/netbsd-x64@0.18.20': + optional: true + '@esbuild/netbsd-x64@0.19.12': optional: true @@ -14555,6 +14910,9 @@ snapshots: '@esbuild/openbsd-arm64@0.25.12': optional: true + '@esbuild/openbsd-x64@0.18.20': + optional: true + '@esbuild/openbsd-x64@0.19.12': optional: true @@ -14567,6 +14925,9 @@ snapshots: '@esbuild/openharmony-arm64@0.25.12': optional: true + '@esbuild/sunos-x64@0.18.20': + optional: true + '@esbuild/sunos-x64@0.19.12': optional: true @@ -14576,6 +14937,9 @@ snapshots: '@esbuild/sunos-x64@0.25.12': optional: true + '@esbuild/win32-arm64@0.18.20': + optional: true + '@esbuild/win32-arm64@0.19.12': optional: true @@ -14585,6 +14949,9 @@ snapshots: '@esbuild/win32-arm64@0.25.12': optional: true + '@esbuild/win32-ia32@0.18.20': + optional: true + '@esbuild/win32-ia32@0.19.12': optional: true @@ -14594,6 +14961,9 @@ snapshots: '@esbuild/win32-ia32@0.25.12': optional: true + '@esbuild/win32-x64@0.18.20': + optional: true + '@esbuild/win32-x64@0.19.12': optional: true @@ -14742,7 +15112,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.18.3 optionalDependencies: - expo-router: 6.0.15(f27ak5avlguc2jker6h5375iqq) + expo-router: 6.0.15(h2kortjfz5ptrdu2vyrckew36u) react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -14819,7 +15189,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.18.3 optionalDependencies: - expo-router: 6.0.15(5rdkykxvwgevjabrjzqisvsnwm) + expo-router: 6.0.15(fhrhlvzxrnwl4ouzxvkehjwypa) react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -14896,7 +15266,7 @@ snapshots: wrap-ansi: 7.0.0 ws: 8.18.3 optionalDependencies: - expo-router: 6.0.15(ezsgqtdheoeph6fqqf6q7ixwnu) + expo-router: 6.0.15(6vtqpixpsaohn2qohrl6z3hsuy) react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) transitivePeerDependencies: - '@modelcontextprotocol/sdk' @@ -15859,7 +16229,7 @@ snapshots: - supports-color - ts-node - '@jest/core@30.2.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))': + '@jest/core@30.2.0(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))': dependencies: '@jest/console': 30.2.0 '@jest/pattern': 30.0.1 @@ -15874,7 +16244,7 @@ snapshots: exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.2.0 - jest-config: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-haste-map: 30.2.0 jest-message-util: 30.2.0 jest-regex-util: 30.0.1 @@ -16281,7 +16651,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/cli@11.0.12(@types/node@22.19.1)': + '@nestjs/cli@11.0.12(@types/node@22.19.1)(esbuild@0.25.12)': dependencies: '@angular-devkit/core': 19.2.19(chokidar@4.0.3) '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) @@ -16292,14 +16662,14 @@ snapshots: chokidar: 4.0.3 cli-table3: 0.6.5 commander: 4.1.1 - fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.100.2) + fork-ts-checker-webpack-plugin: 9.1.0(typescript@5.9.3)(webpack@5.100.2(esbuild@0.25.12)) glob: 12.0.0 node-emoji: 1.11.0 ora: 5.4.1 tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 typescript: 5.9.3 - webpack: 5.100.2 + webpack: 5.100.2(esbuild@0.25.12) webpack-node-externals: 3.0.0 transitivePeerDependencies: - '@types/node' @@ -17774,7 +18144,7 @@ snapshots: optionalDependencies: jest: 29.5.0(@types/node@18.15.11)(ts-node@10.9.2(@types/node@18.15.11)(typescript@5.9.3)) - '@testing-library/react-native@13.3.3(jest@30.2.0)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react-native@13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.25.12)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.2.0 picocolors: 1.1.1 @@ -17784,10 +18154,10 @@ snapshots: react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: - jest: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + jest: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) optional: true - '@testing-library/react-native@13.3.3(jest@30.2.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': + '@testing-library/react-native@13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.25.12)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: jest-matcher-utils: 30.2.0 picocolors: 1.1.1 @@ -17797,7 +18167,7 @@ snapshots: react-test-renderer: 19.1.0(react@19.1.0) redent: 3.0.0 optionalDependencies: - jest: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + jest: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) optional: true '@tokenizer/inflate@0.2.7': @@ -20261,18 +20631,42 @@ snapshots: no-case: 3.0.4 tslib: 2.8.1 + dotenv-cli@7.4.4: + dependencies: + cross-spawn: 7.0.6 + dotenv: 16.6.1 + dotenv-expand: 10.0.0 + minimist: 1.2.8 + + dotenv-expand@10.0.0: {} + dotenv-expand@11.0.7: dependencies: - dotenv: 16.4.7 + dotenv: 16.6.1 dotenv-expand@12.0.1: dependencies: - dotenv: 16.4.7 + dotenv: 16.6.1 dotenv@16.4.7: {} dotenv@16.6.1: {} + drizzle-kit@0.28.1: + dependencies: + '@drizzle-team/brocli': 0.10.2 + '@esbuild-kit/esm-loader': 2.6.5 + esbuild: 0.19.12 + esbuild-register: 3.6.0(esbuild@0.19.12) + transitivePeerDependencies: + - supports-color + + drizzle-orm@0.36.4(@types/react@19.2.7)(postgres@3.4.7)(react@19.1.0): + optionalDependencies: + '@types/react': 19.2.7 + postgres: 3.4.7 + react: 19.1.0 + dset@3.1.4: {} dunder-proto@1.0.1: @@ -20487,6 +20881,46 @@ snapshots: esast-util-from-estree: 2.0.0 vfile-message: 4.0.3 + esbuild-register@3.6.0(esbuild@0.19.12): + dependencies: + debug: 4.4.3 + esbuild: 0.19.12 + transitivePeerDependencies: + - supports-color + + esbuild-register@3.6.0(esbuild@0.25.12): + dependencies: + debug: 4.4.3 + esbuild: 0.25.12 + transitivePeerDependencies: + - supports-color + optional: true + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + esbuild@0.19.12: optionalDependencies: '@esbuild/aix-ppc64': 0.19.12 @@ -20598,7 +21032,7 @@ snapshots: '@typescript-eslint/eslint-plugin': 8.47.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-expo: 1.0.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) @@ -20672,7 +21106,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -20697,14 +21131,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -20776,7 +21210,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.1(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.47.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -21371,7 +21805,7 @@ snapshots: expo-device@8.0.9(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) ua-parser-js: 0.7.41 expo-document-picker@14.0.7(expo@54.0.25): @@ -21426,7 +21860,7 @@ snapshots: expo-image-loader@6.0.0(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-image-picker@17.0.8(expo@54.0.13): dependencies: @@ -21435,7 +21869,7 @@ snapshots: expo-image-picker@17.0.8(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-image-loader: 6.0.0(expo@54.0.25) expo-image@3.0.10(expo@54.0.25)(react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0): @@ -21502,7 +21936,7 @@ snapshots: expo-localization@17.0.7(expo@54.0.25)(react@19.1.0): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) react: 19.1.0 rtl-detect: 1.1.2 @@ -21574,21 +22008,21 @@ snapshots: react: 19.1.0 react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - expo-router@6.0.15(5rdkykxvwgevjabrjzqisvsnwm): + expo-router@6.0.15(6vtqpixpsaohn2qohrl6z3hsuy): dependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) '@expo/schema-utils': 0.1.7 '@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0) '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@react-navigation/native-stack': 7.7.0(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@react-navigation/native-stack': 7.7.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) client-only: 0.0.1 debug: 4.4.3 escape-string-regexp: 4.0.0 - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) - expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) + expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-server: 1.0.4 fast-deep-equal: 3.1.3 invariant: 2.2.4 @@ -21596,10 +22030,10 @@ snapshots: query-string: 7.1.3 react: 19.1.0 react-fast-compare: 3.2.2 - react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) semver: 7.6.3 server-only: 0.0.1 sf-symbols-typescript: 2.1.0 @@ -21607,13 +22041,13 @@ snapshots: use-latest-callback: 0.2.6(react@19.1.0) vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: - '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@testing-library/react-native': 13.3.3(jest@30.2.0)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@testing-library/react-native': 13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.25.12)))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) react-dom: 19.1.0(react@19.1.0) - react-native-gesture-handler: 2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2) + react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.25.12)) transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@types/react' @@ -21666,7 +22100,53 @@ snapshots: - '@types/react-dom' - supports-color - expo-router@6.0.15(f27ak5avlguc2jker6h5375iqq): + expo-router@6.0.15(fhrhlvzxrnwl4ouzxvkehjwypa): + dependencies: + '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@expo/schema-utils': 0.1.7 + '@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@react-navigation/native': 7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@react-navigation/native-stack': 7.7.0(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + client-only: 0.0.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) + expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo-server: 1.0.4 + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 19.1.0 + react-fast-compare: 3.2.2 + react-native: 0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-safe-area-context: 5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-screens: 4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + semver: 7.6.3 + server-only: 0.0.1 + sf-symbols-typescript: 2.1.0 + shallowequal: 1.1.0 + use-latest-callback: 0.2.6(react@19.1.0) + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + optionalDependencies: + '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + '@testing-library/react-native': 13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.25.12)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + react-dom: 19.1.0(react@19.1.0) + react-native-gesture-handler: 2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.25.12)) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + - '@types/react' + - '@types/react-dom' + - supports-color + + expo-router@6.0.15(h2kortjfz5ptrdu2vyrckew36u): dependencies: '@expo/metro-runtime': 6.1.2(expo@54.0.13)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) '@expo/schema-utils': 0.1.7 @@ -21700,58 +22180,12 @@ snapshots: vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) optionalDependencies: '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@testing-library/react-native': 13.3.3(jest@30.2.0)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) + '@testing-library/react-native': 13.3.3(jest@30.2.0(esbuild-register@3.6.0(esbuild@0.25.12)))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) react-dom: 19.1.0(react@19.1.0) react-native-gesture-handler: 2.28.0(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2) - transitivePeerDependencies: - - '@react-native-masked-view/masked-view' - - '@types/react' - - '@types/react-dom' - - supports-color - - expo-router@6.0.15(xryufifz5wj5kwzsjwy243gnce): - dependencies: - '@expo/metro-runtime': 6.1.2(expo@54.0.25)(react-dom@19.1.0(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@expo/schema-utils': 0.1.7 - '@radix-ui/react-slot': 1.2.0(@types/react@19.2.7)(react@19.1.0) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@react-navigation/bottom-tabs': 7.8.6(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@react-navigation/native': 7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@react-navigation/native-stack': 7.7.0(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - client-only: 0.0.1 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - expo-constants: 18.0.10(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0)) - expo-linking: 8.0.9(expo@54.0.25)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - expo-server: 1.0.4 - fast-deep-equal: 3.1.3 - invariant: 2.2.4 - nanoid: 3.3.11 - query-string: 7.1.3 - react: 19.1.0 - react-fast-compare: 3.2.2 - react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-safe-area-context: 5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-screens: 4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - semver: 7.6.3 - server-only: 0.0.1 - sf-symbols-typescript: 2.1.0 - shallowequal: 1.1.0 - use-latest-callback: 0.2.6(react@19.1.0) - vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - optionalDependencies: - '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - '@testing-library/react-native': 13.3.3(jest@30.2.0)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react-test-renderer@19.1.0(react@19.1.0))(react@19.1.0) - react-dom: 19.1.0(react@19.1.0) - react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) - react-native-web: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2) + react-server-dom-webpack: 19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.25.12)) transitivePeerDependencies: - '@react-native-masked-view/masked-view' - '@types/react' @@ -21764,7 +22198,7 @@ snapshots: expo-secure-store@15.0.7(expo@54.0.25): dependencies: - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) expo-server@1.0.4: {} @@ -21775,7 +22209,7 @@ snapshots: expo-splash-screen@31.0.11(expo@54.0.25): dependencies: '@expo/prebuild-config': 54.0.6(expo@54.0.25) - expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) + expo: 54.0.25(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.15)(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.7)(react@19.1.0))(react@19.1.0) transitivePeerDependencies: - supports-color @@ -22309,7 +22743,7 @@ snapshots: typescript: 5.7.2 webpack: 5.97.1 - fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.100.2): + fork-ts-checker-webpack-plugin@9.1.0(typescript@5.9.3)(webpack@5.100.2(esbuild@0.25.12)): dependencies: '@babel/code-frame': 7.27.1 chalk: 4.1.2 @@ -22324,7 +22758,7 @@ snapshots: semver: 7.7.3 tapable: 2.3.0 typescript: 5.9.3 - webpack: 5.100.2 + webpack: 5.100.2(esbuild@0.25.12) form-data@2.5.5: dependencies: @@ -23384,15 +23818,15 @@ snapshots: - supports-color - ts-node - jest-cli@30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): + jest-cli@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + '@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-util: 30.2.0 jest-validate: 30.2.0 yargs: 17.7.2 @@ -23465,7 +23899,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): + jest-config@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: '@babel/core': 7.28.5 '@jest/get-type': 30.1.0 @@ -23493,6 +23927,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 22.19.1 + esbuild-register: 3.6.0(esbuild@0.25.12) ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros @@ -24008,12 +24443,12 @@ snapshots: - supports-color - ts-node - jest@30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): + jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + '@jest/core': 30.2.0(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/types': 30.2.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + jest-cli: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -26100,6 +26535,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postgres@3.4.7: {} + posthog-js@1.297.2: dependencies: '@posthog/core': 1.5.5 @@ -26782,6 +27219,16 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 + react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2(esbuild@0.25.12)): + dependencies: + acorn-loose: 8.5.2 + neo-async: 2.6.2 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + webpack: 5.100.2(esbuild@0.25.12) + webpack-sources: 3.3.3 + optional: true + react-server-dom-webpack@19.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(webpack@5.100.2): dependencies: acorn-loose: 8.5.2 @@ -27924,6 +28371,17 @@ snapshots: ansi-escapes: 4.3.2 supports-hyperlinks: 2.3.0 + terser-webpack-plugin@5.3.14(esbuild@0.25.12)(webpack@5.100.2(esbuild@0.25.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.1 + webpack: 5.100.2(esbuild@0.25.12) + optionalDependencies: + esbuild: 0.25.12 + terser-webpack-plugin@5.3.14(webpack@5.100.2): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -28071,12 +28529,12 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.28.5) - ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@30.2.0)(jest@30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 - jest: 30.2.0(@types/node@22.19.1)(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) + jest: 30.2.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.25.12))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -28089,9 +28547,10 @@ snapshots: '@jest/transform': 30.2.0 '@jest/types': 30.2.0 babel-jest: 30.2.0(@babel/core@7.28.5) + esbuild: 0.25.12 jest-util: 30.2.0 - ts-loader@9.5.4(typescript@5.9.3)(webpack@5.100.2): + ts-loader@9.5.4(typescript@5.9.3)(webpack@5.100.2(esbuild@0.25.12)): dependencies: chalk: 4.1.2 enhanced-resolve: 5.18.3 @@ -28099,7 +28558,7 @@ snapshots: semver: 7.7.3 source-map: 0.7.6 typescript: 5.9.3 - webpack: 5.100.2 + webpack: 5.100.2(esbuild@0.25.12) ts-loader@9.5.4(typescript@5.9.3)(webpack@5.97.1): dependencies: @@ -28956,6 +29415,38 @@ snapshots: - esbuild - uglify-js + webpack@5.100.2(esbuild@0.25.12): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.28.0 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.3 + es-module-lexer: 1.7.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.14(esbuild@0.25.12)(webpack@5.100.2(esbuild@0.25.12)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.97.1: dependencies: '@types/eslint-scope': 3.7.7