style(13-03): apply Prettier formatting across repo

Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
This commit is contained in:
Lucas Berger
2026-06-11 20:35:18 -04:00
parent 4bc0445173
commit 982438dc10
398 changed files with 19050 additions and 16382 deletions
@@ -1,6 +1,6 @@
---
phase: 02-calendar-display
plan: "02"
plan: '02'
subsystem: api-expansion, api-events
tags: [recurrence-expansion, dst-correctness, windowed-query, color-join, zod-validation]
dependency_graph:
@@ -22,12 +22,12 @@ key_files:
- apps/api/src/routes/events.ts
- apps/api/tests/routes/events.test.ts
decisions:
- "ICAL.TimezoneService.register(timezone, name) arg order: first arg is Timezone object, second is optional name string — inverted from research pseudocode which incorrectly placed tzid first"
- "events.test.ts needed @hono/oidc-auth mock — oidcAuthMiddleware throws HTTP 500 when OIDC env vars are absent, blocking all route tests; added passthrough mock as Rule 3 fix"
- "Task 3 (shared-family calendar marking) deferred by operator: id=1 is the operator personal calendar, dedicated shared Family calendar does not exist yet — is_shared stays false for all current rows"
- 'ICAL.TimezoneService.register(timezone, name) arg order: first arg is Timezone object, second is optional name string — inverted from research pseudocode which incorrectly placed tzid first'
- 'events.test.ts needed @hono/oidc-auth mock — oidcAuthMiddleware throws HTTP 500 when OIDC env vars are absent, blocking all route tests; added passthrough mock as Rule 3 fix'
- 'Task 3 (shared-family calendar marking) deferred by operator: id=1 is the operator personal calendar, dedicated shared Family calendar does not exist yet — is_shared stays false for all current rows'
metrics:
duration: "22m"
completed: "2026-06-05"
duration: '22m'
completed: '2026-06-05'
tasks_completed: 2
tasks_deferred: 1
files_created: 1
@@ -45,6 +45,7 @@ Server-side recurrence expansion with DST-correct VTIMEZONE registration, all-da
New file exporting `CalendarOccurrence` interface and `expandOccurrences()` function.
**Interface `CalendarOccurrence`** — carries all fields the Schedule-X frontend needs:
- `id`: `${uid}::${startIso}` stable identity
- `ownerUserId`: load-bearing client field; Schedule-X calendarId = `isShared ? 'shared' : String(ownerUserId)`
- `isShared`: from calendar row, stamped on every occurrence from meta
@@ -52,6 +53,7 @@ New file exporting `CalendarOccurrence` interface and `expandOccurrences()` func
- `allDay`, `color`, `calendarId`, `calendarName`, `uid`, `title`, `location`, `description`
**Implementation contracts met:**
1. `ICAL.parse()` in try/catch — malformed input returns `[]` without throwing
2. VTIMEZONE registration loop runs before `new ICAL.RecurExpansion(...)` — mandatory for DST correctness (Pitfall 3)
3. Non-recurring: single occurrence check against [windowStart, windowEnd)
@@ -61,6 +63,7 @@ New file exporting `CalendarOccurrence` interface and `expandOccurrences()` func
7. No `import ... 'rrule'` anywhere in expand.ts
**Test results (expand.test.ts — 3/3 green):**
- DST: `T10:00:00` present in every March 2026 occurrence across EST→EDT boundary
- All-day: `allDay:true`, `start === '2026-06-15'`, no `T` in string
- EXDATE: 4 occurrences returned (not 5), June 15 absent
@@ -70,6 +73,7 @@ New file exporting `CalendarOccurrence` interface and `expandOccurrences()` func
Rewrote `eventsRouter.get('/')` from raw table dump to display-ready windowed endpoint.
**Zod validation:**
- `eventsQuerySchema`: `start` and `end` each required, validated as `/^\d{4}-\d{2}-\d{2}$/`
- `zValidator('query', eventsQuerySchema)` — 400 returned automatically on schema failure
- Post-schema: 90-day window cap returns 400 if span exceeds limit (T-02b-02 DoS guard)
@@ -79,6 +83,7 @@ Rewrote `eventsRouter.get('/')` from raw table dump to display-ready windowed en
**WHERE pre-filter (RESEARCH.md Open Q3 / Pitfall 5):**
Three-branch OR covering:
1. `hasRrule=1 AND dtstartUtc < windowEnd` — recurring masters from any date
2. `hasRrule=0 AND dtstartUtc IN [windowStart, windowEnd)` — non-recurring timed events
3. `dtstartDate IN [start, end)` — all-day events (DATE comparison)
@@ -92,6 +97,7 @@ Three-branch OR covering:
**Broker-boundary invariant preserved:** No tsdav / createFastmailClient import.
**Test results (events.test.ts — 4/4 green):**
- 400 on missing start
- 400 on missing end
- 400 on malformed date
@@ -114,12 +120,14 @@ The `calendars.is_shared` column exists (added in Plan 01, default false). The r
### Auto-fixed Issues
**1. [Rule 1 - Bug] ICAL.TimezoneService.register() argument order**
- **Found during:** Task 1 typecheck
- **Issue:** Research pseudocode showed `register(tzid, timezone)` but the actual API is `register(timezone, name?)` — tzid-first call causes TS2345 type error
- **Fix:** Swapped to `register(new ICAL.Timezone({ component: vtz, tzid }), tzid)`
- **Files modified:** apps/api/src/broker/expand.ts
**2. [Rule 3 - Blocker] @hono/oidc-auth throws 500 in test environment**
- **Found during:** Task 2 (events test execution)
- **Issue:** `oidcAuthMiddleware()` calls `throw new HTTPException(500, ...)` when `OIDC_AUTH_SECRET` env var is absent. The RED stub's test imports `app` from `src/index.js` which mounts `oidcAuthMiddleware`, so all `/api/events` requests get 500 before reaching the route handler.
- **Fix:** Added `vi.mock('@hono/oidc-auth', ...)` passthrough mock to events.test.ts, making `oidcAuthMiddleware` a no-op in the test environment. Same pattern works for future route tests that use app.request().
@@ -141,16 +149,20 @@ No new threat surface beyond the plan's threat model.
## Self-Check: PASSED
Files created:
- [x] apps/api/src/broker/expand.ts — FOUND
Files modified:
- [x] apps/api/src/routes/events.ts — FOUND
- [x] apps/api/tests/routes/events.test.ts — FOUND
Commits:
- [x] 6736194 — feat(02-02): expandOccurrences Task 1
- [x] 9ee26c0 — feat(02-02): windowed events route Task 2
Test suite:
- [x] pnpm --filter @familysync/api test — 34/34 passed
- [x] pnpm --filter @familysync/api typecheck — clean