/** * Loading a transport's `send` from the module that provides it. * * A notification transport is an ordinary module providing the `notification` * capability, so reaching it goes through the same capability-loader every * other cross-module call uses. Nothing here knows what Signal is. * * A transport that cannot be loaded throws at send time rather than returning * a no-op. Silently swallowing it would mean the sweep records "notified" * for a page that never left the building — the one outcome an alerting * system must never produce. */ import type { NotificationCapability } from '@celilo/capabilities'; import type { DbClient } from '../../db/client'; import { loadCapabilityFunctions } from '../../hooks/capability-loader'; import { createCapturingLogger } from '../../hooks/logger'; import type { NotificationTransport } from './notifier'; /** * A transport bound to one module. * * Resolution is deferred to the first `send` because a sweep may evaluate many * alerts and page for none of them — loading a module's capability functions * for every alert would be work done to reach nobody. */ export function loadNotificationTransport( db: DbClient, transportModuleId: string, ): NotificationTransport { return { async send(request) { const { logger } = createCapturingLogger(); const capabilities = await loadCapabilityFunctions(transportModuleId, db, logger); const notification = (capabilities as Record).notification as | NotificationCapability | undefined; if (!notification) { throw new Error( `Module "${transportModuleId}" does not provide the notification capability. Deploy it, or point the route at a module that does.`, ); } return notification.send(request); }, }; }