From 99cb1698a83eb46b831ac78199d129215ecee6ed Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Fri, 5 Jun 2026 20:38:27 -0400 Subject: [PATCH] feat(03-09): rename eventFieldsSchema to canonical title/start/end contract (CR-01) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace summary→title, dtstart→start, dtend→end in eventFieldsSchema - Server now accepts exact CreateEventPayload shape the PWA sends - Update existing write tests to use new canonical field names - No internal rename map; one canonical name set end-to-end - grep confirms no summary/dtstart/dtend in eventFieldsSchema --- apps/api/src/routes/events.ts | 15 +++++++++++---- apps/api/tests/routes/events.test.ts | 18 +++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/apps/api/src/routes/events.ts b/apps/api/src/routes/events.ts index 1e22c32..18f2436 100644 --- a/apps/api/src/routes/events.ts +++ b/apps/api/src/routes/events.ts @@ -64,12 +64,19 @@ const eventsQuerySchema = z.object({ end: z.string().regex(/^\d{4}-\d{2}-\d{2}$/), }) -/** Shared event field validation (V5 — bounded lengths, T-03-08). */ +/** + * Shared event field validation (V5 — bounded lengths, T-03-08). + * + * Field names match the PWA CreateEventPayload (apps/pwa/src/api/client.ts:119-128) + * exactly — title/start/end — so no rename map is needed end-to-end (CR-01). + * The stored payload JSON uses these same names; the outbox worker (plan 03-10) + * reads title/start/end when building the VEVENT. + */ const eventFieldsSchema = z.object({ - summary: z.string().min(1).max(255), + title: z.string().min(1).max(255), allDay: z.boolean(), - dtstart: z.string().min(1).max(64), // ISO string or DATE - dtend: z.string().min(1).max(64), + start: z.string().min(1).max(64), // ISO string or DATE (YYYY-MM-DD for allDay) + end: z.string().min(1).max(64), location: z.string().max(2000).optional(), description: z.string().max(2000).optional(), recurrence: z.enum(['none', 'daily', 'weekly', 'monthly', 'yearly']).optional(), diff --git a/apps/api/tests/routes/events.test.ts b/apps/api/tests/routes/events.test.ts index 89b7847..67d3c8a 100644 --- a/apps/api/tests/routes/events.test.ts +++ b/apps/api/tests/routes/events.test.ts @@ -289,10 +289,10 @@ describe('POST /api/events/create', () => { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - summary: 'New event', + title: 'New event', allDay: false, - dtstart: '2026-06-15T10:00:00Z', - dtend: '2026-06-15T11:00:00Z', + start: '2026-06-15T10:00:00Z', + end: '2026-06-15T11:00:00Z', calendarUrl: 'https://caldav.fastmail.com/dav/calendars/user/test@fm.com/Default/', }), }) @@ -324,10 +324,10 @@ describe('POST /api/events/create', () => { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - summary: 'Unauthorized write', + title: 'Unauthorized write', allDay: false, - dtstart: '2026-06-15T10:00:00Z', - dtend: '2026-06-15T11:00:00Z', + start: '2026-06-15T10:00:00Z', + end: '2026-06-15T11:00:00Z', calendarUrl: 'https://caldav.fastmail.com/dav/calendars/user/other@fm.com/Personal/', }), }) @@ -363,10 +363,10 @@ describe('PATCH /api/events/:uid/edit', () => { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ - summary: 'Updated title', + title: 'Updated title', allDay: false, - dtstart: '2026-06-15T10:00:00Z', - dtend: '2026-06-15T11:00:00Z', + start: '2026-06-15T10:00:00Z', + end: '2026-06-15T11:00:00Z', calendarUrl: 'https://caldav.fastmail.com/dav/calendars/user/test@fm.com/Default/', }), })