diff --git a/apps/api/src/db/schema.ts b/apps/api/src/db/schema.ts index 3df72fe..54c1617 100644 --- a/apps/api/src/db/schema.ts +++ b/apps/api/src/db/schema.ts @@ -55,6 +55,7 @@ export const memberCredentials = mysqlTable( /** * Calendar collections discovered via CalDAV PROPFIND. * One row per calendar per member. ctag/syncToken track change state for polling (D-13). + * isShared: true when this calendar is the shared-family calendar (marked by operator). */ export const calendars = mysqlTable( 'calendars', @@ -69,6 +70,7 @@ export const calendars = mysqlTable( ctag: varchar('ctag', { length: 512 }), syncToken: varchar('sync_token', { length: 1024 }), lastSyncedAt: timestamp('last_synced_at'), + isShared: boolean('is_shared').default(false).notNull(), }, (t) => [index('idx_calendars_user_id').on(t.userId)], ) @@ -94,11 +96,16 @@ export const calendarEvents = mysqlTable( dtstartUtc: timestamp('dtstart_utc'), // NULL for all-day events dtstartDate: date('dtstart_date'), // set for all-day events; NULL for timed allDay: boolean('all_day').default(false).notNull(), + // hasRrule: pre-computed flag for SQL pre-filtering of recurring event masters. + // Events with hasRrule=true have dtstartUtc potentially years before any window, + // so the windowed query must include them regardless of dtstartUtc range (see RESEARCH.md §Pitfall 5). + hasRrule: boolean('has_rrule').default(false).notNull(), updatedAt: timestamp('updated_at').defaultNow().onUpdateNow(), }, (t) => [ index('idx_calendar_events_dtstart_utc').on(t.dtstartUtc), index('idx_calendar_events_dtstart_date').on(t.dtstartDate), + index('idx_calendar_events_has_rrule').on(t.hasRrule), // uid is unique per calendar (idempotency key for broker upsert) unique('uniq_calendar_uid').on(t.calendarId, t.uid), ], diff --git a/apps/api/tests/broker/expand.test.ts b/apps/api/tests/broker/expand.test.ts new file mode 100644 index 0000000..b76e0a3 --- /dev/null +++ b/apps/api/tests/broker/expand.test.ts @@ -0,0 +1,141 @@ +/** + * RED test stubs for expandOccurrences() — Wave 0 state. + * + * These tests encode the concrete behavioral contracts that Plan 02 (GREEN phase) must satisfy. + * All tests reference the not-yet-built module apps/api/src/broker/expand.ts and are expected + * to fail until that module is implemented. + * + * Contracts locked here: + * 1. DST wall-clock correctness: occurrences in America/New_York must show 10:00 local time + * on BOTH sides of the March 2026 EST→EDT boundary (not shifted ±1h by UTC fallback). + * 2. All-day events return allDay:true and start as 'YYYY-MM-DD' with no time component. + * 3. EXDATE exclusions reduce the returned array by exactly one occurrence. + */ + +import { describe, it, expect } from 'vitest' +import { readFileSync } from 'fs' +import { join, dirname } from 'path' +import { fileURLToPath } from 'url' + +// Not yet built — import will fail (RED state) until Plan 02 implements expand.ts +import { expandOccurrences } from '../../src/broker/expand.js' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const FIXTURES = join(__dirname, '../fixtures') + +function loadFixture(name: string): string { + return readFileSync(join(FIXTURES, name), 'utf8') +} + +describe('expandOccurrences — RED stubs (Wave 0)', () => { + + describe('DST correctness — weekly-dst.ics', () => { + it('returns 10:00 America/New_York wall-clock time on BOTH sides of March 2026 DST boundary', () => { + // The fixture has DTSTART;TZID=America/New_York:20260301T100000 RRULE:FREQ=WEEKLY. + // March 8 2026 is the DST transition (clocks spring forward at 02:00). + // Occurrences on 2026-03-01 (EST, UTC-5) and 2026-03-08 (DST transition day) and + // 2026-03-15 (EDT, UTC-4) must ALL show hour === 10 in America/New_York. + // A broken implementation that falls back to UTC would show hour === 10 UTC before + // transition and hour === 11 local after transition — off by one DST hour. + const rawVevent = loadFixture('weekly-dst.ics') + const windowStart = new Date('2026-03-01T00:00:00Z') + const windowEnd = new Date('2026-04-01T00:00:00Z') + + const occurrences = expandOccurrences( + rawVevent, + windowStart, + windowEnd, + 1, // calendarId + 'My Calendar', // calendarName + 1, // ownerUserId + '#4A90D9', // color + false, // isShared + ) + + // Should return several weekly occurrences in March + expect(occurrences.length).toBeGreaterThan(0) + + // The key contract: every occurrence must have local hour === 10 in America/New_York. + // We verify this by checking the ISO string offset — before DST: offset is -05:00 + // (so 10:00-05:00 = 15:00 UTC); after DST: offset is -04:00 (10:00-04:00 = 14:00 UTC). + // Both are valid as long as the LOCAL wall-clock hour is 10. + for (const occ of occurrences) { + expect(occ.allDay).toBe(false) + // start must be an offset-aware ISO string: '2026-03-01T10:00:00-05:00' or similar + expect(occ.start).toMatch(/T10:00:00/) + } + + // Explicitly check one pre-transition occurrence (EST) and one post-transition (EDT) + const preTransition = occurrences.find(o => o.start.includes('2026-03-01')) + const postTransition = occurrences.find(o => o.start.includes('2026-03-15')) + + expect(preTransition).toBeDefined() + expect(postTransition).toBeDefined() + + // Pre-transition occurrence: EST offset -05:00 + expect(preTransition!.start).toContain('T10:00:00') + // Post-transition occurrence: EDT offset -04:00 + expect(postTransition!.start).toContain('T10:00:00') + }) + }) + + describe('All-day events — allday-birthday.ics', () => { + it('returns allDay:true with start as YYYY-MM-DD and no time component', () => { + // The fixture has DTSTART;VALUE=DATE:20260615 with RRULE:FREQ=YEARLY. + // The all-day occurrence should have allDay:true and start === '2026-06-15' (DATE format). + // A broken implementation returning '2026-06-15T00:00:00Z' would fail on date-shift. + const rawVevent = loadFixture('allday-birthday.ics') + const windowStart = new Date('2026-06-01T00:00:00Z') + const windowEnd = new Date('2026-07-01T00:00:00Z') + + const occurrences = expandOccurrences( + rawVevent, + windowStart, + windowEnd, + 1, + 'My Calendar', + 1, + '#4A90D9', + false, + ) + + expect(occurrences.length).toBe(1) + + const occ = occurrences[0] + expect(occ.allDay).toBe(true) + // start must be plain date string 'YYYY-MM-DD' — no 'T' time component + expect(occ.start).toBe('2026-06-15') + expect(occ.start).not.toContain('T') + }) + }) + + describe('EXDATE exclusions — exdate-series.ics', () => { + it('omits the EXDATE-excluded occurrence (array length is one fewer than un-excluded)', () => { + // The fixture has RRULE:FREQ=WEEKLY;COUNT=5 with EXDATE for the June 15 occurrence. + // Without EXDATE: 5 occurrences (Jun 1, Jun 8, Jun 15, Jun 22, Jun 29). + // With EXDATE on Jun 15: 4 occurrences returned. + // ICAL.RecurExpansion handles EXDATE internally — no manual filtering needed. + const rawVevent = loadFixture('exdate-series.ics') + const windowStart = new Date('2026-06-01T00:00:00Z') + const windowEnd = new Date('2026-07-01T00:00:00Z') + + const occurrences = expandOccurrences( + rawVevent, + windowStart, + windowEnd, + 1, + 'My Calendar', + 1, + '#4A90D9', + false, + ) + + // 5 total occurrences minus 1 EXDATE = 4 + expect(occurrences.length).toBe(4) + + // The June 15 occurrence must be absent + const june15 = occurrences.find(o => o.start.includes('2026-06-15')) + expect(june15).toBeUndefined() + }) + }) +}) diff --git a/apps/api/tests/fixtures/allday-birthday.ics b/apps/api/tests/fixtures/allday-birthday.ics new file mode 100644 index 0000000..0dc1c59 --- /dev/null +++ b/apps/api/tests/fixtures/allday-birthday.ics @@ -0,0 +1,11 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//FamilySync//Test//EN +BEGIN:VEVENT +UID:birthday-annual@familysync.test +DTSTART;VALUE=DATE:20260615 +RRULE:FREQ=YEARLY +SUMMARY:Birthday +DESCRIPTION:Annual birthday event - all-day, no time component +END:VEVENT +END:VCALENDAR diff --git a/apps/api/tests/fixtures/exdate-series.ics b/apps/api/tests/fixtures/exdate-series.ics new file mode 100644 index 0000000..3cf11e2 --- /dev/null +++ b/apps/api/tests/fixtures/exdate-series.ics @@ -0,0 +1,30 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//FamilySync//Test//EN +BEGIN:VTIMEZONE +TZID:America/New_York +BEGIN:STANDARD +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:weekly-exdate-series@familysync.test +DTSTART;TZID=America/New_York:20260601T090000 +RRULE:FREQ=WEEKLY;COUNT=5 +EXDATE;TZID=America/New_York:20260615T090000 +SUMMARY:Weekly Series With Exdate +DESCRIPTION:Weekly series with the June 15 occurrence excluded via EXDATE +DURATION:PT30M +END:VEVENT +END:VCALENDAR diff --git a/apps/api/tests/fixtures/weekly-dst.ics b/apps/api/tests/fixtures/weekly-dst.ics new file mode 100644 index 0000000..a45b7c3 --- /dev/null +++ b/apps/api/tests/fixtures/weekly-dst.ics @@ -0,0 +1,29 @@ +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:-//FamilySync//Test//EN +BEGIN:VTIMEZONE +TZID:America/New_York +BEGIN:STANDARD +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11 +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3 +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:weekly-meeting-dst@familysync.test +DTSTART;TZID=America/New_York:20260301T100000 +RRULE:FREQ=WEEKLY +SUMMARY:Weekly Meeting DST Test +DESCRIPTION:Weekly meeting spanning the March 2026 America/New_York EST to EDT transition +DURATION:PT1H +END:VEVENT +END:VCALENDAR diff --git a/apps/api/tests/routes/events.test.ts b/apps/api/tests/routes/events.test.ts new file mode 100644 index 0000000..a062101 --- /dev/null +++ b/apps/api/tests/routes/events.test.ts @@ -0,0 +1,68 @@ +/** + * RED test stubs for GET /api/events — Wave 0 state. + * + * These tests encode the contract for the evolved windowed /api/events route. + * They reference the current events route which does not yet support windowed queries, + * color joins, or the isShared flag — all tests are expected to fail (RED) until Plan 02. + * + * Contracts locked here: + * 1. Missing/malformed start or end params → 400 (input validation guard) + * 2. Valid window returns occurrences each with a color field and isShared flag + */ + +import { describe, it, expect, vi } from 'vitest' + +// Mock DB to avoid real DB connections in unit tests +vi.mock('../../src/db/client.js', () => ({ + db: { + select: vi.fn().mockReturnValue({ + from: vi.fn().mockReturnValue({ + innerJoin: vi.fn().mockReturnValue({ + innerJoin: vi.fn().mockReturnValue({ + where: vi.fn().mockResolvedValue([]), + }), + }), + where: vi.fn().mockResolvedValue([]), + }), + }), + }, +})) + +describe('GET /api/events', () => { + + it('returns 400 when start param is missing', async () => { + const { app } = await import('../../src/index.js') + const res = await app.request('/api/events?end=2026-07-01') + expect(res.status).toBe(400) + }) + + it('returns 400 when end param is missing', async () => { + const { app } = await import('../../src/index.js') + const res = await app.request('/api/events?start=2026-06-01') + expect(res.status).toBe(400) + }) + + it('returns 400 when start param is malformed (not YYYY-MM-DD)', async () => { + const { app } = await import('../../src/index.js') + const res = await app.request('/api/events?start=not-a-date&end=2026-07-01') + expect(res.status).toBe(400) + }) + + it('returns occurrences with color and isShared fields for valid window', async () => { + const { app } = await import('../../src/index.js') + const res = await app.request('/api/events?start=2026-06-01&end=2026-07-01') + // Route not yet evolved — currently returns raw events without windowing or color join. + // This test will pass once Plan 02 implements the windowed query + expansion. + expect(res.status).toBe(200) + const body = await res.json() as { occurrences: Array<{ color: string; isShared: boolean }> } + expect(body).toHaveProperty('occurrences') + expect(Array.isArray(body.occurrences)).toBe(true) + // Each occurrence must carry color + isShared (may be empty array if DB is mocked empty) + for (const occ of body.occurrences) { + expect(occ).toHaveProperty('color') + expect(typeof occ.color).toBe('string') + expect(occ).toHaveProperty('isShared') + expect(typeof occ.isShared).toBe('boolean') + } + }) +}) diff --git a/apps/pwa/package.json b/apps/pwa/package.json index fda7356..4107481 100644 --- a/apps/pwa/package.json +++ b/apps/pwa/package.json @@ -7,7 +7,8 @@ "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", - "typecheck": "tsc --noEmit" + "typecheck": "tsc --noEmit", + "test": "vitest run" }, "dependencies": { "@tanstack/react-query": "5.101.0", @@ -17,10 +18,14 @@ "zustand": "5.0.14" }, "devDependencies": { + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.3.0", "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "@vitejs/plugin-react": "^4.3.0", + "jsdom": "^26.1.0", "typescript": "^5.5.0", - "vite": "8.0.16" + "vite": "8.0.16", + "vitest": "^4.1.8" } } diff --git a/apps/pwa/src/lib/calendarConfig.test.ts b/apps/pwa/src/lib/calendarConfig.test.ts new file mode 100644 index 0000000..d1686cb --- /dev/null +++ b/apps/pwa/src/lib/calendarConfig.test.ts @@ -0,0 +1,51 @@ +/** + * RED test stubs for calendarConfig — Wave 0 state. + * + * These tests encode the contracts for calendarConfig.ts. + * The tests reference the not-yet-built module apps/pwa/src/lib/calendarConfig.ts. + * + * Contracts locked here: + * 1. WEEK_START_DAY === 0 (JS/date-fns Sunday convention) → Schedule-X firstDayOfWeek === 7 + * (Temporal convention: 7 = Sunday, not 0) + * Passing 0 would silently default to Monday in Schedule-X v4. + */ + +import { describe, it, expect } from 'vitest' + +// Not yet built — import will fail (RED state) until Plan 03 implements calendarConfig.ts +import { WEEK_START_DAY, buildCalendarConfig } from './calendarConfig.js' + +describe('calendarConfig — RED stubs (Wave 0)', () => { + + it('WEEK_START_DAY constant equals 0 (Sunday in JS/date-fns convention)', () => { + // The project uses 0 = Sunday (JS/date-fns convention). + // This constant is translated to 7 before passing to Schedule-X. + expect(WEEK_START_DAY).toBe(0) + }) + + it('WEEK_START_DAY=0 translates to Schedule-X firstDayOfWeek === 7', () => { + // Schedule-X v4 uses Temporal numbering: 1=Mon, 7=Sun. + // WEEK_START_DAY=0 (JS Sunday) must become 7 (Temporal Sunday). + // This avoids the Pitfall 1 silent failure where weeks start on Monday. + const config = buildCalendarConfig([]) + expect(config.firstDayOfWeek).toBe(7) + }) + + it('buildCalendarConfig includes shared-family calendar with id "shared"', () => { + const config = buildCalendarConfig([]) + expect(config.calendars).toHaveProperty('shared') + expect(config.calendars['shared'].colorName).toBe('shared') + }) + + it('buildCalendarConfig includes per-member calendars keyed by String(userId)', () => { + const members = [ + { id: '1', name: 'Lucas', color: '#4A90D9' }, + { id: '2', name: 'Spouse', color: '#E8734A' }, + ] + const config = buildCalendarConfig(members) + expect(config.calendars).toHaveProperty('1') + expect(config.calendars).toHaveProperty('2') + expect(config.calendars['1'].lightColors.main).toBe('#4A90D9') + expect(config.calendars['2'].lightColors.main).toBe('#E8734A') + }) +}) diff --git a/apps/pwa/src/lib/hydrateEvents.test.ts b/apps/pwa/src/lib/hydrateEvents.test.ts new file mode 100644 index 0000000..794718a --- /dev/null +++ b/apps/pwa/src/lib/hydrateEvents.test.ts @@ -0,0 +1,133 @@ +/** + * RED test stubs for hydrateEvents() — Wave 0 state. + * + * These tests encode the concrete contracts that Plan 03 (GREEN phase) must satisfy. + * The tests reference the not-yet-built module apps/pwa/src/lib/hydrateEvents.ts and + * are expected to fail until that module is implemented. + * + * Contracts locked here: + * 1. All-day occurrence (allDay:true, start 'YYYY-MM-DD') → start is Temporal.PlainDate + * 2. Timed occurrence (allDay:false) → start is Temporal.ZonedDateTime + * 3. Shared occurrence (isShared:true) → Schedule-X calendarId === 'shared' + * 4. Personal occurrence (isShared:false, ownerUserId:7, calendarId:99) → + * Schedule-X calendarId === '7' (String(ownerUserId)), NOT '99' (String(calendarId)) + * This assertion locks the Plan 03 routing fix. + */ + +import { describe, it, expect } from 'vitest' + +// Not yet built — import will fail (RED state) until Plan 03 implements hydrateEvents.ts +import { hydrateEvents } from './hydrateEvents.js' + +// Minimal CalendarOccurrence shape for test purposes +interface TestOccurrence { + id: string + uid: string + calendarId: number + calendarName: string + ownerUserId: number + color: string + isShared: boolean + title: string + start: string + end: string + allDay: boolean + location: string | null + description: string | null +} + +function makeOccurrence(overrides: Partial): TestOccurrence { + return { + id: 'test::2026-06-15', + uid: 'test-uid', + calendarId: 1, + calendarName: 'Test Calendar', + ownerUserId: 1, + color: '#4A90D9', + isShared: false, + title: 'Test Event', + start: '2026-06-15T10:00:00-04:00[America/New_York]', + end: '2026-06-15T11:00:00-04:00[America/New_York]', + allDay: false, + location: null, + description: null, + ...overrides, + } +} + +describe('hydrateEvents — RED stubs (Wave 0)', () => { + + describe('Temporal type conversion', () => { + it('converts all-day occurrence (allDay:true) to Temporal.PlainDate for start and end', () => { + const occurrences = [ + makeOccurrence({ + allDay: true, + start: '2026-06-15', + end: '2026-06-15', + }), + ] + + const result = hydrateEvents(occurrences) + expect(result).toHaveLength(1) + + const evt = result[0] + // Schedule-X requires Temporal.PlainDate for all-day events + expect(evt.start).toBeInstanceOf(Temporal.PlainDate) + expect(evt.end).toBeInstanceOf(Temporal.PlainDate) + }) + + it('converts timed occurrence (allDay:false) to Temporal.ZonedDateTime for start and end', () => { + const occurrences = [ + makeOccurrence({ + allDay: false, + start: '2026-06-15T10:00:00-04:00[America/New_York]', + end: '2026-06-15T11:00:00-04:00[America/New_York]', + }), + ] + + const result = hydrateEvents(occurrences) + expect(result).toHaveLength(1) + + const evt = result[0] + expect(evt.start).toBeInstanceOf(Temporal.ZonedDateTime) + expect(evt.end).toBeInstanceOf(Temporal.ZonedDateTime) + }) + }) + + describe('calendarId routing — Plan 03 contract', () => { + it('shared occurrence (isShared:true) gets Schedule-X calendarId "shared"', () => { + const occurrences = [ + makeOccurrence({ + isShared: true, + calendarId: 5, + ownerUserId: 2, + }), + ] + + const result = hydrateEvents(occurrences) + expect(result).toHaveLength(1) + + // Shared-family events must route to the 'shared' calendar slot in Schedule-X config + expect(result[0].calendarId).toBe('shared') + }) + + it('personal occurrence (isShared:false) gets Schedule-X calendarId === String(ownerUserId), NOT String(calendarId)', () => { + // This assertion locks the routing decision: personal events are grouped by MEMBER (ownerUserId), + // not by DB calendarId. If two calendars belong to the same user, they share the same color slot. + const occurrences = [ + makeOccurrence({ + isShared: false, + calendarId: 99, // DB calendar row id + ownerUserId: 7, // DB user id — this is the correct Schedule-X key + }), + ] + + const result = hydrateEvents(occurrences) + expect(result).toHaveLength(1) + + // Must be '7' (String(ownerUserId)), NOT '99' (String(calendarId)) + expect(result[0].calendarId).toBe('7') + expect(result[0].calendarId).not.toBe('99') + }) + }) +}) diff --git a/apps/pwa/vitest.config.ts b/apps/pwa/vitest.config.ts new file mode 100644 index 0000000..2a2cda2 --- /dev/null +++ b/apps/pwa/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + environment: 'jsdom', + globals: true, + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc58ea9..13bbc6f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -52,7 +52,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.1.8 - version: 4.1.8(@types/node@22.19.19)(vite@8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4)) + version: 4.1.8(@types/node@22.19.19)(jsdom@26.1.0)(vite@8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4)) apps/pwa: dependencies: @@ -72,6 +72,12 @@ importers: specifier: 5.0.14 version: 5.0.14(@types/react@19.2.16)(react@19.2.7) devDependencies: + '@testing-library/jest-dom': + specifier: ^6.6.3 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.0 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@types/react': specifier: ^19.0.0 version: 19.2.16 @@ -81,15 +87,27 @@ importers: '@vitejs/plugin-react': specifier: ^4.3.0 version: 4.7.0(vite@8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4)) + jsdom: + specifier: ^26.1.0 + version: 26.1.0 typescript: specifier: ^5.5.0 version: 5.9.3 vite: specifier: 8.0.16 version: 8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4) + vitest: + specifier: ^4.1.8 + version: 4.1.8(@types/node@22.19.19)(jsdom@26.1.0)(vite@8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4)) packages: + '@adobe/css-tools@4.5.0': + resolution: {integrity: sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==} + + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} @@ -161,6 +179,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} @@ -173,6 +195,34 @@ packages: resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + '@drizzle-team/brocli@0.10.2': resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} @@ -792,9 +842,35 @@ packages: peerDependencies: react: ^18 || ^19 + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -862,6 +938,25 @@ packages: '@vitest/utils@4.1.8': resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -896,9 +991,20 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -908,14 +1014,27 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + drizzle-kit@0.31.10: resolution: {integrity: sha512-7OZcmQUrdGI+DUNNsKBn1aW8qSoKuTH7d0mYgSP8bAzdFzKoovxEFnoGQp2dVs82EOJeYycqRtciopszwUf8bw==} hasBin: true @@ -1015,6 +1134,10 @@ packages: electron-to-chromium@1.5.366: resolution: {integrity: sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg==} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + es-module-lexer@2.1.0: resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} @@ -1072,19 +1195,51 @@ packages: resolution: {integrity: sha512-eIaZ9qDgu7XV0pxOCrg7/WhnQ6Ivm22UcxhXx/A3dcbqbbYgBEkc6e/J/s7j2tS96zoB0S9VBdLwQNCWwUo4LA==} engines: {node: '>=16.9.0'} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + ical.js@2.2.1: resolution: {integrity: sha512-yK/UlPbEs316igb/tjRgbFA8ZV75rCsBJp/hWOatpyaPNlgw0dGDmU+FoicOcwX4xXkeXOkYiOmCqNPFpNPkQg==} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + iconv-lite@0.7.2: resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-property@1.0.2: resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1172,6 +1327,9 @@ packages: long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} @@ -1179,9 +1337,17 @@ packages: resolution: {integrity: sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==} engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1208,12 +1374,18 @@ packages: resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} engines: {node: '>=18'} + nwsapi@2.2.23: + resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} + oauth4webapi@3.8.6: resolution: {integrity: sha512-iwemM91xz8nryHti2yTmg5fhyEMVOkOXwHNqbvcATjyajb5oQxCQzrNOA6uElRHuMhQQTKUyFKV9y/CNyg25BQ==} obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1228,11 +1400,22 @@ packages: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + react-dom@19.2.7: resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: react: ^19.2.7 + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -1241,6 +1424,10 @@ packages: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -1249,6 +1436,9 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1256,6 +1446,10 @@ packages: resolution: {integrity: sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==} engines: {node: '>=11.0.0'} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -1287,6 +1481,13 @@ packages: std-env@4.1.0: resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -1302,6 +1503,21 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + tsdav@2.2.2: resolution: {integrity: sha512-/uC/RItcdYVxUj8b2gT4CorAkUmDFUuzhgS4XVT+OVzGRE80hH5ldubcWH13dHa2T9rNaXu6UIEWHlQ1UL7H1Q==} engines: {node: '>=18'} @@ -1412,15 +1628,55 @@ packages: jsdom: optional: true + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xml-js@1.6.11: resolution: {integrity: sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==} hasBin: true + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -1447,6 +1703,16 @@ packages: snapshots: + '@adobe/css-tools@4.5.0': {} + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 @@ -1536,6 +1802,8 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 + '@babel/runtime@7.29.7': {} + '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 @@ -1559,6 +1827,26 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + '@drizzle-team/brocli@0.10.2': {} '@emnapi/core@1.10.0': @@ -1913,11 +2201,43 @@ snapshots: '@tanstack/query-core': 5.101.0 react: 19.2.7 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.5.0 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.16))(@types/react@19.2.16)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.1 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.16 + '@types/react-dom': 19.2.3(@types/react@19.2.16) + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 optional: true + '@types/aria-query@5.0.4': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.29.7 @@ -2013,6 +2333,18 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + agent-base@7.1.4: {} + + ansi-regex@5.0.1: {} + + ansi-styles@5.2.0: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + assertion-error@2.0.1: {} aws-ssl-profiles@1.1.2: {} @@ -2037,16 +2369,36 @@ snapshots: convert-source-map@2.0.0: {} + css.escape@1.5.1: {} + + cssstyle@4.6.0: + dependencies: + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 + csstype@3.2.3: {} + data-urls@5.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + debug@4.4.3: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + denque@2.1.0: {} + dequal@2.0.3: {} + detect-libc@2.1.2: {} + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + drizzle-kit@0.31.10: dependencies: '@drizzle-team/brocli': 0.10.2 @@ -2060,6 +2412,8 @@ snapshots: electron-to-chromium@1.5.366: {} + entities@6.0.1: {} + es-module-lexer@2.1.0: {} esbuild@0.18.20: @@ -2172,16 +2526,69 @@ snapshots: hono@4.12.23: {} + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + ical.js@2.2.1: {} + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 + indent-string@4.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + is-property@1.0.2: {} js-tokens@4.0.0: {} + jsdom@26.1.0: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.23 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.21.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jsesc@3.1.0: {} json5@2.2.3: {} @@ -2237,16 +2644,22 @@ snapshots: long@5.3.2: {} + lru-cache@10.4.3: {} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 lru.min@1.1.4: {} + lz-string@1.5.0: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + min-indent@1.0.1: {} + ms@2.1.3: {} mysql2@3.22.4(@types/node@22.19.19): @@ -2271,10 +2684,16 @@ snapshots: node-releases@2.0.47: {} + nwsapi@2.2.23: {} + oauth4webapi@3.8.6: {} obug@2.1.1: {} + parse5@7.3.0: + dependencies: + entities: 6.0.1 + pathe@2.0.3: {} picocolors@1.1.1: {} @@ -2287,15 +2706,30 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + punycode@2.3.1: {} + react-dom@19.2.7(react@19.2.7): dependencies: react: 19.2.7 scheduler: 0.27.0 + react-is@17.0.2: {} + react-refresh@0.17.0: {} react@19.2.7: {} + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + resolve-pkg-maps@1.0.0: {} rolldown@1.0.3: @@ -2319,10 +2753,16 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.3 '@rolldown/binding-win32-x64-msvc': 1.0.3 + rrweb-cssom@0.8.0: {} + safer-buffer@2.1.2: {} sax@1.6.0: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.27.0: {} semver@6.3.1: {} @@ -2344,6 +2784,12 @@ snapshots: std-env@4.1.0: {} + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + symbol-tree@3.2.4: {} + tinybench@2.9.0: {} tinyexec@1.2.4: {} @@ -2355,6 +2801,20 @@ snapshots: tinyrainbow@3.1.0: {} + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tough-cookie@5.1.2: + dependencies: + tldts: 6.1.86 + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + tsdav@2.2.2: dependencies: base-64: 1.0.0 @@ -2395,7 +2855,7 @@ snapshots: fsevents: 2.3.3 tsx: 4.22.4 - vitest@4.1.8(@types/node@22.19.19)(vite@8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4)): + vitest@4.1.8(@types/node@22.19.19)(jsdom@26.1.0)(vite@8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4)): dependencies: '@vitest/expect': 4.1.8 '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@22.19.19)(esbuild@0.28.0)(tsx@4.22.4)) @@ -2419,18 +2879,42 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.19 + jsdom: 26.1.0 transitivePeerDependencies: - msw + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@7.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 + ws@8.21.0: {} + xml-js@1.6.11: dependencies: sax: 1.6.0 + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + yallist@3.1.1: {} zod@3.25.76: {}