export const INVALIDATION_TYPES = [ "directory.updated", "page.updated", "admin.updated", ] as const; export type InvalidationType = (typeof INVALIDATION_TYPES)[number]; export interface InvalidationEvent { type: InvalidationType; id: string; version: number; } export interface OutboxEventLike { aggregateId: string; eventType: string; payload: Record; topic: string; } function isInvalidationType(value: string): value is InvalidationType { return INVALIDATION_TYPES.some((type) => type === value); } export function expectedTopic(event: InvalidationEvent): string { switch (event.type) { case "directory.updated": return "directory"; case "page.updated": return `page:${event.id}`; case "admin.updated": return "admin"; } } export function toInvalidationEvent( record: OutboxEventLike, ): InvalidationEvent | null { if (!isInvalidationType(record.eventType)) return null; const id = record.payload.id; const version = record.payload.version; if ( typeof id !== "string" || id !== record.aggregateId || typeof version !== "number" || !Number.isSafeInteger(version) || version < 0 ) { return null; } const event: InvalidationEvent = { type: record.eventType, id, version, }; return expectedTopic(event) === record.topic ? event : null; } export function parseInvalidationEvent(value: string): InvalidationEvent | null { try { const parsed = JSON.parse(value) as Record; const type = parsed.type; const id = parsed.id; const version = parsed.version; if ( typeof type !== "string" || !isInvalidationType(type) || typeof id !== "string" || typeof version !== "number" || !Number.isSafeInteger(version) || version < 0 ) { return null; } return { type, id, version }; } catch { return null; } }