feat(02-01): schema columns, PWA vitest harness, ICS fixtures, RED test stubs
- Add calendarEvents.hasRrule boolean + idx_calendar_events_has_rrule index (Phase 2 pre-filter) - Add calendars.isShared boolean for shared-family calendar identification - Create apps/pwa/vitest.config.ts with jsdom environment - Add vitest, @testing-library/react, jsdom, @testing-library/jest-dom to PWA devDependencies - Add "test": "vitest run" script to apps/pwa/package.json - Create three ICS fixtures: weekly-dst.ics (DST spanning), allday-birthday.ics, exdate-series.ics - Create RED test stub expand.test.ts with concrete DST wall-clock assertions (10:00 local both sides of March 2026 boundary) - Create RED test stub events.test.ts with 400 validation and color/isShared field contracts - Create RED test stub hydrateEvents.test.ts with Temporal type and calendarId routing contracts (shared→"shared", personal→String(ownerUserId)) - Create RED test stub calendarConfig.test.ts with firstDayOfWeek 0→7 translation contract
This commit is contained in:
@@ -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),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user