import { type SseTicketStore } from '@pipeline-builder/api-core'; import type { Express, RequestHandler } from 'express'; import type { SSEManager } from './sse-connection-manager.js'; export interface SseTicketChannelOptions { /** POST route that exchanges the JWT for a single-use ticket (e.g. '/messages/notifications/ticket'). */ ticketPath: string; /** GET route that redeems the ticket and opens the EventSource (e.g. '/messages/notifications'). */ streamPath: string; /** Org-scoped, single-use ticket store (Redis-backed for multi-replica). */ ticketStore: SseTicketStore; /** SSE manager — the org id is the stream subject (`sseManager.send(orgId, …)`). */ sseManager: SSEManager; /** Noun used in capacity/connection error messages (e.g. 'notification', 'execution-stream'). */ label: string; /** Extra middleware applied AFTER requireAuth on the mint route (e.g. requirePermission('reports:read')). */ ticketGuards?: RequestHandler[]; } /** * Register a per-ORG SSE channel: a POST that exchanges the caller's VERIFIED JWT * org for a single-use, org-bound ticket, and a GET that redeems the ticket and * attaches an EventSource keyed by that org. The org IS the stream subject, so a * producer reaches subscribers with `sseManager.send(orgId, …)`. Registering a * channel turns on the manager's cross-pod relay (this service's own relay * channel), so a frame produced on one replica reaches a subscriber on another. * Shared by the message-notification and reporting execution-status * channels so the subtle bits — the ticket→addClient→flushHeaders ordering (a 429 * MUST precede flushHeaders, which commits the 200) and the capacity semantics — * live in exactly one place. * * Distinct from app-factory's `/logs` channel, which is SUBJECT-bound (per build, * via SSEManager.createTicket) rather than org-bound. */ export declare function registerSseTicketChannel(app: Express, opts: SseTicketChannelOptions): void;