import type { AuthLogger } from '../../types.js'; import type { NotificationPreferenceRepository, NotificationRecord, NotificationRepository, PushSubscriptionRepository } from '../adapters/types.js'; import type { PushResult, PushService } from './push.js'; import type { NotificationRegistry } from './registry.js'; import type { SSEManager } from './sse.js'; export interface NotificationServiceDeps { registry: NotificationRegistry; sse: SSEManager; push?: PushService; repos: { notification: NotificationRepository; pushSubscription?: PushSubscriptionRepository; notificationPreference?: NotificationPreferenceRepository; }; /** * Resolves the user IDs that receive notification types declared with * `recipients: 'admins'`. The package has no notion of roles itself, so the * consumer must wire this (e.g. `() => repo.findAdminUserIds()`). * * If a type uses `recipients: 'admins'` and this resolver is **not** * provided, `send()` throws — silently delivering admin alerts (suspicious * login, new passkey, …) to nobody would drop security-relevant signals with * no indication anything is wrong. For data-dependent recipients use the * function form of `recipients` instead. */ resolveAdminRecipients?: () => string[] | Promise; /** * Optional observability hook called with the raw push-delivery results for * each recipient. The service swallows push failures (a single bad * subscription must not break a send), so without this hook a consumer has no * way to see crypto errors, rate-limited endpoints, or 4xx/5xx. Invoked * defensively — a throwing hook never breaks delivery. */ onPushResult?: (userId: string, results: PushResult[]) => void; /** * Sink for per-recipient delivery failures (see {@link NotificationService.send}). * Defaults to `console`; calls are shielded, so a throwing sink cannot break * delivery — same contract as `AuthConfig.logger`. */ logger?: AuthLogger; } export interface NotificationService { /** * Resolve the type's recipients, then persist + deliver to each of them. * * **Failure semantics:** configuration errors (unknown type, unwired * `resolveAdminRecipients`, the legacy `'all'` target) throw before any * delivery. Once delivery starts it is **best-effort per recipient**: a * failure for one recipient (DB blip on the insert, preference read, …) is * reported to `deps.logger` and delivery continues with the remaining * recipients — `send()` resolves. Push-transport failures are additionally * observable per recipient via `onPushResult`. */ send(typeKey: string, data: Record): Promise; getForUser(userId: string, options?: { limit?: number; unreadOnly?: boolean; }): Promise; /** Scoped to the owner; owner-first parameter order (see adapters/types.ts). */ markAsRead(userId: string, id: string): Promise; markAllAsRead(userId: string): Promise; getUnreadCount(userId: string): Promise; /** Scoped to the owner; owner-first parameter order (see adapters/types.ts). */ deleteNotification(userId: string, id: string): Promise; } export declare function createNotificationService(deps: NotificationServiceDeps): NotificationService;