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
+31 -31
View File
@@ -33,11 +33,11 @@
* T-05-19 — per-subscription try/catch; dispatchPush already swallows 410/404.
*/
import { and, eq, gt, lte, sql } from 'drizzle-orm'
import { db } from '../db/client.js'
import { calendars, calendarEvents, pushSubscriptions } from '../db/schema.js'
import { dispatchPush } from '../lib/pushDispatcher.js'
import type { NotificationPayload } from '../lib/pushDispatcher.js'
import { and, eq, gt, lte, sql } from 'drizzle-orm';
import { db } from '../db/client.js';
import { calendars, calendarEvents, pushSubscriptions } from '../db/schema.js';
import { dispatchPush } from '../lib/pushDispatcher.js';
import type { NotificationPayload } from '../lib/pushDispatcher.js';
// ── In-memory dedup (D-12: single-process, no Redis) ─────────────────────────
// Key: event uid (bare string — no minuteBucket suffix).
@@ -45,7 +45,7 @@ import type { NotificationPayload } from '../lib/pushDispatcher.js'
// Prevents double-fire when the same event sits in the catch-up window across
// multiple consecutive ticks (cross-tick exactly-once guarantee).
// Acceptable data loss on process restart for a two-person household.
const sentReminders = new Map<string, number>()
const sentReminders = new Map<string, number>();
// ── Helpers ───────────────────────────────────────────────────────────────────
@@ -53,10 +53,10 @@ const sentReminders = new Map<string, number>()
* Format a UTC Date as "YYYY-MM-DD" for calendar deep-link URLs.
*/
function yyyyMmDd(d: Date): string {
const y = d.getUTCFullYear()
const m = String(d.getUTCMonth() + 1).padStart(2, '0')
const day = String(d.getUTCDate()).padStart(2, '0')
return `${y}-${m}-${day}`
const y = d.getUTCFullYear();
const m = String(d.getUTCMonth() + 1).padStart(2, '0');
const day = String(d.getUTCDate()).padStart(2, '0');
return `${y}-${m}-${day}`;
}
// ── Core scan ─────────────────────────────────────────────────────────────────
@@ -69,7 +69,7 @@ function yyyyMmDd(d: Date): string {
* default = new Date() (current wall-clock time).
*/
export async function runReminderCheck(now = new Date()): Promise<void> {
const windowEnd = new Date(now.getTime() + 16 * 60 * 1000)
const windowEnd = new Date(now.getTime() + 16 * 60 * 1000);
// Single query: events (isShared, timed, in catch-up window) cross-joined with ALL
// push_subscriptions. The cross-join (sql`1=1`) fans every matching event out
@@ -99,21 +99,21 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
.innerJoin(pushSubscriptions, sql`1=1`)
.where(
and(
eq(calendars.isShared, true), // D-05: shared calendar only (in QUERY, not copy)
eq(calendarEvents.allDay, false), // D-07: timed events only
gt(calendarEvents.dtstartUtc, now), // strictly future (excludes already-started)
eq(calendars.isShared, true), // D-05: shared calendar only (in QUERY, not copy)
eq(calendarEvents.allDay, false), // D-07: timed events only
gt(calendarEvents.dtstartUtc, now), // strictly future (excludes already-started)
lte(calendarEvents.dtstartUtc, windowEnd),
),
)
);
// Group flat (event, subscription) rows by event uid so we can:
// a) dedup per event (not per (event, sub) pair), and
// b) fan out to ALL subscriptions for a deduped event in one pass.
type SubRow = { id: number; userId: number; endpoint: string; p256dh: string; auth: string }
type SubRow = { id: number; userId: number; endpoint: string; p256dh: string; auth: string };
const byUid = new Map<
string,
{ uid: string; title: string | null; dtstartUtc: Date; subs: SubRow[] }
>()
>();
for (const row of rows) {
if (!byUid.has(row.uid)) {
@@ -123,7 +123,7 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
// dtstartUtc is guaranteed non-null by the allDay=false + gt/lte WHERE
dtstartUtc: row.dtstartUtc as Date,
subs: [],
})
});
}
// subId is undefined when cross-join produces no subscriptions row (empty table)
if (row.subId != null) {
@@ -133,7 +133,7 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
endpoint: row.subEndpoint,
p256dh: row.subP256dh,
auth: row.subAuth,
})
});
}
}
@@ -143,12 +143,12 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
// Per-uid exactly-once dedup (D-12): keyed on bare uid, no minuteBucket.
// Same event fires at most once regardless of how many ticks it sits in window.
// CAVEAT: rescheduling dtstart earlier after a reminder fired will not re-fire (v1).
if (sentReminders.has(uid)) continue
if (sentReminders.has(uid)) continue;
const dateStr = yyyyMmDd(event.dtstartUtc)
const dateStr = yyyyMmDd(event.dtstartUtc);
// Lead-accurate body: compute actual minutes to start, guarded to minimum 1.
const minutes = Math.max(1, Math.round((event.dtstartUtc.getTime() - now.getTime()) / 60000))
const minutes = Math.max(1, Math.round((event.dtstartUtc.getTime() - now.getTime()) / 60000));
const notification: NotificationPayload = {
// Null-safe title fallback (D-02 / NOTIF-01): title column may be NULL on rows
@@ -157,12 +157,12 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
body: `Starts in ${minutes} min`,
tag: `reminder-${uid}`,
navigate: `/calendar?date=${dateStr}&event=${uid}`,
}
};
// Fan out to every subscriber (member-count-agnostic)
for (const sub of event.subs) {
try {
await dispatchPush(sub, notification)
await dispatchPush(sub, notification);
} catch (err) {
// Per-subscription error isolation (T-05-19): one bad sub never aborts the cycle.
// dispatchPush itself never throws (it resolves after logging), so this outer
@@ -170,20 +170,20 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
console.error(
`[broker/reminderScheduler] Error dispatching reminder to sub id=${sub.id} for event uid=${uid}:`,
err instanceof Error ? err.message : String(err),
)
);
}
}
// WR-01: mark sent AFTER all dispatches have been attempted. Pre-marking before
// dispatch prevents retry when dispatchPush throws — at-least-once delivery
// requires not pre-marking the uid.
sentReminders.set(uid, event.dtstartUtc.getTime())
sentReminders.set(uid, event.dtstartUtc.getTime());
} catch (err) {
// Per-event error isolation (T-05-18): one bad event never aborts remaining events.
console.error(
`[broker/reminderScheduler] Error processing event uid=${uid}:`,
err instanceof Error ? err.message : String(err),
)
);
}
}
@@ -192,7 +192,7 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
// it can be removed safely (it will never re-enter the (now, now+16min] window).
for (const [uid, dtstartMs] of sentReminders) {
if (dtstartMs <= now.getTime()) {
sentReminders.delete(uid)
sentReminders.delete(uid);
}
}
}
@@ -209,7 +209,7 @@ export async function runReminderCheck(now = new Date()): Promise<void> {
export function startReminderScheduler(): void {
setInterval(() => {
runReminderCheck().catch((err: unknown) => {
console.error('[broker/reminderScheduler] Unhandled runReminderCheck error:', err)
})
}, 60 * 1000)
console.error('[broker/reminderScheduler] Unhandled runReminderCheck error:', err);
});
}, 60 * 1000);
}