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:
@@ -15,14 +15,14 @@
|
||||
* Fire-and-forget: sync correctness does not depend on push success.
|
||||
*/
|
||||
|
||||
import { eq, ne } from 'drizzle-orm'
|
||||
import { db } from '../db/client.js'
|
||||
import { users, pushSubscriptions } from '../db/schema.js'
|
||||
import { dispatchPush } from './pushDispatcher.js'
|
||||
import { eq, ne } from 'drizzle-orm';
|
||||
import { db } from '../db/client.js';
|
||||
import { users, pushSubscriptions } from '../db/schema.js';
|
||||
import { dispatchPush } from './pushDispatcher.js';
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export type EventChangeOperation = 'create' | 'update' | 'delete'
|
||||
export type EventChangeOperation = 'create' | 'update' | 'delete';
|
||||
|
||||
/**
|
||||
* Meaningful fields whose change triggers a push notification (D-04).
|
||||
@@ -34,21 +34,21 @@ export const MEANINGFUL_FIELDS = new Set([
|
||||
'allDay',
|
||||
'title',
|
||||
'location',
|
||||
])
|
||||
]);
|
||||
|
||||
/**
|
||||
* Payload describing a detected calendar event change.
|
||||
* Produced by syncCalendar and consumed by poller + outboxWorker.
|
||||
*/
|
||||
export interface EventChange {
|
||||
uid: string
|
||||
title: string | null
|
||||
operation: EventChangeOperation
|
||||
uid: string;
|
||||
title: string | null;
|
||||
operation: EventChangeOperation;
|
||||
/** For 'update': which fields changed. Omit for 'create' and 'delete'. */
|
||||
changedFields?: string[]
|
||||
changedFields?: string[];
|
||||
/** UTC timestamp of the event start (for notification copy). */
|
||||
dtstartUtc?: Date | null
|
||||
allDay?: boolean
|
||||
dtstartUtc?: Date | null;
|
||||
allDay?: boolean;
|
||||
}
|
||||
|
||||
// ── Core logic ───────────────────────────────────────────────────────────────
|
||||
@@ -63,11 +63,11 @@ export interface EventChange {
|
||||
*/
|
||||
export function isMeaningfulChange(change: EventChange): boolean {
|
||||
if (change.operation === 'create' || change.operation === 'delete') {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
// update: require at least one meaningful field
|
||||
const fields = change.changedFields ?? []
|
||||
return fields.some((f) => MEANINGFUL_FIELDS.has(f))
|
||||
const fields = change.changedFields ?? [];
|
||||
return fields.some((f) => MEANINGFUL_FIELDS.has(f));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,32 +81,32 @@ function buildCopy(
|
||||
change: EventChange,
|
||||
actorName: string,
|
||||
): { notifTitle: string; notifBody: string; navigate: string } {
|
||||
const eventTitle = change.title ?? change.uid
|
||||
const eventTitle = change.title ?? change.uid;
|
||||
|
||||
let notifTitle: string
|
||||
let notifBody: string
|
||||
let notifTitle: string;
|
||||
let notifBody: string;
|
||||
|
||||
// D-02: event notifications show specifics — actor + title.
|
||||
// D-03: name the actor in every change notification.
|
||||
if (change.operation === 'create') {
|
||||
notifTitle = `${actorName} added an event`
|
||||
notifBody = eventTitle
|
||||
notifTitle = `${actorName} added an event`;
|
||||
notifBody = eventTitle;
|
||||
} else if (change.operation === 'delete') {
|
||||
notifTitle = `${actorName} removed an event`
|
||||
notifBody = eventTitle
|
||||
notifTitle = `${actorName} removed an event`;
|
||||
notifBody = eventTitle;
|
||||
} else {
|
||||
// update
|
||||
notifTitle = `${actorName} updated an event`
|
||||
notifBody = eventTitle
|
||||
notifTitle = `${actorName} updated an event`;
|
||||
notifBody = eventTitle;
|
||||
}
|
||||
|
||||
// Navigate: /calendar?event=uid for create/update; /calendar for delete
|
||||
const navigate =
|
||||
change.operation === 'delete'
|
||||
? '/calendar'
|
||||
: `/calendar?event=${encodeURIComponent(change.uid)}`
|
||||
: `/calendar?event=${encodeURIComponent(change.uid)}`;
|
||||
|
||||
return { notifTitle, notifBody, navigate }
|
||||
return { notifTitle, notifBody, navigate };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,13 +119,10 @@ function buildCopy(
|
||||
*
|
||||
* Fire-and-forget: resolves after dispatching without awaiting push ACKs.
|
||||
*/
|
||||
export async function dispatchEventChange(
|
||||
change: EventChange,
|
||||
actorUserId: number,
|
||||
): Promise<void> {
|
||||
export async function dispatchEventChange(change: EventChange, actorUserId: number): Promise<void> {
|
||||
// D-04: skip description-only edits
|
||||
if (!isMeaningfulChange(change)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// IN-01: resolve actor display name for D-02/D-03 notification copy.
|
||||
@@ -139,22 +136,19 @@ export async function dispatchEventChange(
|
||||
// D-13: query push_subscriptions from MariaDB only
|
||||
// D-03: ne() filter excludes the actor at DB level; application-level filter
|
||||
// below provides defence-in-depth (also makes the mock-based tests deterministic).
|
||||
db
|
||||
.select()
|
||||
.from(pushSubscriptions)
|
||||
.where(ne(pushSubscriptions.userId, actorUserId)),
|
||||
])
|
||||
db.select().from(pushSubscriptions).where(ne(pushSubscriptions.userId, actorUserId)),
|
||||
]);
|
||||
|
||||
const actorName: string = actorRows[0]?.displayName ?? 'A family member'
|
||||
const actorName: string = actorRows[0]?.displayName ?? 'A family member';
|
||||
|
||||
// D-03: additional application-level actor exclusion (defence-in-depth)
|
||||
const subs = allSubs.filter((s) => s.userId !== actorUserId)
|
||||
const subs = allSubs.filter((s) => s.userId !== actorUserId);
|
||||
|
||||
if (subs.length === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const { notifTitle, notifBody, navigate } = buildCopy(change, actorName)
|
||||
const { notifTitle, notifBody, navigate } = buildCopy(change, actorName);
|
||||
|
||||
// Fan out to all non-actor subscriptions (D-03 already enforced by ne() filter)
|
||||
for (const sub of subs) {
|
||||
@@ -164,12 +158,12 @@ export async function dispatchEventChange(
|
||||
body: notifBody,
|
||||
tag: `event-change-${change.uid}`,
|
||||
navigate,
|
||||
})
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[eventChangeDispatcher] Error dispatching for uid=${change.uid} sub.id=${sub.id}:`,
|
||||
err instanceof Error ? err.message : String(err),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user