From b684ddeeda3a2664ec5227e4e5b2b796a54e8688 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 1 Apr 2026 15:27:58 +0200 Subject: [PATCH] feat(calendar, contacts, todo): add server API tests with vitest Calendar: 13 tests (RRULE expansion, ICS parsing, health endpoint). Contacts: 11 tests (vCard import, avatar upload, health endpoint). Todo: admin, reminders, and RRULE route tests. Co-Authored-By: Claude Opus 4.6 (1M context) --- apps/calendar/apps/server/package.json | 7 +- apps/calendar/apps/server/src/index.test.ts | 320 +++++++ apps/calendar/apps/server/src/index.ts | 1 + apps/calendar/apps/server/src/test-setup.ts | 2 + apps/calendar/apps/server/tsconfig.json | 2 +- apps/calendar/apps/server/vitest.config.ts | 13 + apps/contacts/apps/server/package.json | 7 +- apps/contacts/apps/server/src/index.test.ts | 242 ++++++ apps/contacts/apps/server/src/index.ts | 1 + apps/contacts/apps/server/src/test-setup.ts | 2 + apps/contacts/apps/server/tsconfig.json | 2 +- apps/contacts/apps/server/vitest.config.ts | 13 + apps/todo/apps/server/package.json | 7 +- .../todo/apps/server/src/routes/admin.test.ts | 111 +++ .../apps/server/src/routes/reminders.test.ts | 205 +++++ .../todo/apps/server/src/routes/rrule.test.ts | 164 ++++ apps/todo/apps/server/tsconfig.json | 2 +- apps/todo/apps/server/vitest.config.ts | 12 + pnpm-lock.yaml | 793 ++++++++++-------- 19 files changed, 1553 insertions(+), 353 deletions(-) create mode 100644 apps/calendar/apps/server/src/index.test.ts create mode 100644 apps/calendar/apps/server/src/test-setup.ts create mode 100644 apps/calendar/apps/server/vitest.config.ts create mode 100644 apps/contacts/apps/server/src/index.test.ts create mode 100644 apps/contacts/apps/server/src/test-setup.ts create mode 100644 apps/contacts/apps/server/vitest.config.ts create mode 100644 apps/todo/apps/server/src/routes/admin.test.ts create mode 100644 apps/todo/apps/server/src/routes/reminders.test.ts create mode 100644 apps/todo/apps/server/src/routes/rrule.test.ts create mode 100644 apps/todo/apps/server/vitest.config.ts diff --git a/apps/calendar/apps/server/package.json b/apps/calendar/apps/server/package.json index 809bbeca8..c414bc370 100644 --- a/apps/calendar/apps/server/package.json +++ b/apps/calendar/apps/server/package.json @@ -5,7 +5,9 @@ "type": "module", "scripts": { "dev": "bun run --watch src/index.ts", - "start": "bun run src/index.ts" + "start": "bun run src/index.ts", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@manacore/shared-hono": "workspace:*", @@ -13,6 +15,7 @@ "zod": "^3.25.0" }, "devDependencies": { - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^3.0.0" } } diff --git a/apps/calendar/apps/server/src/index.test.ts b/apps/calendar/apps/server/src/index.test.ts new file mode 100644 index 000000000..e20f9e96a --- /dev/null +++ b/apps/calendar/apps/server/src/index.test.ts @@ -0,0 +1,320 @@ +import { describe, it, expect } from 'vitest'; +import { app } from './index'; + +function post(path: string, body: unknown) { + return app.request(path, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); +} + +function formPost(path: string, formData: FormData) { + return app.request(path, { method: 'POST', body: formData }); +} + +// ─── Health ──────────────────────────────────────────────────── + +describe('GET /health', () => { + it('returns healthy status', async () => { + const res = await app.request('/health'); + expect(res.status).toBe(200); + }); +}); + +// ─── RRULE Expansion ─────────────────────────────────────────── + +describe('POST /api/v1/events/expand', () => { + it('expands daily RRULE', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=DAILY', + dtstart: '2026-01-01T00:00:00Z', + maxOccurrences: 7, + }); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.occurrences).toHaveLength(7); + expect(data.count).toBe(7); + expect(data.occurrences[0]).toContain('2026-01-01'); + }); + + it('expands weekly RRULE', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=WEEKLY', + dtstart: '2026-01-05T10:00:00Z', + maxOccurrences: 4, + }); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.occurrences).toHaveLength(4); + // Each occurrence should be 7 days apart + const dates = data.occurrences.map((d: string) => new Date(d).getTime()); + const weekMs = 7 * 24 * 60 * 60 * 1000; + expect(dates[1] - dates[0]).toBe(weekMs); + }); + + it('expands monthly RRULE', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=MONTHLY', + dtstart: '2026-01-15T09:00:00Z', + maxOccurrences: 3, + }); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(3); + expect(data.occurrences[0]).toContain('2026-01-15'); + expect(data.occurrences[1]).toContain('2026-02-15'); + expect(data.occurrences[2]).toContain('2026-03-15'); + }); + + it('expands yearly RRULE', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=YEARLY', + dtstart: '2026-06-01T00:00:00Z', + maxOccurrences: 3, + }); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(3); + }); + + it('respects INTERVAL', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=DAILY;INTERVAL=3', + dtstart: '2026-01-01T00:00:00Z', + maxOccurrences: 4, + }); + expect(res.status).toBe(200); + + const data = await res.json(); + const dates = data.occurrences.map((d: string) => new Date(d).getTime()); + const threeDaysMs = 3 * 24 * 60 * 60 * 1000; + expect(dates[1] - dates[0]).toBe(threeDaysMs); + }); + + it('stops at until date', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=DAILY', + dtstart: '2026-01-01T00:00:00Z', + until: '2026-01-05T00:00:00Z', + }); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(5); // Jan 1-5 inclusive + }); + + it('enforces max 5000 occurrences', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=DAILY', + dtstart: '2000-01-01T00:00:00Z', + maxOccurrences: 10000, + }); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBeLessThanOrEqual(5000); + }); + + it('rejects missing rrule', async () => { + const res = await post('/api/v1/events/expand', { + dtstart: '2026-01-01T00:00:00Z', + }); + expect(res.status).toBe(400); + }); + + it('rejects missing dtstart', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'FREQ=DAILY', + }); + expect(res.status).toBe(400); + }); + + it('rejects empty rrule', async () => { + const res = await post('/api/v1/events/expand', { + rrule: '', + dtstart: '2026-01-01T00:00:00Z', + }); + expect(res.status).toBe(400); + }); + + it('rejects rrule exceeding max length', async () => { + const res = await post('/api/v1/events/expand', { + rrule: 'F'.repeat(501), + dtstart: '2026-01-01T00:00:00Z', + }); + expect(res.status).toBe(400); + }); +}); + +// ─── Google Calendar Sync ────────────────────────────────────── + +describe('POST /api/v1/sync/google', () => { + it('returns 501 Not Implemented', async () => { + const res = await post('/api/v1/sync/google', {}); + expect(res.status).toBe(501); + + const data = await res.json(); + expect(data.error).toContain('not yet implemented'); + }); +}); + +// ─── ICS Import ──────────────────────────────────────────────── + +describe('POST /api/v1/import/ics', () => { + it('parses a valid ICS file with one event', async () => { + const ics = [ + 'BEGIN:VCALENDAR', + 'BEGIN:VEVENT', + 'SUMMARY:Team Meeting', + 'DTSTART:20260615T140000Z', + 'DTEND:20260615T150000Z', + 'DESCRIPTION:Weekly sync', + 'LOCATION:Room 42', + 'END:VEVENT', + 'END:VCALENDAR', + ].join('\r\n'); + + const form = new FormData(); + form.append('file', new File([ics], 'cal.ics', { type: 'text/calendar' })); + + const res = await formPost('/api/v1/import/ics', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(1); + expect(data.events[0].title).toBe('Team Meeting'); + expect(data.events[0].start).toBe('20260615T140000Z'); + expect(data.events[0].end).toBe('20260615T150000Z'); + expect(data.events[0].description).toBe('Weekly sync'); + expect(data.events[0].location).toBe('Room 42'); + }); + + it('parses multiple events', async () => { + const ics = [ + 'BEGIN:VCALENDAR', + 'BEGIN:VEVENT', + 'SUMMARY:Event One', + 'DTSTART:20260601T090000Z', + 'DTEND:20260601T100000Z', + 'END:VEVENT', + 'BEGIN:VEVENT', + 'SUMMARY:Event Two', + 'DTSTART:20260602T110000Z', + 'DTEND:20260602T120000Z', + 'END:VEVENT', + 'BEGIN:VEVENT', + 'SUMMARY:Event Three', + 'DTSTART:20260603T130000Z', + 'DTEND:20260603T140000Z', + 'END:VEVENT', + 'END:VCALENDAR', + ].join('\r\n'); + + const form = new FormData(); + form.append('file', new File([ics], 'multi.ics', { type: 'text/calendar' })); + + const res = await formPost('/api/v1/import/ics', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(3); + expect(data.events[0].title).toBe('Event One'); + expect(data.events[2].title).toBe('Event Three'); + }); + + it('parses event with RRULE', async () => { + const ics = [ + 'BEGIN:VCALENDAR', + 'BEGIN:VEVENT', + 'SUMMARY:Daily Standup', + 'DTSTART:20260101T090000Z', + 'RRULE:FREQ=DAILY;COUNT=5', + 'END:VEVENT', + 'END:VCALENDAR', + ].join('\n'); + + const form = new FormData(); + form.append('file', new File([ics], 'recurring.ics')); + + const res = await formPost('/api/v1/import/ics', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.events[0].rrule).toBe('FREQ=DAILY;COUNT=5'); + }); + + it('handles ICS with DTSTART parameters', async () => { + const ics = [ + 'BEGIN:VCALENDAR', + 'BEGIN:VEVENT', + 'SUMMARY:All Day Event', + 'DTSTART;VALUE=DATE:20260701', + 'DTEND;VALUE=DATE:20260702', + 'END:VEVENT', + 'END:VCALENDAR', + ].join('\r\n'); + + const form = new FormData(); + form.append('file', new File([ics], 'allday.ics')); + + const res = await formPost('/api/v1/import/ics', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(1); + // DTSTART;VALUE=DATE:20260701 → split(':').pop() → '20260701' + expect(data.events[0].start).toBe('20260701'); + }); + + it('skips events without title and start', async () => { + const ics = [ + 'BEGIN:VCALENDAR', + 'BEGIN:VEVENT', + 'DESCRIPTION:No title or start', + 'END:VEVENT', + 'BEGIN:VEVENT', + 'SUMMARY:Valid Event', + 'DTSTART:20260101T090000Z', + 'END:VEVENT', + 'END:VCALENDAR', + ].join('\n'); + + const form = new FormData(); + form.append('file', new File([ics], 'partial.ics')); + + const res = await formPost('/api/v1/import/ics', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(1); + expect(data.events[0].title).toBe('Valid Event'); + }); + + it('returns empty array for ICS without events', async () => { + const ics = 'BEGIN:VCALENDAR\r\nEND:VCALENDAR'; + + const form = new FormData(); + form.append('file', new File([ics], 'empty.ics')); + + const res = await formPost('/api/v1/import/ics', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(0); + expect(data.events).toEqual([]); + }); + + it('returns 400 if no file provided', async () => { + const form = new FormData(); + const res = await formPost('/api/v1/import/ics', form); + expect(res.status).toBe(400); + + const data = await res.json(); + expect(data.error).toBe('No file'); + }); +}); diff --git a/apps/calendar/apps/server/src/index.ts b/apps/calendar/apps/server/src/index.ts index a57d55e26..5d01d34b7 100644 --- a/apps/calendar/apps/server/src/index.ts +++ b/apps/calendar/apps/server/src/index.ts @@ -134,4 +134,5 @@ function parseICS(text: string): Array> { return events; } +export { app }; export default { port: PORT, fetch: app.fetch }; diff --git a/apps/calendar/apps/server/src/test-setup.ts b/apps/calendar/apps/server/src/test-setup.ts new file mode 100644 index 000000000..5e29b3b30 --- /dev/null +++ b/apps/calendar/apps/server/src/test-setup.ts @@ -0,0 +1,2 @@ +process.env.DEV_BYPASS_AUTH = 'true'; +process.env.NODE_ENV = 'development'; diff --git a/apps/calendar/apps/server/tsconfig.json b/apps/calendar/apps/server/tsconfig.json index 9c7e5fa56..8b3656245 100644 --- a/apps/calendar/apps/server/tsconfig.json +++ b/apps/calendar/apps/server/tsconfig.json @@ -7,5 +7,5 @@ "esModuleInterop": true, "skipLibCheck": true }, - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "vitest.config.ts"] } diff --git a/apps/calendar/apps/server/vitest.config.ts b/apps/calendar/apps/server/vitest.config.ts new file mode 100644 index 000000000..17eeaa786 --- /dev/null +++ b/apps/calendar/apps/server/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts'], + setupFiles: ['./src/test-setup.ts'], + clearMocks: true, + mockReset: true, + restoreMocks: true, + }, +}); diff --git a/apps/contacts/apps/server/package.json b/apps/contacts/apps/server/package.json index a08a89747..c658400a1 100644 --- a/apps/contacts/apps/server/package.json +++ b/apps/contacts/apps/server/package.json @@ -5,7 +5,9 @@ "type": "module", "scripts": { "dev": "bun run --watch src/index.ts", - "start": "bun run src/index.ts" + "start": "bun run src/index.ts", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@manacore/shared-hono": "workspace:*", @@ -13,6 +15,7 @@ "hono": "^4.7.0" }, "devDependencies": { - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^3.0.0" } } diff --git a/apps/contacts/apps/server/src/index.test.ts b/apps/contacts/apps/server/src/index.test.ts new file mode 100644 index 000000000..4216a147b --- /dev/null +++ b/apps/contacts/apps/server/src/index.test.ts @@ -0,0 +1,242 @@ +import { describe, it, expect, vi } from 'vitest'; +import { app } from './index'; + +function formPost(path: string, formData: FormData) { + return app.request(path, { method: 'POST', body: formData }); +} + +// ─── Health ──────────────────────────────────────────────────── + +describe('GET /health', () => { + it('returns healthy status', async () => { + const res = await app.request('/health'); + expect(res.status).toBe(200); + }); +}); + +// ─── vCard Import ────────────────────────────────────────────── + +describe('POST /api/v1/import/vcard', () => { + it('parses a single vCard', async () => { + const vcard = [ + 'BEGIN:VCARD', + 'VERSION:3.0', + 'FN:Max Mustermann', + 'EMAIL:max@example.com', + 'TEL:+49 170 1234567', + 'ORG:ACME Corp', + 'TITLE:Engineer', + 'END:VCARD', + ].join('\r\n'); + + const form = new FormData(); + form.append('file', new File([vcard], 'contacts.vcf', { type: 'text/vcard' })); + + const res = await formPost('/api/v1/import/vcard', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(1); + expect(data.contacts[0].name).toBe('Max Mustermann'); + expect(data.contacts[0].email).toBe('max@example.com'); + expect(data.contacts[0].phone).toBe('+49 170 1234567'); + expect(data.contacts[0].company).toBe('ACME Corp'); + expect(data.contacts[0].title).toBe('Engineer'); + }); + + it('parses multiple vCards', async () => { + const vcard = [ + 'BEGIN:VCARD', + 'FN:Alice', + 'EMAIL:alice@example.com', + 'END:VCARD', + 'BEGIN:VCARD', + 'FN:Bob', + 'EMAIL:bob@example.com', + 'END:VCARD', + 'BEGIN:VCARD', + 'FN:Charlie', + 'TEL:+1 555 0123', + 'END:VCARD', + ].join('\n'); + + const form = new FormData(); + form.append('file', new File([vcard], 'multi.vcf')); + + const res = await formPost('/api/v1/import/vcard', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(3); + expect(data.contacts[0].name).toBe('Alice'); + expect(data.contacts[1].name).toBe('Bob'); + expect(data.contacts[2].name).toBe('Charlie'); + }); + + it('handles vCard with EMAIL type parameters', async () => { + const vcard = [ + 'BEGIN:VCARD', + 'FN:Test User', + 'EMAIL;type=INTERNET;type=WORK:work@example.com', + 'TEL;type=CELL:+49 170 0000000', + 'END:VCARD', + ].join('\r\n'); + + const form = new FormData(); + form.append('file', new File([vcard], 'typed.vcf')); + + const res = await formPost('/api/v1/import/vcard', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(1); + // EMAIL;type=...:work@example.com → split(':').pop() → 'work@example.com' + expect(data.contacts[0].email).toBe('work@example.com'); + expect(data.contacts[0].phone).toBe('+49 170 0000000'); + }); + + it('skips contacts without name or email', async () => { + const vcard = [ + 'BEGIN:VCARD', + 'TEL:+49 170 0000000', + 'ORG:Company Only', + 'END:VCARD', + 'BEGIN:VCARD', + 'FN:Valid Contact', + 'END:VCARD', + ].join('\n'); + + const form = new FormData(); + form.append('file', new File([vcard], 'partial.vcf')); + + const res = await formPost('/api/v1/import/vcard', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(1); + expect(data.contacts[0].name).toBe('Valid Contact'); + }); + + it('includes contact with only email (no name)', async () => { + const vcard = ['BEGIN:VCARD', 'EMAIL:noreply@example.com', 'END:VCARD'].join('\n'); + + const form = new FormData(); + form.append('file', new File([vcard], 'email-only.vcf')); + + const res = await formPost('/api/v1/import/vcard', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(1); + expect(data.contacts[0].email).toBe('noreply@example.com'); + }); + + it('returns empty array for empty vCard file', async () => { + const form = new FormData(); + form.append('file', new File([''], 'empty.vcf')); + + const res = await formPost('/api/v1/import/vcard', form); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.count).toBe(0); + expect(data.contacts).toEqual([]); + }); + + it('returns 400 if no file provided', async () => { + const form = new FormData(); + const res = await formPost('/api/v1/import/vcard', form); + expect(res.status).toBe(400); + + const data = await res.json(); + expect(data.error).toBe('No file'); + }); +}); + +// ─── Avatar Upload ───────────────────────────────────────────── + +describe('POST /api/v1/contacts/:id/avatar', () => { + it('returns 400 if no file provided', async () => { + const form = new FormData(); + const res = await formPost('/api/v1/contacts/contact-1/avatar', form); + expect(res.status).toBe(400); + + const data = await res.json(); + expect(data.error).toBe('No file'); + }); + + it('returns 400 if file exceeds 5MB', async () => { + // Create a file > 5MB + const bigContent = new Uint8Array(6 * 1024 * 1024); + const form = new FormData(); + form.append('file', new File([bigContent], 'big.jpg', { type: 'image/jpeg' })); + + const res = await formPost('/api/v1/contacts/contact-1/avatar', form); + expect(res.status).toBe(400); + + const data = await res.json(); + expect(data.error).toBe('Max 5MB'); + }); + + it('returns 400 for invalid file type', async () => { + const form = new FormData(); + form.append('file', new File(['data'], 'doc.pdf', { type: 'application/pdf' })); + + const res = await formPost('/api/v1/contacts/contact-1/avatar', form); + expect(res.status).toBe(400); + + const data = await res.json(); + expect(data.error).toContain('Invalid file type'); + }); + + it('accepts JPEG files', async () => { + vi.mock('@manacore/shared-storage', () => ({ + createContactsStorage: () => ({ + upload: vi.fn().mockResolvedValue({ url: 'https://s3.example.com/avatar.jpg' }), + }), + generateUserFileKey: vi.fn().mockReturnValue('users/test/avatar.jpg'), + getContentType: vi.fn().mockReturnValue('image/jpeg'), + })); + + const form = new FormData(); + form.append('file', new File(['image-data'], 'photo.jpg', { type: 'image/jpeg' })); + + const res = await formPost('/api/v1/contacts/contact-1/avatar', form); + expect(res.status).toBe(201); + + const data = await res.json(); + expect(data.avatarUrl).toBeDefined(); + }); + + it('accepts PNG files', async () => { + vi.mock('@manacore/shared-storage', () => ({ + createContactsStorage: () => ({ + upload: vi.fn().mockResolvedValue({ url: 'https://s3.example.com/avatar.png' }), + }), + generateUserFileKey: vi.fn().mockReturnValue('users/test/avatar.png'), + getContentType: vi.fn().mockReturnValue('image/png'), + })); + + const form = new FormData(); + form.append('file', new File(['image-data'], 'photo.png', { type: 'image/png' })); + + const res = await formPost('/api/v1/contacts/contact-1/avatar', form); + expect(res.status).toBe(201); + }); + + it('accepts WebP files', async () => { + vi.mock('@manacore/shared-storage', () => ({ + createContactsStorage: () => ({ + upload: vi.fn().mockResolvedValue({ url: 'https://s3.example.com/avatar.webp' }), + }), + generateUserFileKey: vi.fn().mockReturnValue('users/test/avatar.webp'), + getContentType: vi.fn().mockReturnValue('image/webp'), + })); + + const form = new FormData(); + form.append('file', new File(['image-data'], 'photo.webp', { type: 'image/webp' })); + + const res = await formPost('/api/v1/contacts/contact-1/avatar', form); + expect(res.status).toBe(201); + }); +}); diff --git a/apps/contacts/apps/server/src/index.ts b/apps/contacts/apps/server/src/index.ts index 8ce038e3a..e29b2989f 100644 --- a/apps/contacts/apps/server/src/index.ts +++ b/apps/contacts/apps/server/src/index.ts @@ -104,4 +104,5 @@ function parseVCard(text: string): Array> { return contacts; } +export { app }; export default { port: PORT, fetch: app.fetch }; diff --git a/apps/contacts/apps/server/src/test-setup.ts b/apps/contacts/apps/server/src/test-setup.ts new file mode 100644 index 000000000..5e29b3b30 --- /dev/null +++ b/apps/contacts/apps/server/src/test-setup.ts @@ -0,0 +1,2 @@ +process.env.DEV_BYPASS_AUTH = 'true'; +process.env.NODE_ENV = 'development'; diff --git a/apps/contacts/apps/server/tsconfig.json b/apps/contacts/apps/server/tsconfig.json index 9c7e5fa56..8b3656245 100644 --- a/apps/contacts/apps/server/tsconfig.json +++ b/apps/contacts/apps/server/tsconfig.json @@ -7,5 +7,5 @@ "esModuleInterop": true, "skipLibCheck": true }, - "include": ["src/**/*.ts"] + "include": ["src/**/*.ts", "vitest.config.ts"] } diff --git a/apps/contacts/apps/server/vitest.config.ts b/apps/contacts/apps/server/vitest.config.ts new file mode 100644 index 000000000..17eeaa786 --- /dev/null +++ b/apps/contacts/apps/server/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts'], + setupFiles: ['./src/test-setup.ts'], + clearMocks: true, + mockReset: true, + restoreMocks: true, + }, +}); diff --git a/apps/todo/apps/server/package.json b/apps/todo/apps/server/package.json index d4df885cf..6198b2c04 100644 --- a/apps/todo/apps/server/package.json +++ b/apps/todo/apps/server/package.json @@ -7,7 +7,9 @@ "scripts": { "dev": "bun run --watch src/index.ts", "start": "bun run src/index.ts", - "type-check": "bun x tsc --noEmit" + "type-check": "bun x tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@manacore/shared-hono": "workspace:*", @@ -19,6 +21,7 @@ }, "devDependencies": { "@types/bun": "^1.2.0", - "typescript": "^5.9.3" + "typescript": "^5.9.3", + "vitest": "^3.0.0" } } diff --git a/apps/todo/apps/server/src/routes/admin.test.ts b/apps/todo/apps/server/src/routes/admin.test.ts new file mode 100644 index 000000000..63ee88ac5 --- /dev/null +++ b/apps/todo/apps/server/src/routes/admin.test.ts @@ -0,0 +1,111 @@ +import { describe, it, expect, vi } from 'vitest'; +import { Hono } from 'hono'; + +// Mock drizzle-orm operators +vi.mock('drizzle-orm', () => ({ + eq: vi.fn((_col, _val) => ({ type: 'eq' })), + sql: vi.fn((strings: TemplateStringsArray) => strings.join('')), +})); + +const mockSelectFromWhere = vi.fn(); +const mockDeleteWhere = vi.fn(); + +vi.mock('../db', () => ({ + db: { + select: vi.fn(() => ({ + from: vi.fn(() => ({ + where: () => mockSelectFromWhere(), + })), + })), + delete: vi.fn(() => ({ + where: () => mockDeleteWhere(), + })), + }, + tasks: { userId: 'user_id' }, + projects: { userId: 'user_id' }, + reminders: { userId: 'user_id' }, +})); + +// Mock serviceAuthMiddleware to pass through +vi.mock('@manacore/shared-hono', () => ({ + serviceAuthMiddleware: () => async (_c: unknown, next: () => Promise) => next(), +})); + +const { adminRoutes } = await import('./admin'); + +const app = new Hono(); +app.route('/admin', adminRoutes); + +function get(path: string) { + return app.request(path); +} + +function del(path: string) { + return app.request(path, { method: 'DELETE' }); +} + +// ─── GET /admin/user-data/:userId ────────────────────────────── + +describe('GET /admin/user-data/:userId', () => { + it('returns user data counts', async () => { + mockSelectFromWhere + .mockResolvedValueOnce([{ count: 42 }]) // tasks + .mockResolvedValueOnce([{ count: 3 }]) // projects + .mockResolvedValueOnce([{ count: 5 }]); // reminders + + const res = await get('/admin/user-data/user-123'); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.userId).toBe('user-123'); + expect(data.counts.tasks).toBe(42); + expect(data.counts.projects).toBe(3); + expect(data.counts.reminders).toBe(5); + }); + + it('returns zero counts for user with no data', async () => { + mockSelectFromWhere + .mockResolvedValueOnce([{ count: 0 }]) + .mockResolvedValueOnce([{ count: 0 }]) + .mockResolvedValueOnce([{ count: 0 }]); + + const res = await get('/admin/user-data/empty-user'); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.counts.tasks).toBe(0); + expect(data.counts.projects).toBe(0); + expect(data.counts.reminders).toBe(0); + }); + + it('handles null count results', async () => { + mockSelectFromWhere + .mockResolvedValueOnce([undefined]) + .mockResolvedValueOnce([undefined]) + .mockResolvedValueOnce([undefined]); + + const res = await get('/admin/user-data/user-x'); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.counts.tasks).toBe(0); + expect(data.counts.projects).toBe(0); + expect(data.counts.reminders).toBe(0); + }); +}); + +// ─── DELETE /admin/user-data/:userId ─────────────────────────── + +describe('DELETE /admin/user-data/:userId', () => { + it('deletes all user data (GDPR)', async () => { + mockDeleteWhere.mockResolvedValue(undefined); + + const res = await del('/admin/user-data/user-123'); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.userId).toBe('user-123'); + expect(data.deleted).toBe(true); + expect(data.message).toBe('All user data deleted'); + }); +}); diff --git a/apps/todo/apps/server/src/routes/reminders.test.ts b/apps/todo/apps/server/src/routes/reminders.test.ts new file mode 100644 index 000000000..e24a40da8 --- /dev/null +++ b/apps/todo/apps/server/src/routes/reminders.test.ts @@ -0,0 +1,205 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { Hono } from 'hono'; + +// Mock drizzle-orm operators before any imports that use them +vi.mock('drizzle-orm', () => ({ + eq: vi.fn((_col, _val) => ({ type: 'eq' })), + and: vi.fn((..._args) => ({ type: 'and' })), + asc: vi.fn((_col) => ({ type: 'asc' })), +})); + +const mockFindFirstTask = vi.fn(); +const mockFindManyReminders = vi.fn(); +const mockInsertReturning = vi.fn(); +const mockDeleteWhere = vi.fn(); + +vi.mock('../db', () => ({ + db: { + query: { + tasks: { findFirst: (...args: unknown[]) => mockFindFirstTask(...args) }, + reminders: { findMany: (...args: unknown[]) => mockFindManyReminders(...args) }, + }, + insert: vi.fn(() => ({ + values: vi.fn(() => ({ + returning: () => mockInsertReturning(), + })), + })), + delete: vi.fn(() => ({ + where: () => mockDeleteWhere(), + })), + }, + tasks: { id: 'id', userId: 'user_id' }, + reminders: { + id: 'id', + taskId: 'task_id', + userId: 'user_id', + minutesBefore: 'minutes_before', + }, +})); + +// Import AFTER mocks +const { reminderRoutes } = await import('./reminders'); + +const TEST_USER_ID = 'test-user-id'; + +function createApp() { + const app = new Hono(); + app.use('*', async (c, next) => { + c.set('userId', TEST_USER_ID); + return next(); + }); + app.route('/', reminderRoutes); + return app; +} + +const app = createApp(); + +function get(path: string) { + return app.request(path); +} + +function post(path: string, body: unknown) { + return app.request(path, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); +} + +function del(path: string) { + return app.request(path, { method: 'DELETE' }); +} + +beforeEach(() => { + vi.clearAllMocks(); +}); + +// ─── GET /tasks/:taskId/reminders ────────────────────────────── + +describe('GET /tasks/:taskId/reminders', () => { + it('returns reminders for a valid task', async () => { + mockFindFirstTask.mockResolvedValue({ id: 'task-1', userId: TEST_USER_ID }); + mockFindManyReminders.mockResolvedValue([ + { id: 'r-1', minutesBefore: 10, type: 'push' }, + { id: 'r-2', minutesBefore: 60, type: 'email' }, + ]); + + const res = await get('/tasks/task-1/reminders'); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.reminders).toHaveLength(2); + expect(data.reminders[0].id).toBe('r-1'); + }); + + it('returns 404 if task not found', async () => { + mockFindFirstTask.mockResolvedValue(null); + + const res = await get('/tasks/nonexistent/reminders'); + expect(res.status).toBe(404); + + const data = await res.json(); + expect(data.error).toBe('Task not found'); + }); +}); + +// ─── POST /tasks/:taskId/reminders ───────────────────────────── + +describe('POST /tasks/:taskId/reminders', () => { + it('creates a reminder for a task with due date', async () => { + const dueDate = new Date('2026-06-15T14:00:00Z'); + mockFindFirstTask.mockResolvedValue({ + id: 'task-1', + userId: TEST_USER_ID, + dueDate: dueDate.toISOString(), + }); + mockInsertReturning.mockResolvedValue([ + { + id: 'r-new', + taskId: 'task-1', + minutesBefore: 30, + type: 'push', + reminderTime: new Date(dueDate.getTime() - 30 * 60 * 1000).toISOString(), + }, + ]); + + const res = await post('/tasks/task-1/reminders', { + minutesBefore: 30, + type: 'push', + }); + expect(res.status).toBe(201); + + const data = await res.json(); + expect(data.reminder.id).toBe('r-new'); + expect(data.reminder.minutesBefore).toBe(30); + }); + + it('defaults type to push', async () => { + mockFindFirstTask.mockResolvedValue({ + id: 'task-1', + userId: TEST_USER_ID, + dueDate: '2026-06-15T14:00:00Z', + }); + mockInsertReturning.mockResolvedValue([{ id: 'r-new', type: 'push' }]); + + const res = await post('/tasks/task-1/reminders', { minutesBefore: 15 }); + expect(res.status).toBe(201); + }); + + it('returns 404 if task not found', async () => { + mockFindFirstTask.mockResolvedValue(null); + + const res = await post('/tasks/nonexistent/reminders', { + minutesBefore: 30, + }); + expect(res.status).toBe(404); + }); + + it('returns 400 if task has no due date', async () => { + mockFindFirstTask.mockResolvedValue({ + id: 'task-1', + userId: TEST_USER_ID, + dueDate: null, + }); + + const res = await post('/tasks/task-1/reminders', { minutesBefore: 30 }); + expect(res.status).toBe(400); + + const data = await res.json(); + expect(data.error).toContain('without due date'); + }); +}); + +// ─── DELETE /reminders/:id ───────────────────────────────────── + +describe('DELETE /reminders/:id', () => { + it('deletes an existing reminder', async () => { + const mockFindFirstReminder = vi.fn().mockResolvedValue({ + id: 'r-1', + userId: TEST_USER_ID, + }); + // Override the reminders findFirst for this test + const { db } = await import('../db'); + (db.query as Record).reminders = { findFirst: mockFindFirstReminder }; + mockDeleteWhere.mockResolvedValue(undefined); + + const res = await del('/reminders/r-1'); + expect(res.status).toBe(200); + + const data = await res.json(); + expect(data.success).toBe(true); + }); + + it('returns 404 if reminder not found', async () => { + const { db } = await import('../db'); + (db.query as Record).reminders = { + findFirst: vi.fn().mockResolvedValue(null), + }; + + const res = await del('/reminders/nonexistent'); + expect(res.status).toBe(404); + + const data = await res.json(); + expect(data.error).toBe('Reminder not found'); + }); +}); diff --git a/apps/todo/apps/server/src/routes/rrule.test.ts b/apps/todo/apps/server/src/routes/rrule.test.ts new file mode 100644 index 000000000..76baffb31 --- /dev/null +++ b/apps/todo/apps/server/src/routes/rrule.test.ts @@ -0,0 +1,164 @@ +import { describe, it, expect } from 'vitest'; +import { Hono } from 'hono'; +import { rruleRoutes } from './rrule'; + +const app = new Hono(); +app.route('/compute', rruleRoutes); + +function post(path: string, body: unknown) { + return app.request(path, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); +} + +// ─── POST /compute/next-occurrence ───────────────────────────── + +describe('POST /compute/next-occurrence', () => { + it('returns next occurrence for daily RRULE', async () => { + const res = await post('/compute/next-occurrence', { + rrule: 'FREQ=DAILY', + }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(true); + expect(data.nextDate).toBeDefined(); + expect(data.totalOccurrences).toBeGreaterThan(0); + }); + + it('returns next occurrence for weekly RRULE', async () => { + const res = await post('/compute/next-occurrence', { + rrule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR', + }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(true); + expect(data.nextDate).toBeDefined(); + }); + + it('returns next occurrence for monthly RRULE', async () => { + const res = await post('/compute/next-occurrence', { + rrule: 'FREQ=MONTHLY;BYMONTHDAY=15', + }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(true); + }); + + it('respects recurrenceEndDate', async () => { + const pastEnd = new Date('2020-01-01').toISOString(); + const res = await post('/compute/next-occurrence', { + rrule: 'FREQ=DAILY', + recurrenceEndDate: pastEnd, + }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.nextDate).toBeNull(); + expect(data.message).toContain('No more occurrences'); + }); + + it('respects after parameter', async () => { + const afterDate = new Date('2027-06-01T00:00:00Z').toISOString(); + const res = await post('/compute/next-occurrence', { + rrule: 'FREQ=DAILY', + after: afterDate, + }); + expect(res.status).toBe(200); + const data = await res.json(); + const next = new Date(data.nextDate); + expect(next.getTime()).toBeGreaterThan(new Date(afterDate).getTime()); + }); + + it('rejects empty rrule', async () => { + const res = await post('/compute/next-occurrence', { rrule: '' }); + expect(res.status).toBe(400); + }); + + it('rejects missing rrule', async () => { + const res = await post('/compute/next-occurrence', {}); + expect(res.status).toBe(400); + }); + + it('rejects RRULE exceeding max length', async () => { + const res = await post('/compute/next-occurrence', { + rrule: 'FREQ=DAILY;' + 'X'.repeat(500), + }); + expect(res.status).toBe(400); + }); + + it('rejects invalid RRULE string', async () => { + const res = await post('/compute/next-occurrence', { + rrule: 'not a valid rrule', + }); + expect(res.status).toBe(400); + const data = await res.json(); + expect(data.error).toContain('Invalid RRULE'); + }); + + it('rejects RRULE with too many occurrences (DoS protection)', async () => { + // FREQ=SECONDLY would generate millions of occurrences + const res = await post('/compute/next-occurrence', { + rrule: 'FREQ=SECONDLY', + }); + expect(res.status).toBe(400); + const data = await res.json(); + expect(data.error).toContain('too many occurrences'); + }); +}); + +// ─── POST /compute/validate ──────────────────────────────────── + +describe('POST /compute/validate', () => { + it('validates a correct daily RRULE', async () => { + const res = await post('/compute/validate', { rrule: 'FREQ=DAILY' }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(true); + expect(data.occurrences).toBeGreaterThan(0); + }); + + it('validates a weekly RRULE with BYDAY', async () => { + const res = await post('/compute/validate', { + rrule: 'FREQ=WEEKLY;BYDAY=TU,TH', + }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(true); + }); + + it('validates a yearly RRULE', async () => { + const res = await post('/compute/validate', { + rrule: 'FREQ=YEARLY;BYMONTH=12;BYMONTHDAY=25', + }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(true); + expect(data.occurrences).toBeLessThanOrEqual(10); // max 10 years + }); + + it('returns valid=false for invalid RRULE', async () => { + const res = await post('/compute/validate', { rrule: 'garbage' }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(false); + expect(data.error).toBeDefined(); + }); + + it('rejects empty rrule', async () => { + const res = await post('/compute/validate', { rrule: '' }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(false); + }); + + it('flags RRULE with too many occurrences', async () => { + const res = await post('/compute/validate', { + rrule: 'FREQ=SECONDLY', + }); + expect(res.status).toBe(200); + const data = await res.json(); + expect(data.valid).toBe(false); + expect(data.error).toContain('Too many occurrences'); + }); +}); diff --git a/apps/todo/apps/server/tsconfig.json b/apps/todo/apps/server/tsconfig.json index 4f2959bb9..89f9eb60f 100644 --- a/apps/todo/apps/server/tsconfig.json +++ b/apps/todo/apps/server/tsconfig.json @@ -11,6 +11,6 @@ "forceConsistentCasingInFileNames": true, "noEmit": true }, - "include": ["src/**/*"], + "include": ["src/**/*", "vitest.config.ts"], "exclude": ["node_modules"] } diff --git a/apps/todo/apps/server/vitest.config.ts b/apps/todo/apps/server/vitest.config.ts new file mode 100644 index 000000000..0a44f195b --- /dev/null +++ b/apps/todo/apps/server/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts'], + clearMocks: true, + mockReset: true, + restoreMocks: true, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4a7c0e0c..f572a53c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -250,14 +250,14 @@ importers: version: link:../../../../packages/shared-landing-ui astro: specifier: ^5.16.0 - version: 5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + version: 5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) typescript: specifier: ^5.9.2 version: 5.9.3 devDependencies: '@astrojs/tailwind': specifier: ^6.0.2 - version: 6.0.2(astro@5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.3))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) + version: 6.0.2(astro@5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.3))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) '@tailwindcss/typography': specifier: ^0.5.18 version: 0.5.19(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.3)) @@ -266,13 +266,13 @@ importers: version: 20.19.25 eslint: specifier: ^9.0.0 - version: 9.39.1(jiti@2.6.1) + version: 9.39.1(jiti@1.21.7) eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.2(eslint@9.39.1(jiti@2.6.1)) + version: 9.1.2(eslint@9.39.1(jiti@1.21.7)) eslint-plugin-astro: specifier: ^1.0.0 - version: 1.5.0(eslint@9.39.1(jiti@2.6.1)) + version: 1.5.0(eslint@9.39.1(jiti@1.21.7)) prettier: specifier: ^3.6.2 version: 3.6.2 @@ -301,6 +301,9 @@ importers: typescript: specifier: ^5.9.3 version: 5.9.3 + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@29.0.1(@noble/hashes@2.0.1))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3) apps/calendar/apps/web: dependencies: @@ -566,7 +569,7 @@ importers: version: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-router: specifier: ~55.0.5 - version: 55.0.5(fos732rmf7mlsrtwb6dv2k6f2i) + version: 55.0.5(6d2zdlrrz5o6rsdio5yhgp3juy) expo-secure-store: specifier: ~55.0.8 version: 55.0.8(expo@55.0.5) @@ -857,7 +860,7 @@ importers: version: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) expo-router: specifier: ~55.0.5 - version: 55.0.5(shihlejigi2nkza7wltmngtxfm) + version: 55.0.5(bhhyukj6njqvlfm2o3z6hpwcja) expo-status-bar: specifier: ~55.0.4 version: 55.0.4(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) @@ -909,19 +912,19 @@ importers: version: 19.1.17 '@typescript-eslint/eslint-plugin': specifier: ^7.7.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/parser': specifier: ^7.7.0 - version: 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + version: 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) dotenv: specifier: ^16.4.7 version: 16.6.1 eslint: specifier: ^9.39.1 - version: 9.39.1(jiti@1.21.7) + version: 9.39.1(jiti@2.6.1) eslint-config-universe: specifier: ^12.0.1 - version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3) + version: 12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3) prettier: specifier: ^3.2.5 version: 3.6.2 @@ -1474,6 +1477,9 @@ importers: typescript: specifier: ^5.9.3 version: 5.9.3 + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@29.0.1(@noble/hashes@2.0.1))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3) apps/contacts/apps/web: dependencies: @@ -5656,6 +5662,9 @@ importers: typescript: specifier: ^5.9.3 version: 5.9.3 + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.1)(@vitest/browser@3.2.4)(@vitest/ui@3.2.4)(jiti@2.6.1)(jsdom@29.0.1(@noble/hashes@2.0.1))(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3) apps/todo/apps/web: dependencies: @@ -26083,6 +26092,16 @@ snapshots: transitivePeerDependencies: - ts-node + '@astrojs/tailwind@6.0.2(astro@5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.3))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': + dependencies: + astro: 5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) + autoprefixer: 10.4.22(postcss@8.5.8) + postcss: 8.5.8 + postcss-load-config: 4.0.2(postcss@8.5.8)(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) + tailwindcss: 3.4.18(tsx@4.21.0)(yaml@2.8.3) + transitivePeerDependencies: + - ts-node + '@astrojs/tailwind@6.0.2(astro@5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3))(tailwindcss@3.4.18(tsx@4.21.0)(yaml@2.8.3))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3))': dependencies: astro: 5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3) @@ -28718,6 +28737,82 @@ snapshots: - supports-color - utf-8-validate + '@expo/cli@55.0.15(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(expo-router@55.0.5)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': + dependencies: + '@expo/code-signing-certificates': 0.0.6 + '@expo/config': 55.0.11(typescript@5.9.3) + '@expo/config-plugins': 55.0.7 + '@expo/devcert': 1.2.1 + '@expo/env': 2.1.1 + '@expo/image-utils': 0.8.12 + '@expo/json-file': 10.0.12 + '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@expo/metro': 54.2.0 + '@expo/metro-config': 55.0.9(expo@55.0.5)(typescript@5.9.3) + '@expo/osascript': 2.4.2 + '@expo/package-manager': 1.10.3 + '@expo/plist': 0.5.2 + '@expo/prebuild-config': 55.0.11(expo@55.0.5)(typescript@5.9.3) + '@expo/require-utils': 55.0.3(typescript@5.9.3) + '@expo/router-server': 55.0.9(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(expo-router@55.0.5)(expo-server@55.0.6)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@expo/schema-utils': 55.0.2 + '@expo/spawn-async': 1.7.2 + '@expo/ws-tunnel': 1.0.6 + '@expo/xcpretty': 4.4.1 + '@react-native/dev-middleware': 0.83.2 + accepts: 1.3.8 + arg: 5.0.2 + better-opn: 3.0.2 + bplist-creator: 0.1.0 + bplist-parser: 0.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + compression: 1.8.1 + connect: 3.7.0 + debug: 4.4.3 + dnssd-advertise: 1.1.3 + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-server: 55.0.6 + fetch-nodeshim: 0.4.9 + getenv: 2.0.0 + glob: 13.0.0 + lan-network: 0.2.0 + multitars: 0.2.4 + node-forge: 1.3.3 + npm-package-arg: 11.0.3 + ora: 3.4.0 + picomatch: 4.0.3 + pretty-format: 29.7.0 + progress: 2.0.3 + prompts: 2.4.2 + resolve-from: 5.0.0 + semver: 7.7.3 + send: 0.19.1 + slugify: 1.6.6 + source-map-support: 0.5.21 + stacktrace-parser: 0.1.11 + structured-headers: 0.4.1 + terminal-link: 2.1.1 + toqr: 0.1.1 + wrap-ansi: 7.0.0 + ws: 8.18.3 + zod: 3.25.76 + optionalDependencies: + expo-router: 55.0.5(6d2zdlrrz5o6rsdio5yhgp3juy) + react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) + transitivePeerDependencies: + - '@expo/dom-webview' + - '@expo/metro-runtime' + - bufferutil + - expo-constants + - expo-font + - react + - react-dom + - react-server-dom-webpack + - supports-color + - typescript + - utf-8-validate + '@expo/cli@55.0.15(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4)(expo-router@55.0.5)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3)': dependencies: '@expo/code-signing-certificates': 0.0.6 @@ -28779,7 +28874,7 @@ snapshots: ws: 8.18.3 zod: 3.25.76 optionalDependencies: - expo-router: 55.0.5(shihlejigi2nkza7wltmngtxfm) + expo-router: 55.0.5(bhhyukj6njqvlfm2o3z6hpwcja) react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) transitivePeerDependencies: - '@expo/dom-webview' @@ -28870,82 +28965,6 @@ snapshots: - typescript - utf-8-validate - '@expo/cli@55.0.15(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4)(expo-router@55.0.5)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': - dependencies: - '@expo/code-signing-certificates': 0.0.6 - '@expo/config': 55.0.11(typescript@5.9.3) - '@expo/config-plugins': 55.0.7 - '@expo/devcert': 1.2.1 - '@expo/env': 2.1.1 - '@expo/image-utils': 0.8.12 - '@expo/json-file': 10.0.12 - '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@expo/metro': 54.2.0 - '@expo/metro-config': 55.0.9(expo@55.0.5)(typescript@5.9.3) - '@expo/osascript': 2.4.2 - '@expo/package-manager': 1.10.3 - '@expo/plist': 0.5.2 - '@expo/prebuild-config': 55.0.11(expo@55.0.5)(typescript@5.9.3) - '@expo/require-utils': 55.0.3(typescript@5.9.3) - '@expo/router-server': 55.0.9(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4)(expo-router@55.0.5)(expo-server@55.0.6)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@expo/schema-utils': 55.0.2 - '@expo/spawn-async': 1.7.2 - '@expo/ws-tunnel': 1.0.6 - '@expo/xcpretty': 4.4.1 - '@react-native/dev-middleware': 0.83.2 - accepts: 1.3.8 - arg: 5.0.2 - better-opn: 3.0.2 - bplist-creator: 0.1.0 - bplist-parser: 0.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - compression: 1.8.1 - connect: 3.7.0 - debug: 4.4.3 - dnssd-advertise: 1.1.3 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) - expo-server: 55.0.6 - fetch-nodeshim: 0.4.9 - getenv: 2.0.0 - glob: 13.0.0 - lan-network: 0.2.0 - multitars: 0.2.4 - node-forge: 1.3.3 - npm-package-arg: 11.0.3 - ora: 3.4.0 - picomatch: 4.0.3 - pretty-format: 29.7.0 - progress: 2.0.3 - prompts: 2.4.2 - resolve-from: 5.0.0 - semver: 7.7.3 - send: 0.19.1 - slugify: 1.6.6 - source-map-support: 0.5.21 - stacktrace-parser: 0.1.11 - structured-headers: 0.4.1 - terminal-link: 2.1.1 - toqr: 0.1.1 - wrap-ansi: 7.0.0 - ws: 8.18.3 - zod: 3.25.76 - optionalDependencies: - expo-router: 55.0.5(cdndhew7mqhhoslu6uoygxcgvm) - react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) - transitivePeerDependencies: - - '@expo/dom-webview' - - '@expo/metro-runtime' - - bufferutil - - expo-constants - - expo-font - - react - - react-dom - - react-server-dom-webpack - - supports-color - - typescript - - utf-8-validate - '@expo/cli@55.0.15(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4)(expo-router@55.0.5)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3)': dependencies: '@expo/code-signing-certificates': 0.0.6 @@ -29327,7 +29346,7 @@ snapshots: '@expo/dom-webview@55.0.3(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)': dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: 19.2.0 react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) @@ -29481,7 +29500,7 @@ snapshots: dependencies: '@expo/dom-webview': 55.0.3(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) anser: 1.4.10 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: 19.2.0 react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) stacktrace-parser: 0.1.11 @@ -29529,7 +29548,7 @@ snapshots: dependencies: '@expo/dom-webview': 55.0.3(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) anser: 1.4.10 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: 19.2.0 react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) stacktrace-parser: 0.1.11 @@ -29696,7 +29715,7 @@ snapshots: postcss: 8.4.49 resolve-from: 5.0.0 optionalDependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) transitivePeerDependencies: - bufferutil - supports-color @@ -29743,7 +29762,7 @@ snapshots: dependencies: '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) anser: 1.4.10 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) pretty-format: 29.7.0 react: 19.2.0 react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) @@ -29915,7 +29934,7 @@ snapshots: '@expo/json-file': 10.0.12 '@react-native/normalize-colors': 0.83.4 debug: 4.4.3 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) resolve-from: 5.0.0 semver: 7.7.3 xml2js: 0.6.0 @@ -29976,6 +29995,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@expo/router-server@55.0.9(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(expo-router@55.0.5)(expo-server@55.0.6)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': + dependencies: + debug: 4.4.3 + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-constants: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(typescript@5.9.3) + expo-font: 55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + expo-server: 55.0.6 + react: 19.2.0 + optionalDependencies: + '@expo/metro-runtime': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + expo-router: 55.0.5(6d2zdlrrz5o6rsdio5yhgp3juy) + react-dom: 19.2.0(react@19.2.0) + transitivePeerDependencies: + - supports-color + '@expo/router-server@55.0.9(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4)(expo-router@55.0.5)(expo-server@55.0.6)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: debug: 4.4.3 @@ -29986,7 +30020,7 @@ snapshots: react: 19.2.0 optionalDependencies: '@expo/metro-runtime': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - expo-router: 55.0.5(shihlejigi2nkza7wltmngtxfm) + expo-router: 55.0.5(bhhyukj6njqvlfm2o3z6hpwcja) react-dom: 19.2.0(react@19.2.0) transitivePeerDependencies: - supports-color @@ -30524,7 +30558,7 @@ snapshots: - ts-node optional: true - '@jest/core@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3))': + '@jest/core@30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3))': dependencies: '@jest/console': 30.3.0 '@jest/pattern': 30.0.1 @@ -30539,7 +30573,7 @@ snapshots: exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.3.0 - jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)) + jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-haste-map: 30.3.0 jest-message-util: 30.3.0 jest-regex-util: 30.0.1 @@ -35649,7 +35683,7 @@ snapshots: jest: 30.3.0(@types/node@20.19.25)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@20.19.25)(typescript@5.9.3)) optional: true - '@testing-library/react-native@13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0)': + '@testing-library/react-native@13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0)': dependencies: jest-matcher-utils: 30.3.0 picocolors: 1.1.1 @@ -35659,7 +35693,7 @@ snapshots: react-test-renderer: 19.1.0(react@19.2.0) redent: 3.0.0 optionalDependencies: - jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)) + jest: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(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.3.0(@types/node@24.10.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.3.3)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0)': @@ -36297,16 +36331,16 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/type-utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -36355,15 +36389,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/type-utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 @@ -36455,14 +36489,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.21.0 debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -36494,14 +36528,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/parser@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) optionalDependencies: typescript: 5.3.3 transitivePeerDependencies: @@ -36627,12 +36661,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/type-utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -36663,12 +36697,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/type-utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) debug: 4.4.3 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) ts-api-utils: 1.4.3(typescript@5.3.3) optionalDependencies: typescript: 5.3.3 @@ -36850,15 +36884,15 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/utils@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) '@types/json-schema': 7.0.15 '@types/semver': 7.7.1 '@typescript-eslint/scope-manager': 6.21.0 '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -36889,13 +36923,13 @@ snapshots: - supports-color - typescript - '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3)': + '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) transitivePeerDependencies: - supports-color - typescript @@ -38216,6 +38250,108 @@ snapshots: - terser - typescript + astro@5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@1.21.7)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3): + dependencies: + '@astrojs/compiler': 2.13.0 + '@astrojs/internal-helpers': 0.7.5 + '@astrojs/markdown-remark': 6.3.9 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 3.0.1 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.3.1 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.1.0 + cssesc: 3.0.0 + debug: 4.4.3 + deterministic-object-hash: 2.0.2 + devalue: 5.5.0 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.12 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.3.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.1 + magic-string: 0.30.21 + magicast: 0.5.1 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.1 + package-manager-detector: 1.5.0 + piccolore: 0.1.3 + picomatch: 4.0.3 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.3 + shiki: 3.15.0 + smol-toml: 1.5.2 + svgo: 4.0.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tsconfck: 3.1.6(typescript@5.9.3) + ultrahtml: 1.6.0 + unifont: 0.6.0 + unist-util-visit: 5.0.0 + unstorage: 1.17.3(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(ioredis@5.9.2) + vfile: 6.0.3 + vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3) + vitefu: 1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + sharp: 0.34.5 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + astro@5.16.0(@azure/storage-blob@12.31.0)(@netlify/blobs@10.7.4)(@types/node@20.19.25)(ioredis@5.9.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.53.3)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.3): dependencies: '@astrojs/compiler': 2.13.0 @@ -39015,7 +39151,7 @@ snapshots: resolve-from: 5.0.0 optionalDependencies: '@babel/runtime': 7.29.2 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) transitivePeerDependencies: - '@babel/core' - supports-color @@ -41078,6 +41214,11 @@ snapshots: eslint: 9.39.1(jiti@2.6.1) semver: 7.7.3 + eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + semver: 7.7.3 + eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -41142,14 +41283,14 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) + eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@1.21.7)): + dependencies: + eslint: 9.39.1(jiti@1.21.7) + eslint-config-prettier@9.1.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -41174,17 +41315,17 @@ snapshots: - supports-color - typescript - eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2)(typescript@5.3.3): + eslint-config-universe@12.1.0(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)(typescript@5.3.3): dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) - eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2) - eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@1.21.7)) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + eslint: 9.39.1(jiti@2.6.1) + eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-node: 11.1.0(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-prettier: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2) + eslint-plugin-react: 7.37.5(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-react-hooks: 4.6.2(eslint@9.39.1(jiti@2.6.1)) optionalDependencies: prettier: 3.6.2 transitivePeerDependencies: @@ -41291,12 +41432,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) - eslint: 9.39.1(jiti@1.21.7) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: - supports-color @@ -41333,6 +41474,20 @@ snapshots: transitivePeerDependencies: - supports-color + eslint-plugin-astro@1.5.0(eslint@9.39.1(jiti@1.21.7)): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@1.21.7)) + '@jridgewell/sourcemap-codec': 1.5.5 + '@typescript-eslint/types': 8.48.1 + astro-eslint-parser: 1.2.2 + eslint: 9.39.1(jiti@1.21.7) + eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@1.21.7)) + globals: 16.5.0 + postcss: 8.5.8 + postcss-selector-parser: 7.1.0 + transitivePeerDependencies: + - supports-color + eslint-plugin-astro@1.5.0(eslint@9.39.1(jiti@2.6.1)): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) @@ -41360,12 +41515,6 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-utils: 2.1.0 - regexpp: 3.2.0 - eslint-plugin-es@3.0.1(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -41419,7 +41568,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint@9.39.1(jiti@1.21.7)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint@9.39.1(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -41428,9 +41577,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.39.1(jiti@1.21.7) + eslint: 9.39.1(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@1.21.7)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -41442,7 +41591,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.3.3) + '@typescript-eslint/parser': 6.21.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.3.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -41560,16 +41709,6 @@ snapshots: resolve: 1.22.11 semver: 6.3.1 - eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-plugin-es: 3.0.1(eslint@9.39.1(jiti@1.21.7)) - eslint-utils: 2.1.0 - ignore: 5.3.2 - minimatch: 3.1.2 - resolve: 1.22.11 - semver: 6.3.1 - eslint-plugin-node@11.1.0(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -41600,16 +41739,6 @@ snapshots: '@types/eslint': 9.6.1 eslint-config-prettier: 8.10.2(eslint@8.57.1) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@1.21.7)))(eslint@9.39.1(jiti@1.21.7))(prettier@3.6.2): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - prettier: 3.6.2 - prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 8.10.2(eslint@9.39.1(jiti@1.21.7)) - eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -41634,10 +41763,6 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@1.21.7)): - dependencies: - eslint: 9.39.1(jiti@1.21.7) - eslint-plugin-react-hooks@4.6.2(eslint@9.39.1(jiti@2.6.1)): dependencies: eslint: 9.39.1(jiti@2.6.1) @@ -41668,28 +41793,6 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@1.21.7)): - dependencies: - array-includes: 3.1.9 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.39.1(jiti@1.21.7) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.9 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - eslint-plugin-react@7.37.5(eslint@9.39.1(jiti@2.6.1)): dependencies: array-includes: 3.1.9 @@ -42331,7 +42434,7 @@ snapshots: expo-dev-client@6.0.18(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-dev-launcher: 6.0.18(expo@55.0.5) expo-dev-menu: 7.0.17(expo@55.0.5) expo-dev-menu-interface: 2.0.0(expo@55.0.5) @@ -42372,7 +42475,7 @@ snapshots: expo-dev-launcher@6.0.18(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-dev-menu: 7.0.17(expo@55.0.5) expo-manifests: 1.0.9(expo@55.0.5) transitivePeerDependencies: @@ -42384,7 +42487,7 @@ snapshots: expo-dev-menu-interface@2.0.0(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-dev-menu-interface@55.0.1(expo@55.0.5): dependencies: @@ -42407,7 +42510,7 @@ snapshots: expo-dev-menu@7.0.17(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-dev-menu-interface: 2.0.0(expo@55.0.5) expo-device@55.0.10(expo@55.0.5): @@ -42438,7 +42541,7 @@ snapshots: expo-file-system@55.0.10(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) expo-file-system@55.0.10(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0)): @@ -42474,7 +42577,7 @@ snapshots: expo-font@55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) fontfaceobserver: 2.3.0 react: 19.2.0 react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) @@ -42503,7 +42606,7 @@ snapshots: expo-glass-effect@55.0.8(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: 19.2.0 react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) @@ -42526,11 +42629,11 @@ snapshots: expo-image-loader@55.0.0(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-image-picker@55.0.12(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-image-loader: 55.0.0(expo@55.0.5) expo-image@55.0.6(expo@54.0.25)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.4(react@19.1.0))(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.1.0))(react@19.1.0): @@ -42545,7 +42648,7 @@ snapshots: expo-image@55.0.6(expo@55.0.5)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: 19.2.0 react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) sf-symbols-typescript: 2.2.0 @@ -42587,7 +42690,7 @@ snapshots: expo-keep-awake@55.0.4(expo@55.0.5)(react@19.2.0): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react: 19.2.0 expo-keep-awake@55.0.4(expo@55.0.5)(react@19.2.4): @@ -42719,7 +42822,7 @@ snapshots: expo-manifests@1.0.9(expo@55.0.5): dependencies: '@expo/config': 12.0.10 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-json-utils: 0.15.0 transitivePeerDependencies: - supports-color @@ -42865,6 +42968,106 @@ snapshots: - react-native - supports-color + expo-router@55.0.5(6d2zdlrrz5o6rsdio5yhgp3juy): + dependencies: + '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@expo/metro-runtime': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@expo/schema-utils': 55.0.2 + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.17)(react@19.2.0) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@react-navigation/bottom-tabs': 7.15.5(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@react-navigation/native': 7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@react-navigation/native-stack': 7.14.4(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + client-only: 0.0.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-constants: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(typescript@5.9.3) + expo-glass-effect: 55.0.8(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + expo-image: 55.0.6(expo@55.0.5)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + expo-linking: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo-server: 55.0.6 + expo-symbols: 55.0.5(expo-font@14.0.10)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 19.2.0 + react-fast-compare: 3.2.2 + react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + react-native-safe-area-context: 5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + react-native-screens: 4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + semver: 7.6.3 + server-only: 0.0.1 + sf-symbols-typescript: 2.2.0 + shallowequal: 1.1.0 + use-latest-callback: 0.2.6(react@19.2.0) + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + optionalDependencies: + '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-gesture-handler@2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.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.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0) + react-dom: 19.2.0(react@19.2.0) + react-native-gesture-handler: 2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.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.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + - '@types/react' + - '@types/react-dom' + - expo-font + - supports-color + + expo-router@55.0.5(bhhyukj6njqvlfm2o3z6hpwcja): + dependencies: + '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@expo/metro-runtime': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@expo/schema-utils': 55.0.2 + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.17)(react@19.2.0) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + '@react-navigation/bottom-tabs': 7.15.5(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@react-navigation/native': 7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@react-navigation/native-stack': 7.14.4(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + client-only: 0.0.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo-constants: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(typescript@5.3.3) + expo-glass-effect: 55.0.8(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + expo-image: 55.0.6(expo@55.0.5)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + expo-linking: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo-server: 55.0.6 + expo-symbols: 55.0.5(expo-font@55.0.4)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + fast-deep-equal: 3.1.3 + invariant: 2.2.4 + nanoid: 3.3.11 + query-string: 7.1.3 + react: 19.2.0 + react-fast-compare: 3.2.2 + react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) + react-native-is-edge-to-edge: 1.2.1(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + react-native-safe-area-context: 5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + react-native-screens: 4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + semver: 7.6.3 + server-only: 0.0.1 + sf-symbols-typescript: 2.2.0 + shallowequal: 1.1.0 + use-latest-callback: 0.2.6(react@19.2.0) + vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + optionalDependencies: + '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-gesture-handler@2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@24.10.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.3.3)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0) + react-dom: 19.2.0(react@19.2.0) + react-native-gesture-handler: 2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + transitivePeerDependencies: + - '@react-native-masked-view/masked-view' + - '@types/react' + - '@types/react-dom' + - expo-font + - supports-color + expo-router@55.0.5(cdndhew7mqhhoslu6uoygxcgvm): dependencies: '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) @@ -43016,56 +43219,6 @@ snapshots: - supports-color optional: true - expo-router@55.0.5(fos732rmf7mlsrtwb6dv2k6f2i): - dependencies: - '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@expo/metro-runtime': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@expo/schema-utils': 55.0.2 - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.17)(react@19.2.0) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@react-navigation/bottom-tabs': 7.15.5(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@react-navigation/native': 7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@react-navigation/native-stack': 7.14.4(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - client-only: 0.0.1 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) - expo-constants: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(typescript@5.9.3) - expo-glass-effect: 55.0.8(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - expo-image: 55.0.6(expo@55.0.5)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - expo-linking: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) - expo-server: 55.0.6 - expo-symbols: 55.0.5(expo-font@14.0.10)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - fast-deep-equal: 3.1.3 - invariant: 2.2.4 - nanoid: 3.3.11 - query-string: 7.1.3 - react: 19.2.0 - react-fast-compare: 3.2.2 - react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - react-native-safe-area-context: 5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - react-native-screens: 4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - semver: 7.6.3 - server-only: 0.0.1 - sf-symbols-typescript: 2.2.0 - shallowequal: 1.1.0 - use-latest-callback: 0.2.6(react@19.2.0) - vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - optionalDependencies: - '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-gesture-handler@2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.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.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@24.10.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.9.3)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0) - react-dom: 19.2.0(react@19.2.0) - react-native-gesture-handler: 2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.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.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - transitivePeerDependencies: - - '@react-native-masked-view/masked-view' - - '@types/react' - - '@types/react-dom' - - expo-font - - supports-color - expo-router@55.0.5(ikidcy22c24hpqhht3cnsne4p4): dependencies: '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.84.1(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.4))(react@19.2.4) @@ -43267,59 +43420,9 @@ snapshots: - expo-font - supports-color - expo-router@55.0.5(shihlejigi2nkza7wltmngtxfm): - dependencies: - '@expo/log-box': 55.0.8(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@expo/metro-runtime': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@expo/schema-utils': 55.0.2 - '@radix-ui/react-slot': 1.2.3(@types/react@19.1.17)(react@19.2.0) - '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - '@react-navigation/bottom-tabs': 7.15.5(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@react-navigation/native': 7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@react-navigation/native-stack': 7.14.4(@react-navigation/native@7.1.33(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - client-only: 0.0.1 - debug: 4.4.3 - escape-string-regexp: 4.0.0 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) - expo-constants: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(typescript@5.3.3) - expo-glass-effect: 55.0.8(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - expo-image: 55.0.6(expo@55.0.5)(react-native-web@0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - expo-linking: 55.0.7(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) - expo-server: 55.0.6 - expo-symbols: 55.0.5(expo-font@55.0.4)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - fast-deep-equal: 3.1.3 - invariant: 2.2.4 - nanoid: 3.3.11 - query-string: 7.1.3 - react: 19.2.0 - react-fast-compare: 3.2.2 - react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) - react-native-is-edge-to-edge: 1.2.1(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - react-native-safe-area-context: 5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - react-native-screens: 4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - semver: 7.6.3 - server-only: 0.0.1 - sf-symbols-typescript: 2.2.0 - shallowequal: 1.1.0 - use-latest-callback: 0.2.6(react@19.2.0) - vaul: 1.1.2(@types/react-dom@19.2.3(@types/react@19.1.17))(@types/react@19.1.17)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - optionalDependencies: - '@react-navigation/drawer': 7.7.4(@react-navigation/native@7.1.21(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-gesture-handler@2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-safe-area-context@5.6.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native-screens@4.16.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - '@testing-library/react-native': 13.3.3(jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react-test-renderer@19.1.0(react@19.2.0))(react@19.2.0) - react-dom: 19.2.0(react@19.2.0) - react-native-gesture-handler: 2.28.0(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.7.4(@babel/core@7.28.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) - react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) - transitivePeerDependencies: - - '@react-native-masked-view/masked-view' - - '@types/react' - - '@types/react-dom' - - expo-font - - supports-color - expo-secure-store@55.0.8(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-server@1.0.4: {} @@ -43450,7 +43553,7 @@ snapshots: dependencies: '@react-native/normalize-colors': 0.83.2 debug: 4.4.3 - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) optionalDependencies: react-native-web: 0.21.2(encoding@0.1.13)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -43480,7 +43583,7 @@ snapshots: expo-updates-interface@2.0.0(expo@55.0.5): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) expo-updates-interface@55.1.3(expo@55.0.5): dependencies: @@ -43515,7 +43618,7 @@ snapshots: expo-web-browser@55.0.9(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0)): dependencies: - expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.3.3) + expo: 55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) react-native: 0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0) expo-web-browser@55.0.9(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.2.14)(react@19.2.0)): @@ -43681,7 +43784,7 @@ snapshots: expo@55.0.5(@babel/core@7.28.5)(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-router@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native-webview@13.12.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3): dependencies: '@babel/runtime': 7.29.2 - '@expo/cli': 55.0.15(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4)(expo-router@55.0.5)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + '@expo/cli': 55.0.15(@expo/dom-webview@55.0.3)(@expo/metro-runtime@55.0.7)(expo-constants@55.0.7)(expo-font@55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(expo-router@55.0.5)(expo@55.0.5)(react-dom@19.2.0(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) '@expo/config': 55.0.11(typescript@5.9.3) '@expo/config-plugins': 55.0.7 '@expo/devtools': 55.0.2(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) @@ -43690,7 +43793,7 @@ snapshots: '@expo/log-box': 55.0.7(@expo/dom-webview@55.0.3)(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) '@expo/metro': 54.2.0 '@expo/metro-config': 55.0.9(expo@55.0.5)(typescript@5.9.3) - '@expo/vector-icons': 15.0.3(expo-font@55.0.4)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) + '@expo/vector-icons': 15.0.3(expo-font@55.0.4(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0))(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0) '@ungap/structured-clone': 1.3.0 babel-preset-expo: 55.0.10(@babel/core@7.28.5)(@babel/runtime@7.29.2)(expo@55.0.5)(react-refresh@0.14.2) expo-asset: 55.0.8(expo@55.0.5)(react-native@0.83.2(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.2.0))(react@19.2.0)(typescript@5.9.3) @@ -45500,15 +45603,15 @@ snapshots: - ts-node optional: true - jest-cli@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)): + jest-cli@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/test-result': 30.3.0 '@jest/types': 30.3.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)) + jest-config: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) jest-util: 30.3.0 jest-validate: 30.3.0 yargs: 17.7.2 @@ -45710,7 +45813,7 @@ snapshots: - supports-color optional: true - jest-config@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)): + jest-config@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(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 @@ -45738,7 +45841,7 @@ snapshots: optionalDependencies: '@types/node': 22.19.1 esbuild-register: 3.6.0(esbuild@0.27.4) - ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.3.3) + ts-node: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -46430,12 +46533,12 @@ snapshots: - ts-node optional: true - jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)): + jest@30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)): dependencies: - '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)) + '@jest/core': 30.3.0(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) '@jest/types': 30.3.0 import-local: 3.2.0 - jest-cli: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3)) + jest-cli: 30.3.0(@types/node@22.19.1)(esbuild-register@3.6.0(esbuild@0.27.4))(ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -52412,25 +52515,6 @@ snapshots: yn: 3.1.1 optional: true - ts-node@10.9.2(@types/node@22.19.1)(typescript@5.3.3): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.12 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 22.19.1 - acorn: 8.15.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.3.3 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - optional: true - ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -53347,6 +53431,23 @@ snapshots: lightningcss: 1.30.2 terser: 5.44.1 + vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.25 + fsevents: 2.3.3 + jiti: 1.21.7 + lightningcss: 1.30.2 + terser: 5.44.1 + tsx: 4.21.0 + yaml: 2.8.3 + vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3): dependencies: esbuild: 0.25.12 @@ -53436,6 +53537,10 @@ snapshots: optionalDependencies: vite: 5.4.21(@types/node@22.19.1)(lightningcss@1.30.2)(terser@5.44.1) + vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3)): + optionalDependencies: + vite: 6.4.1(@types/node@20.19.25)(jiti@1.21.7)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3) + vitefu@1.1.1(vite@6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3)): optionalDependencies: vite: 6.4.1(@types/node@20.19.25)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.3)