import { subscribePreviewUpdates } from './event-bus.js';
import { encodeSseEvent } from '../realtime/sse.js';
export { encodeSseEvent };
/**
 * SSE wire format helpers. We keep them small and pure so the route handler
 * can build a `ReadableStream<Uint8Array>` without pulling in any extra
 * dependencies (this also keeps the file edge-compatible).
 *
 * The encoder itself lives in `../realtime/sse.ts` — a single source of
 * truth across every SSE channel in cms-core (preview + notifications,
 * today). Re-exported here so existing callers (and tests) that import
 * it from this module keep working.
 */
const encoder = new TextEncoder();
/**
 * Default keepalive interval. 25 s sits safely under the most aggressive
 * proxy/idle timeouts we've encountered in production (Cloudflare 30 s,
 * Vercel Serverless 30 s, most reverse-proxies 60 s).
 */
export const PREVIEW_SSE_HEARTBEAT_MS = 25_000;
/**
 * Maximum stream lifetime in ms. After this, the server closes the stream
 * cleanly so EventSource will reconnect — important because some platforms
 * (Vercel Fluid Compute, Cloudflare Workers) cap any single response at a
 * few minutes regardless of activity.
 */
export const PREVIEW_SSE_MAX_LIFETIME_MS = 4 * 60 * 1000;
/**
 * Build a `ReadableStream` of Server-Sent Events for the given preview
 * session. The stream:
 *
 *   1. Emits an `event: ready` frame with the session's collection/id.
 *      Lets the client confirm the server-side subscription is wired up
 *      before it starts treating the absence of events as "stale".
 *   2. Forwards every `notifyPreviewUpdate(collection, id, payload)` to
 *      the client as `event: update` with `payload` as JSON data.
 *   3. Sends `: heartbeat\n\n` keepalive comments on
 *      `PREVIEW_SSE_HEARTBEAT_MS` intervals.
 *   4. Cleanly closes after `PREVIEW_SSE_MAX_LIFETIME_MS` and lets the
 *      browser reconnect, sidestepping platform idle/max-duration caps.
 *
 * The returned stream MUST be wrapped in a `Response` with the SSE
 * headers — `createPreviewStreamResponse` does this for typical callers.
 */
export function createPreviewStream(session, opts = {}) {
    const heartbeatMs = opts.heartbeatMs ?? PREVIEW_SSE_HEARTBEAT_MS;
    const maxLifetimeMs = opts.maxLifetimeMs ?? PREVIEW_SSE_MAX_LIFETIME_MS;
    let heartbeat;
    let lifetime;
    let unsubscribe;
    return new ReadableStream({
        start(controller) {
            const enqueue = (chunk) => {
                try {
                    controller.enqueue(encoder.encode(chunk));
                }
                catch {
                    // Controller can be closed mid-emit if the client disconnected
                    // between the `notifyPreviewUpdate` call and the enqueue. Swallow.
                }
            };
            enqueue(encodeSseEvent({ collection: session.collection, documentId: session.documentId }, 'ready'));
            unsubscribe = subscribePreviewUpdates(session.collection, session.documentId, (event) => {
                enqueue(encodeSseEvent({
                    collection: event.collection,
                    documentId: event.documentId,
                    at: event.at,
                    ...event.payload,
                }, 'update'));
            });
            heartbeat = setInterval(() => {
                enqueue(`: heartbeat\n\n`);
            }, heartbeatMs);
            lifetime = setTimeout(() => {
                try {
                    controller.close();
                }
                catch {
                    // Already closed; nothing to do.
                }
            }, maxLifetimeMs);
        },
        cancel() {
            if (heartbeat)
                clearInterval(heartbeat);
            if (lifetime)
                clearTimeout(lifetime);
            if (unsubscribe)
                unsubscribe();
        },
    });
}
/**
 * Standard SSE response headers. Exposed for callers (and tests) that want
 * to wrap the stream themselves.
 */
export const PREVIEW_SSE_HEADERS = {
    'Content-Type': 'text/event-stream; charset=utf-8',
    'Cache-Control': 'no-cache, no-transform',
    Connection: 'keep-alive',
    // Discourage proxies/CDNs from buffering — without this nginx will hold
    // events back until 4 KB of payload accumulates.
    'X-Accel-Buffering': 'no',
};
export function createPreviewStreamResponse(session, opts) {
    return new Response(createPreviewStream(session, opts), {
        status: 200,
        headers: PREVIEW_SSE_HEADERS,
    });
}
//# sourceMappingURL=sse-stream.js.map