/** Outbound port: tell the user how a command went (implemented with toasts by the app). */ export abstract class Notifications { abstract success(message: string): void; abstract error(message: string): void; /** Done, offering to undo it for a few seconds: `undo` runs if asked. */ abstract undoable(message: string, undo: () => void): void; } /** Message of a failed GraphQL/HTTP call, for the user. */ export function describeError(error: unknown): string { if ( error && typeof error === 'object' && 'errors' in error && Array.isArray((error as { errors: unknown[] }).errors) ) { return (error as { errors: { message: string }[] }).errors .map((e) => e.message) .join('\n'); } return error instanceof Error ? error.message : String(error); } /** The read model is eventually consistent: right after a command, a query may 404 for a few ms. */ export function isNotFound(error: unknown): boolean { return /not found/i.test(describeError(error)); }