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
+61 -57
View File
@@ -15,21 +15,21 @@
* - https://github.com/kewisch/ical.js/blob/main/lib/ical/time.js
*/
import ICAL from 'ical.js'
import { randomUUID } from 'crypto'
import ICAL from 'ical.js';
import { randomUUID } from 'crypto';
export interface NewEventParams {
uid?: string // omit = generate new UUID (appended with @familysync)
summary: string
allDay: boolean
uid?: string; // omit = generate new UUID (appended with @familysync)
summary: string;
allDay: boolean;
// All-day: YYYY-MM-DD string (or Date — only the date portion is used)
// Timed: JS Date representing a UTC instant
dtstart: string | Date
dtend: string | Date
location?: string
description?: string
rruleString?: string // e.g. 'FREQ=WEEKLY;BYDAY=MO' — omit for non-recurring (CAL-07)
dtstamp?: Date // omit = now()
dtstart: string | Date;
dtend: string | Date;
location?: string;
description?: string;
rruleString?: string; // e.g. 'FREQ=WEEKLY;BYDAY=MO' — omit for non-recurring (CAL-07)
dtstamp?: Date; // omit = now()
}
/**
@@ -51,7 +51,7 @@ export const RRULE_PRESETS: Record<string, string> = {
weekly: 'FREQ=WEEKLY',
monthly: 'FREQ=MONTHLY',
yearly: 'FREQ=YEARLY',
}
};
/**
* WR-01: Extract the existing RRULE string from a stored VCALENDAR/VEVENT, so the
@@ -61,21 +61,21 @@ export const RRULE_PRESETS: Record<string, string> = {
* the input cannot be parsed.
*/
export function extractRruleString(rawVevent: string): string | undefined {
let parsed: ReturnType<typeof ICAL.parse>
let parsed: ReturnType<typeof ICAL.parse>;
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- ical.js parse() returns 'any'; result is only passed to ICAL.Component which accepts it
parsed = ICAL.parse(rawVevent)
parsed = ICAL.parse(rawVevent);
} catch {
return undefined
return undefined;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- ical.js parse() output is 'any'; ICAL.Component is the correct consumer of this value
const comp = new ICAL.Component(parsed)
const vevent = comp.getFirstSubcomponent('vevent')
if (!vevent) return undefined
const rrule = vevent.getFirstPropertyValue('rrule')
if (!rrule) return undefined
const comp = new ICAL.Component(parsed);
const vevent = comp.getFirstSubcomponent('vevent');
if (!vevent) return undefined;
const rrule = vevent.getFirstPropertyValue('rrule');
if (!rrule) return undefined;
// ICAL.Recur#toString() yields the RECUR value, e.g. 'FREQ=WEEKLY'.
return typeof rrule === 'string' ? rrule : (rrule as ICAL.Recur).toString()
return typeof rrule === 'string' ? rrule : (rrule as ICAL.Recur).toString();
}
/**
@@ -85,57 +85,61 @@ export function extractRruleString(rawVevent: string): string | undefined {
* icsString is the full VCALENDAR string ready for PUT to Fastmail.
*/
export function buildVeventString(params: NewEventParams): { uid: string; icsString: string } {
const uid = params.uid ?? `${randomUUID()}@familysync`
const uid = params.uid ?? `${randomUUID()}@familysync`;
// --- VCALENDAR wrapper ---
const cal = new ICAL.Component(['vcalendar', [], []])
cal.updatePropertyWithValue('version', '2.0')
cal.updatePropertyWithValue('prodid', '-//FamilySync//FamilySync//EN')
const cal = new ICAL.Component(['vcalendar', [], []]);
cal.updatePropertyWithValue('version', '2.0');
cal.updatePropertyWithValue('prodid', '-//FamilySync//FamilySync//EN');
// --- VEVENT ---
const vevent = new ICAL.Component('vevent')
vevent.addPropertyWithValue('uid', uid)
vevent.addPropertyWithValue('summary', params.summary)
const vevent = new ICAL.Component('vevent');
vevent.addPropertyWithValue('uid', uid);
vevent.addPropertyWithValue('summary', params.summary);
// DTSTAMP: always present (RFC 5545 §3.8.7.2 — required property)
const dtstamp = ICAL.Time.fromJSDate(params.dtstamp ?? new Date(), true)
vevent.addPropertyWithValue('dtstamp', dtstamp)
const dtstamp = ICAL.Time.fromJSDate(params.dtstamp ?? new Date(), true);
vevent.addPropertyWithValue('dtstamp', dtstamp);
if (params.allDay) {
// DATE value (not DATETIME) — isDate:true produces VALUE=DATE, no time component (D-13 contract)
const startStr =
typeof params.dtstart === 'string'
? params.dtstart
: params.dtstart.toISOString().slice(0, 10)
: params.dtstart.toISOString().slice(0, 10);
const endStr =
typeof params.dtend === 'string'
? params.dtend
: params.dtend.toISOString().slice(0, 10)
typeof params.dtend === 'string' ? params.dtend : params.dtend.toISOString().slice(0, 10);
const [sy, sm, sd] = startStr.split('-').map(Number) as [number, number, number]
const [ey, em, ed] = endStr.split('-').map(Number) as [number, number, number]
const [sy, sm, sd] = startStr.split('-').map(Number) as [number, number, number];
const [ey, em, ed] = endStr.split('-').map(Number) as [number, number, number];
// WR-04 (owning boundary): RFC-5545 §3.6.1 — DTEND for an all-day event is the
// EXCLUSIVE end date. Advance the user-entered inclusive end by one calendar day.
// Building a Date from UTC components ensures no DST ambiguity during the roll-over.
const endDate = new Date(Date.UTC(ey, em - 1, ed))
endDate.setUTCDate(endDate.getUTCDate() + 1)
const ey2 = endDate.getUTCFullYear()
const em2 = endDate.getUTCMonth() + 1
const ed2 = endDate.getUTCDate()
const endDate = new Date(Date.UTC(ey, em - 1, ed));
endDate.setUTCDate(endDate.getUTCDate() + 1);
const ey2 = endDate.getUTCFullYear();
const em2 = endDate.getUTCMonth() + 1;
const ed2 = endDate.getUTCDate();
// ICAL.Timezone.localTimezone is passed as the zone arg required by TS types.
// isDate:true suppresses any time/TZID output regardless of zone. (D-13)
const startTime = new ICAL.Time({ year: sy, month: sm, day: sd, isDate: true }, ICAL.Timezone.localTimezone)
const endTime = new ICAL.Time({ year: ey2, month: em2, day: ed2, isDate: true }, ICAL.Timezone.localTimezone)
vevent.addPropertyWithValue('dtstart', startTime)
vevent.addPropertyWithValue('dtend', endTime)
const startTime = new ICAL.Time(
{ year: sy, month: sm, day: sd, isDate: true },
ICAL.Timezone.localTimezone,
);
const endTime = new ICAL.Time(
{ year: ey2, month: em2, day: ed2, isDate: true },
ICAL.Timezone.localTimezone,
);
vevent.addPropertyWithValue('dtstart', startTime);
vevent.addPropertyWithValue('dtend', endTime);
} else {
// DATETIME in UTC (useUTC=true → Z suffix; TZID is NOT added by ical.js) (D-13 contract)
const startTime = ICAL.Time.fromJSDate(params.dtstart as Date, true)
const endTime = ICAL.Time.fromJSDate(params.dtend as Date, true)
vevent.addPropertyWithValue('dtstart', startTime)
vevent.addPropertyWithValue('dtend', endTime)
const startTime = ICAL.Time.fromJSDate(params.dtstart as Date, true);
const endTime = ICAL.Time.fromJSDate(params.dtend as Date, true);
vevent.addPropertyWithValue('dtstart', startTime);
vevent.addPropertyWithValue('dtend', endTime);
}
// Optional: RRULE (CAL-07 — whole-series recurring events only in v1, D-11)
@@ -143,21 +147,21 @@ export function buildVeventString(params: NewEventParams): { uid: string; icsStr
// addPropertyWithValue('rrule', string) serializes the string character-by-character
// instead of as a RECUR value type.
if (params.rruleString) {
const recur = ICAL.Recur.fromString(params.rruleString)
const rruleProp = new ICAL.Property('rrule')
rruleProp.setValue(recur)
vevent.addProperty(rruleProp)
const recur = ICAL.Recur.fromString(params.rruleString);
const rruleProp = new ICAL.Property('rrule');
rruleProp.setValue(recur);
vevent.addProperty(rruleProp);
}
// Optional fields — included only when provided (no empty properties in ICS)
if (params.location) {
vevent.addPropertyWithValue('location', params.location)
vevent.addPropertyWithValue('location', params.location);
}
if (params.description) {
vevent.addPropertyWithValue('description', params.description)
vevent.addPropertyWithValue('description', params.description);
}
cal.addSubcomponent(vevent)
cal.addSubcomponent(vevent);
return { uid, icsString: cal.toString() }
return { uid, icsString: cal.toString() };
}