feat(11-03): sync.ts derives reminderLeadMinutes from VALARM classification (D-07/NOTIF-05)
- Import classifyValarms from vevent.ts
- Derive reminderLeadMinutesValue: preset/offlist → leadMinutes; custom/none → null
- Add reminderLeadMinutes to .values() and .onDuplicateKeyUpdate({ set: {} })
- Scheduler now has ground truth for native-client VALARMs (T-11-07 mitigated)
This commit is contained in:
@@ -22,6 +22,7 @@ import { and, eq, notInArray } from 'drizzle-orm';
|
|||||||
import { db } from '../db/client.js';
|
import { db } from '../db/client.js';
|
||||||
import { calendars, calendarEvents } from '../db/schema.js';
|
import { calendars, calendarEvents } from '../db/schema.js';
|
||||||
import type { EventChange } from '../lib/eventChangeDispatcher.js';
|
import type { EventChange } from '../lib/eventChangeDispatcher.js';
|
||||||
|
import { classifyValarms } from './vevent.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches all calendar objects for a given DAVCalendar, parses VEVENTs with ical.js,
|
* Fetches all calendar objects for a given DAVCalendar, parses VEVENTs with ical.js,
|
||||||
@@ -128,6 +129,17 @@ export async function syncCalendar(
|
|||||||
const locationValue: string | null =
|
const locationValue: string | null =
|
||||||
(vevent.getFirstPropertyValue('location') as string | null) ?? null;
|
(vevent.getFirstPropertyValue('location') as string | null) ?? null;
|
||||||
|
|
||||||
|
// Phase 11: derive reminderLeadMinutes from the VALARM classification (D-07, NOTIF-05).
|
||||||
|
// classifyValarms wraps ICAL.parse in try/catch (T-11-07 mitigated).
|
||||||
|
// preset/offlist → single relative lead in minutes (scheduler ground truth).
|
||||||
|
// custom (absolute DATE-TIME or multiple VALARMs) → null (can't resolve a single lead).
|
||||||
|
// none → null (no VALARM present).
|
||||||
|
const alarmClass = classifyValarms(obj.data as string);
|
||||||
|
const reminderLeadMinutesValue: number | null =
|
||||||
|
alarmClass.kind === 'preset' || alarmClass.kind === 'offlist'
|
||||||
|
? alarmClass.leadMinutes
|
||||||
|
: null;
|
||||||
|
|
||||||
// NOTIF-03: look up the existing row so we can classify add vs update.
|
// NOTIF-03: look up the existing row so we can classify add vs update.
|
||||||
// One indexed lookup on (calendarId, uid) — cheap, covered by uniq_calendar_uid.
|
// One indexed lookup on (calendarId, uid) — cheap, covered by uniq_calendar_uid.
|
||||||
// D-13: this read is from MariaDB cache, not Fastmail.
|
// D-13: this read is from MariaDB cache, not Fastmail.
|
||||||
@@ -154,6 +166,8 @@ export async function syncCalendar(
|
|||||||
dtstartDate: dtstartDateValue,
|
dtstartDate: dtstartDateValue,
|
||||||
allDay,
|
allDay,
|
||||||
hasRrule: isRecurring,
|
hasRrule: isRecurring,
|
||||||
|
// Phase 11: ground-truth VALARM lead for the scheduler (D-07/NOTIF-05, T-11-07)
|
||||||
|
reminderLeadMinutes: reminderLeadMinutesValue,
|
||||||
})
|
})
|
||||||
.onDuplicateKeyUpdate({
|
.onDuplicateKeyUpdate({
|
||||||
set: {
|
set: {
|
||||||
@@ -165,6 +179,8 @@ export async function syncCalendar(
|
|||||||
dtstartDate: dtstartDateValue,
|
dtstartDate: dtstartDateValue,
|
||||||
allDay,
|
allDay,
|
||||||
hasRrule: isRecurring,
|
hasRrule: isRecurring,
|
||||||
|
// Phase 11: keep reminderLeadMinutes current on re-sync (native client may change VALARM)
|
||||||
|
reminderLeadMinutes: reminderLeadMinutesValue,
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user