@runlane/realtime 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":["RunlaneError","ErrorCode","encoder","RunlaneError","ErrorCode","z","RunlaneError","ErrorCode","RunSubscriptionEventType"],"sources":["../src/connection-observer.ts","../src/sse.ts","../src/wire.ts","../src/client.ts"],"sourcesContent":["import {\n ErrorCode,\n RealtimeConnectionCloseReason,\n RealtimeConnectionState,\n RunlaneError,\n type RealtimeConnectionChange,\n} from '@runlane/contracts/realtime'\nimport { getDownwardJitteredDelayMs } from '@runlane/utilities/browser'\n\n/** Owns notification ordering and preserves the first callback failure without recursive reporting. */\nexport class ConnectionObserver {\n failed = false\n\n constructor(private readonly callback: ((change: RealtimeConnectionChange) => void) | undefined) {}\n\n emit(change: RealtimeConnectionChange): void {\n try {\n this.callback?.(change)\n } catch (cause) {\n // Once the observer throws, calling it again could replace the original failure.\n this.failed = true\n throw cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Realtime connection observer failed.',\n cause,\n })\n }\n }\n\n reconnect(signal: AbortSignal, attempt: number, ceilingMs: number): number | undefined {\n if (signal.aborted) return undefined\n const delayMs = getDownwardJitteredDelayMs(ceilingMs)\n this.emit({ state: RealtimeConnectionState.Reconnecting, attempt, delayMs })\n return delayMs\n }\n\n close(signal: AbortSignal, failure: { readonly cause: RunlaneError } | undefined): void {\n if (this.failed) return\n if (failure !== undefined) {\n this.emit({\n state: RealtimeConnectionState.Closed,\n reason: RealtimeConnectionCloseReason.Failed,\n error: failure.cause,\n })\n return\n }\n const reason = signal.aborted ? RealtimeConnectionCloseReason.Aborted : RealtimeConnectionCloseReason.Completed\n this.emit({ state: RealtimeConnectionState.Closed, reason })\n }\n}\n","import { ErrorCode, realtimeDefaults, RunlaneError } from '@runlane/contracts/realtime'\nimport { createParser } from 'eventsource-parser'\n\nimport { wireInvalid } from './errors.js'\n\nconst encoder = new TextEncoder()\n\n/** One parsed SSE event; id retains the preceding checkpoint when omitted by the sender. */\nexport interface SseEvent {\n readonly event: string\n readonly id: string\n readonly data: string\n}\n\n/** Incremental UTF-8 SSE parser; incomplete trailing events are never dispatched. */\nexport async function* readSse(body: ReadableStream<Uint8Array>, signal: AbortSignal): AsyncGenerator<SseEvent, void> {\n const reader = body.getReader()\n const decoder = new TextDecoder('utf-8', { fatal: true })\n let lastId = ''\n const ready: { frame?: SseEvent } = {}\n const budget = new FrameByteBudget()\n const parser = createParser({\n maxBufferSize: realtimeDefaults.maxEventBytes,\n onId(id) {\n lastId = id\n },\n onEvent(message) {\n ready.frame = { event: message.event ?? 'message', id: lastId, data: message.data }\n },\n onError(error) {\n if (error.type === 'max-buffer-size-exceeded') throw wireInvalid(error)\n },\n })\n const abort = () => {\n void reader.cancel().catch(() => undefined)\n }\n signal.addEventListener('abort', abort, { once: true })\n if (signal.aborted) abort()\n try {\n while (!signal.aborted) {\n const result = await readBytes(reader)\n if (result.done) {\n decodeUtf8(decoder)\n return\n }\n for (const char of decodeUtf8(decoder, result.value)) {\n budget.accept(char)\n parser.feed(char)\n if (ready.frame !== undefined) {\n const frame = ready.frame\n delete ready.frame\n yield frame\n }\n }\n }\n } finally {\n signal.removeEventListener('abort', abort)\n // Cancellation may race a disconnected socket; it cannot change the already delivered result.\n await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n}\n\n// Parser buffers count selected characters; this guard counts every raw UTF-8 field and comment.\nclass FrameByteBudget {\n private retained = 0\n private lineEmpty = true\n private skipLf = false\n private resetAfterCr = false\n\n accept(char: string): void {\n if (this.resetAfterCr && char !== '\\n') {\n this.retained = 0\n this.resetAfterCr = false\n }\n const point = char.codePointAt(0) ?? 0\n this.retained += point < 128 ? 1 : encoder.encode(char).byteLength\n if (this.retained > realtimeDefaults.maxEventBytes) throw wireInvalid()\n if (this.skipLf && char === '\\n') {\n this.skipLf = false\n if (this.resetAfterCr) {\n this.retained = 0\n this.resetAfterCr = false\n }\n return\n }\n this.skipLf = char === '\\r'\n if (char !== '\\r' && char !== '\\n') {\n this.lineEmpty = false\n return\n }\n if (this.lineEmpty && char === '\\n') this.retained = 0\n this.resetAfterCr = this.lineEmpty && char === '\\r'\n this.lineEmpty = true\n }\n}\n\nasync function readBytes(reader: ReadableStreamDefaultReader<Uint8Array>) {\n try {\n return await reader.read()\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime connection was interrupted.',\n retryable: true,\n cause,\n })\n }\n}\n\nfunction decodeUtf8(decoder: TextDecoder, bytes?: Uint8Array): string {\n try {\n return bytes === undefined ? decoder.decode() : decoder.decode(bytes, { stream: true })\n } catch (cause) {\n throw wireInvalid(cause)\n }\n}\n","import { realtimeDefaults } from '@runlane/contracts/realtime'\n\nimport { wireInvalid } from './errors.js'\n\nconst encoder = new TextEncoder()\n\n/** Parses JSON within the serialized UTF-8 event bound. Storage owns application value validation. */\nexport function parseWireJson(text: string): unknown {\n try {\n if (encoder.encode(text).byteLength > realtimeDefaults.maxEventBytes) throw wireInvalid()\n return JSON.parse(text)\n } catch (cause) {\n throw wireInvalid(cause)\n }\n}\n","import {\n createRunlaneIdSchema,\n ErrorCode,\n RealtimeConnectionState,\n realtimeDefaults,\n recordRunlaneTelemetryMetric,\n runCursorSchema,\n RunlaneError,\n RunlaneMetricAttribute,\n RunlaneMetricName,\n RunReaderKind,\n runRealtimeControlSchema,\n runRealtimeErrorSchema,\n runStatusValues,\n runStreamNameSchema,\n runStreamWireSchema,\n RunSubscriptionEventType,\n runSubscriptionWireSchema,\n type RealtimeConnectionChange,\n type RunCursor,\n type RunId,\n type RunlaneIdInput,\n type RunlaneTelemetryRecordingContext,\n type RunStreamName,\n type RunStreamPart,\n type RunStreamRecord,\n type RunSubscribeOptions,\n type RunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\nimport {\n createFunctionSchema,\n createUrlSchema,\n objectSurfaceSchema,\n waitForAbortableTimeout,\n type Awaitable,\n} from '@runlane/utilities/browser'\nimport * as z from 'zod'\n\nimport { ConnectionObserver } from './connection-observer.js'\nimport { invalidOptions, wireInvalid } from './errors.js'\nimport { readSse, type SseEvent } from './sse.js'\nimport { realtimeClock, realtimeTiming } from './time.js'\nimport { parseWireJson } from './wire.js'\n\n/** Run and cancellation context for an initial token request or a refresh after denial. */\nexport interface RunTokenRequest {\n readonly runId: RunId\n readonly signal: AbortSignal\n}\n\n/** Browser/Node Fetch client; the application owns its token refresh callback and lifetime. */\nexport interface RunlaneClientOptions extends RunlaneTelemetryRecordingContext {\n readonly baseUrl: string\n /** Requests an initial token when missing, or refreshes once after HTTP access denial. */\n readonly refreshAccessToken?: (request: RunTokenRequest) => Awaitable<string>\n readonly fetch?: typeof globalThis.fetch\n}\n/** Initial token, resume cursor, cancellation, and connection notifications shared by both readers. */\nexport type RunlaneClientReadOptions = Pick<RunSubscribeOptions, 'after' | 'signal'> & {\n readonly accessToken?: string\n readonly onConnectionChange?: (change: RealtimeConnectionChange) => void\n}\n/** Pull-based readers validate input on the first next(), before effects. Task generics add types only; abort ends normally. */\nexport interface RunlaneClient {\n subscribe<TTask = unknown>(\n runId: RunlaneIdInput,\n options?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunSubscriptionEvent<TTask>, void>\n /** Supply TName explicitly for an exact chunk type when TTask declares several streams. */\n readStream<TTask = unknown, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n options?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunStreamRecord<RunStreamPart<TTask, TName>>, void>\n}\nconst realtimeErrorEnvelopeSchema = z.object({ error: runRealtimeErrorSchema })\n\ntype Delivered =\n | {\n readonly kind: RunReaderKind.Subscription\n readonly value: RunSubscriptionEvent\n readonly cursor: RunCursor\n readonly terminal?: boolean\n }\n | {\n readonly kind: RunReaderKind.Stream\n readonly value: RunStreamRecord\n readonly cursor?: RunCursor\n readonly terminal?: false\n }\n | { readonly kind: 'control'; readonly value?: never; readonly cursor?: RunCursor; readonly terminal?: boolean }\n\nconst clientOptionsSchema = z.strictObject({\n baseUrl: createUrlSchema('baseUrl must be an absolute HTTP or HTTPS URL.')\n .refine((base) => base.protocol === 'https:' || base.protocol === 'http:', 'baseUrl must use HTTP or HTTPS.')\n .refine(\n (base) => !base.username && !base.password && !base.search && !base.hash,\n 'baseUrl must not contain credentials, a query, or a fragment.',\n ),\n refreshAccessToken: createFunctionSchema<NonNullable<RunlaneClientOptions['refreshAccessToken']>>(\n 'refreshAccessToken must be a function.',\n ).optional(),\n fetch: createFunctionSchema<typeof fetch>('fetch must be a function.').optional(),\n telemetry: objectSurfaceSchema.optional(),\n onTelemetryError: createFunctionSchema<NonNullable<RunlaneClientOptions['onTelemetryError']>>(\n 'onTelemetryError must be a function.',\n ).optional(),\n})\n\nconst readerFields = {\n runId: createRunlaneIdSchema<'run'>('Run id'),\n after: runCursorSchema.optional(),\n}\nconst readerRequestSchema = z.discriminatedUnion('kind', [\n z.object({ ...readerFields, kind: z.literal(RunReaderKind.Subscription), name: z.undefined() }),\n z.object({ ...readerFields, kind: z.literal(RunReaderKind.Stream), name: runStreamNameSchema }),\n])\n\n/** Creates a reusable authenticated streaming client with bounded parsing and reconnect delays. */\nexport function createRunlaneClient(options: RunlaneClientOptions): RunlaneClient {\n const checked = clientOptionsSchema.safeParse(options)\n if (!checked.success) throw invalidOptions(checked.error, 'client')\n const base = checked.data.baseUrl\n const fetcher = options.fetch ?? globalThis.fetch\n function subscribe<TTask = unknown>(\n runId: RunlaneIdInput,\n input?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunSubscriptionEvent<TTask>, void>\n async function* subscribe(\n runId: RunlaneIdInput,\n input?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunSubscriptionEvent, void> {\n for await (const event of connect(options, fetcher, base, runId, undefined, RunReaderKind.Subscription, input)) {\n if (event.kind === RunReaderKind.Subscription) yield event.value\n }\n }\n function readStream<TTask = unknown, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n input?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunStreamRecord<RunStreamPart<TTask, TName>>, void>\n async function* readStream(\n runId: RunlaneIdInput,\n name: string,\n input?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunStreamRecord, void> {\n for await (const event of connect(options, fetcher, base, runId, name, RunReaderKind.Stream, input)) {\n if (event.kind === RunReaderKind.Stream) yield event.value\n }\n }\n return { subscribe, readStream }\n}\n\nasync function* connect(\n options: RunlaneClientOptions,\n fetcher: typeof fetch,\n base: URL,\n runId: RunlaneIdInput,\n name: string | undefined,\n kind: RunReaderKind.Subscription | RunReaderKind.Stream,\n input: RunlaneClientReadOptions = {},\n): AsyncGenerator<Delivered, void> {\n const checked = readerRequestSchema.safeParse({ kind, runId, name, after: input.after })\n if (!checked.success)\n throw new RunlaneError({\n code: checked.error.issues[0]?.path[0] === 'after' ? ErrorCode.RunCursorInvalid : ErrorCode.ValidationFailed,\n message: 'Run id, stream name, or after cursor is invalid.',\n cause: checked.error,\n })\n const request = checked.data\n const url = makeUrl(base, request.runId, request.name)\n const signal = input.signal ?? new AbortController().signal\n const observer = new ConnectionObserver(input.onConnectionChange)\n let failure: { readonly cause: RunlaneError } | undefined\n try {\n yield* followConnections(\n options,\n fetcher,\n url,\n request.runId,\n kind,\n request.after,\n input.accessToken,\n signal,\n observer,\n )\n } catch (cause) {\n const error =\n cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Realtime reader failed.',\n cause,\n })\n failure = { cause: error }\n throw error\n } finally {\n observer.close(signal, failure)\n }\n}\n\nasync function* followConnections(\n options: RunlaneClientOptions,\n fetcher: typeof fetch,\n url: URL,\n runId: RunId,\n kind: RunReaderKind.Subscription | RunReaderKind.Stream,\n after: RunCursor | undefined,\n accessToken: string | undefined,\n signal: AbortSignal,\n observer: ConnectionObserver,\n): AsyncGenerator<Delivered, void> {\n const retry = new ReconnectState(after, accessToken, options.refreshAccessToken)\n const named = kind === RunReaderKind.Stream\n const operation = named ? 'read' : 'subscribe'\n while (!signal.aborted) {\n observer.emit({ state: RealtimeConnectionState.Connecting })\n for await (const delivered of connectionEvents(fetcher, url, runId, retry, signal, named, observer)) {\n const duplicate = delivered.kind === RunReaderKind.Stream && !retry.deliverRecord(delivered.value.id)\n if (!duplicate && delivered.value !== undefined) yield delivered\n // Resume only after the caller has received the value.\n retry.delivered(delivered.cursor)\n if (delivered.terminal) return\n }\n const scheduled = retry.schedule(signal, observer)\n if (scheduled === undefined) return\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeReconnectCount,\n value: 1,\n attributes: { [RunlaneMetricAttribute.Operation]: operation },\n })\n await waitForAbortableTimeout(scheduled, signal)\n }\n}\n\n// One owner for the current token, delivered checkpoint, and consecutive-failure budget.\nclass ReconnectState {\n cursor: RunCursor | undefined\n private readonly records = new Set<RunStreamRecord['id']>()\n private delay = realtimeTiming.clientReconnectDelay\n private attempts = 0\n private failures = 0\n private openedAt: number | undefined\n lastFailure: RunlaneError | undefined\n private needsRefresh: boolean\n private refreshedAfterDenial = false\n constructor(\n after: RunCursor | undefined,\n public accessToken: string | undefined,\n private readonly refreshAccessToken: RunlaneClientOptions['refreshAccessToken'],\n ) {\n this.cursor = after\n this.needsRefresh = accessToken === undefined && refreshAccessToken !== undefined\n }\n async readyToken(runId: RunId, signal: AbortSignal): Promise<boolean> {\n if (!this.needsRefresh || this.refreshAccessToken === undefined) return true\n try {\n signal.throwIfAborted()\n const token = await awaitToken(this.refreshAccessToken({ runId, signal }), signal)\n signal.throwIfAborted()\n this.accessToken = token\n this.needsRefresh = false\n return true\n } catch (cause) {\n if (signal.aborted) return false\n if (cause instanceof RunlaneError && !cause.retryable) throw cause\n this.lastFailure =\n cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime token refresh is temporarily unavailable.',\n retryable: true,\n cause,\n })\n return false\n }\n }\n refreshAfterDenial(status: number, error: RunlaneError): boolean {\n if (status !== 403 || error.code !== ErrorCode.AccessDenied) return false\n if (this.refreshAccessToken === undefined || this.refreshedAfterDenial) return false\n this.needsRefresh = true\n this.refreshedAfterDenial = true\n return true\n }\n open(): void {\n this.refreshedAfterDenial = false\n this.attempts = 0\n this.delay = realtimeTiming.clientReconnectDelay\n this.openedAt = realtimeClock.now()\n }\n deliverRecord(id: RunStreamRecord['id']): boolean {\n if (this.records.has(id)) return false\n this.records.add(id)\n return true\n }\n delivered(cursor: RunCursor | undefined): void {\n if (cursor === undefined || cursor === this.cursor) return\n this.cursor = cursor\n this.records.clear()\n this.failures = 0\n this.lastFailure = undefined\n this.attempts = 0\n this.delay = realtimeTiming.clientReconnectDelay\n }\n schedule(signal: AbortSignal, observer: ConnectionObserver): number | undefined {\n if (signal.aborted) return undefined\n // A healthy idle connection can rotate for hours without producing a new run cursor.\n if (this.openedAt !== undefined && realtimeClock.now() - this.openedAt >= realtimeTiming.heartbeatInterval) {\n this.failures = 0\n this.lastFailure = undefined\n }\n this.openedAt = undefined\n if (++this.failures >= realtimeDefaults.clientMaxReconnectAttempts)\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime connection repeatedly failed without progress.',\n retryable: false,\n cause: this.lastFailure,\n ...(this.cursor === undefined ? {} : { meta: { cursor: this.cursor } }),\n })\n const scheduled = observer.reconnect(signal, ++this.attempts, this.delay)\n this.delay = Math.min(realtimeTiming.clientMaxReconnectDelay, this.delay * 2)\n return scheduled\n }\n}\n\nasync function* connectionEvents(\n fetcher: typeof fetch,\n url: URL,\n runId: RunId,\n retry: ReconnectState,\n signal: AbortSignal,\n named: boolean,\n observer: ConnectionObserver,\n): AsyncGenerator<Delivered, void> {\n // Permanent refresher errors bypass the retryable I/O catch.\n if (!(await retry.readyToken(runId, signal))) return\n yield* readConnection(fetcher, url, retry, signal, named, observer)\n}\n\nasync function* readConnection(\n fetcher: typeof fetch,\n url: URL,\n retry: ReconnectState,\n signal: AbortSignal,\n named: boolean,\n observer: ConnectionObserver,\n): AsyncGenerator<Delivered, void> {\n let body: ReadableStream<Uint8Array> | undefined\n try {\n body = await requestStream(fetcher, url, retry, signal)\n if (body === undefined) return\n retry.open()\n observer.emit({ state: RealtimeConnectionState.Open })\n for await (const event of readSse(body, signal)) yield decodeEvent(event, named)\n } catch (cause) {\n if (observer.failed) throw cause\n if (signal.aborted) return\n if (cause instanceof RunlaneError && cause.retryable) {\n retry.lastFailure = cause\n return\n }\n throw cause\n } finally {\n if (body !== undefined && !body.locked) await body.cancel().catch(() => undefined)\n }\n}\n\nasync function requestStream(\n fetcher: typeof fetch,\n url: URL,\n retry: ReconnectState,\n signal: AbortSignal,\n): Promise<ReadableStream<Uint8Array> | undefined> {\n signal.throwIfAborted()\n const headers = new Headers({ accept: 'text/event-stream' })\n if (retry.accessToken !== undefined) headers.set('authorization', `Bearer ${retry.accessToken}`)\n if (retry.cursor !== undefined) headers.set('last-event-id', retry.cursor)\n const response = await fetchResponse(fetcher, url, headers, signal, retry)\n if (response === undefined) return undefined\n if (response.body === null || !/^text\\/event-stream(?:\\s*;|$)/i.test(response.headers.get('content-type') ?? ''))\n throw wireInvalid()\n return response.body\n}\n\nasync function awaitToken(token: Awaitable<string>, signal: AbortSignal): Promise<string> {\n const aborted = Promise.withResolvers<never>()\n const abort = () => aborted.reject(signal.reason)\n signal.addEventListener('abort', abort, { once: true })\n if (signal.aborted) abort()\n try {\n // The race observes late token rejection even after abort wins.\n return await Promise.race([token, aborted.promise])\n } finally {\n signal.removeEventListener('abort', abort)\n }\n}\n\nasync function fetchResponse(\n fetcher: typeof fetch,\n url: URL,\n headers: Headers,\n signal: AbortSignal,\n retry: ReconnectState,\n): Promise<Response | undefined> {\n let response: Response\n try {\n response = await fetcher(url, { headers, signal, cache: 'no-store', redirect: 'error' })\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime connection was interrupted.',\n retryable: true,\n cause,\n })\n }\n if (response.status >= 500 && response.headers.get('content-type')?.includes('application/json'))\n throw await responseError(response, signal)\n if (response.status === 429 || response.status >= 500) {\n await response.body?.cancel()\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime server is temporarily unavailable.',\n retryable: true,\n })\n }\n if (!response.ok) {\n const error = await responseError(response, signal)\n if (retry.refreshAfterDenial(response.status, error)) return undefined\n throw error\n }\n return response\n}\n\nfunction decodeEvent(event: SseEvent, named: boolean): Delivered {\n const raw = parseWireJson(event.data)\n if (event.event === 'error') {\n const result = runRealtimeErrorSchema.safeParse(raw)\n if (!result.success) throw wireInvalid(result.error)\n const { cursor, ...error } = result.data\n throw new RunlaneError({ ...error, ...(cursor === undefined ? {} : { meta: { cursor } }) })\n }\n if (event.event === 'end' || event.event === 'checkpoint') {\n if (!runRealtimeControlSchema.safeParse(raw).success) throw wireInvalid()\n if (event.event === 'end') return { kind: 'control', terminal: true }\n const cursor = runCursorSchema.safeParse(event.id)\n if (!cursor.success) throw wireInvalid(cursor.error)\n return { kind: 'control', cursor: cursor.data }\n }\n return named ? decodeNamedEvent(event, raw) : decodeSubscriptionEvent(event, raw)\n}\n\nfunction decodeNamedEvent(event: SseEvent, raw: unknown): Delivered {\n const result = runStreamWireSchema.safeParse(raw)\n if (\n !result.success ||\n event.event !== 'stream' ||\n (result.data.cursor !== undefined && result.data.cursor !== event.id)\n )\n throw wireInvalid()\n return { kind: RunReaderKind.Stream, value: result.data.record, cursor: result.data.cursor }\n}\n\nfunction decodeSubscriptionEvent(event: SseEvent, raw: unknown): Delivered {\n const result = runSubscriptionWireSchema.safeParse(raw)\n if (!result.success || String(result.data.type) !== event.event || result.data.cursor !== event.id)\n throw wireInvalid()\n return {\n kind: RunReaderKind.Subscription,\n value: result.data,\n cursor: result.data.cursor,\n terminal: result.data.type === RunSubscriptionEventType.Status && runStatusValues.isTerminal(result.data.status),\n }\n}\n\nasync function responseError(response: Response, signal: AbortSignal): Promise<RunlaneError> {\n if (response.body === null) throw wireInvalid()\n const reader = response.body.getReader()\n const decoder = new TextDecoder('utf-8', { fatal: true })\n let text = ''\n let bytes = 0\n const abort = () => {\n void reader.cancel().catch(() => undefined)\n }\n signal.addEventListener('abort', abort, { once: true })\n try {\n while (!signal.aborted) {\n const chunk = await reader.read()\n if (chunk.done) break\n bytes += chunk.value.byteLength\n if (bytes > realtimeDefaults.maxBufferedBytes) throw wireInvalid()\n text += decoder.decode(chunk.value, { stream: true })\n }\n const raw = parseWireJson(text + decoder.decode())\n const result = realtimeErrorEnvelopeSchema.safeParse(raw)\n if (!result.success) throw wireInvalid(result.error)\n const { cursor, ...error } = result.data.error\n return new RunlaneError({\n ...error,\n ...(cursor === undefined ? {} : { meta: { cursor } }),\n retryable: response.status >= 500 && result.data.error.retryable,\n })\n } finally {\n signal.removeEventListener('abort', abort)\n await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n}\n\nfunction makeUrl(base: URL, runId: RunId, name?: string): URL {\n try {\n const path = name === undefined ? 'events' : `streams/${encodeURIComponent(name)}`\n return new URL(`${base.href.replace(/\\/$/, '')}/runs/${encodeURIComponent(runId)}/${path}`)\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.ValidationFailed,\n message: 'Run id or stream name cannot be encoded in a URL.',\n cause,\n })\n }\n}\n\nexport {\n ErrorCode,\n RunlaneError,\n RunSubscriptionEventType,\n RunStreamRecordType,\n type RunSubscriptionEvent,\n type RunStreamRecord,\n type RunCursor,\n} from '@runlane/contracts/realtime'\n\nexport type {\n RunTypes,\n RunOutput,\n RunMeta,\n RunStreams,\n RunStreamName,\n RunStreamPart,\n} from '@runlane/contracts/realtime'\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.mjs","names":["RunlaneError","ErrorCode","RunlaneError","ErrorCode","z","RunlaneError","ErrorCode","RunSubscriptionEventType"],"sources":["../src/connection-observer.ts","../src/sse.ts","../src/client.ts"],"sourcesContent":["import {\n ErrorCode,\n RealtimeConnectionCloseReason,\n RealtimeConnectionState,\n RunlaneError,\n type RealtimeConnectionChange,\n} from '@runlane/contracts/realtime'\nimport { getDownwardJitteredDelayMs } from '@runlane/utilities/browser'\n\n/** Owns notification ordering and preserves the first callback failure without recursive reporting. */\nexport class ConnectionObserver {\n failed = false\n\n constructor(private readonly callback: ((change: RealtimeConnectionChange) => void) | undefined) {}\n\n emit(change: RealtimeConnectionChange): void {\n try {\n this.callback?.(change)\n } catch (cause) {\n // Once the observer throws, calling it again could replace the original failure.\n this.failed = true\n throw cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Realtime connection observer failed.',\n cause,\n })\n }\n }\n\n reconnect(signal: AbortSignal, attempt: number, ceilingMs: number): number | undefined {\n if (signal.aborted) return undefined\n const delayMs = getDownwardJitteredDelayMs(ceilingMs)\n this.emit({ state: RealtimeConnectionState.Reconnecting, attempt, delayMs })\n return delayMs\n }\n\n close(signal: AbortSignal, failure: { readonly cause: RunlaneError } | undefined): void {\n if (this.failed) return\n if (failure !== undefined) {\n this.emit({\n state: RealtimeConnectionState.Closed,\n reason: RealtimeConnectionCloseReason.Failed,\n error: failure.cause,\n })\n return\n }\n const reason = signal.aborted ? RealtimeConnectionCloseReason.Aborted : RealtimeConnectionCloseReason.Completed\n this.emit({ state: RealtimeConnectionState.Closed, reason })\n }\n}\n","import { ErrorCode, realtimeDefaults, RunlaneError } from '@runlane/contracts/realtime'\nimport { createParser } from 'eventsource-parser'\n\nimport { wireInvalid } from './errors.js'\n\nconst encoder = new TextEncoder()\n\n/** One parsed SSE event; id retains the preceding checkpoint when omitted by the sender. */\nexport interface SseEvent {\n readonly event: string\n readonly id: string\n readonly data: string\n}\n\n/** Incremental UTF-8 SSE parser; incomplete trailing events are never dispatched. */\nexport async function* readSse(body: ReadableStream<Uint8Array>, signal: AbortSignal): AsyncGenerator<SseEvent, void> {\n const reader = body.getReader()\n const decoder = new TextDecoder('utf-8', { fatal: true })\n let lastId = ''\n const ready: { frame?: SseEvent } = {}\n const budget = new FrameByteBudget()\n const parser = createParser({\n maxBufferSize: realtimeDefaults.maxEventBytes,\n onId(id) {\n lastId = id\n },\n onEvent(message) {\n ready.frame = { event: message.event ?? 'message', id: lastId, data: message.data }\n },\n onError(error) {\n if (error.type === 'max-buffer-size-exceeded') throw wireInvalid(error)\n },\n })\n const abort = () => {\n void reader.cancel().catch(() => undefined)\n }\n signal.addEventListener('abort', abort, { once: true })\n if (signal.aborted) abort()\n try {\n while (!signal.aborted) {\n const result = await readBytes(reader)\n if (result.done) {\n decodeUtf8(decoder)\n return\n }\n for (const char of decodeUtf8(decoder, result.value)) {\n budget.accept(char)\n parser.feed(char)\n if (ready.frame !== undefined) {\n const frame = ready.frame\n delete ready.frame\n yield frame\n }\n }\n }\n } finally {\n signal.removeEventListener('abort', abort)\n // Cancellation may race a disconnected socket; it cannot change the already delivered result.\n await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n}\n\n// Parser buffers count selected characters; this guard counts every raw UTF-8 field and comment.\nclass FrameByteBudget {\n private retained = 0\n private lineEmpty = true\n private skipLf = false\n private resetAfterCr = false\n\n accept(char: string): void {\n if (this.resetAfterCr && char !== '\\n') {\n this.retained = 0\n this.resetAfterCr = false\n }\n const point = char.codePointAt(0) ?? 0\n this.retained += point < 128 ? 1 : encoder.encode(char).byteLength\n if (this.retained > realtimeDefaults.maxEventBytes) throw wireInvalid()\n if (this.skipLf && char === '\\n') {\n this.skipLf = false\n if (this.resetAfterCr) {\n this.retained = 0\n this.resetAfterCr = false\n }\n return\n }\n this.skipLf = char === '\\r'\n if (char !== '\\r' && char !== '\\n') {\n this.lineEmpty = false\n return\n }\n if (this.lineEmpty && char === '\\n') this.retained = 0\n this.resetAfterCr = this.lineEmpty && char === '\\r'\n this.lineEmpty = true\n }\n}\n\nasync function readBytes(reader: ReadableStreamDefaultReader<Uint8Array>) {\n try {\n return await reader.read()\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime connection was interrupted.',\n retryable: true,\n cause,\n })\n }\n}\n\nfunction decodeUtf8(decoder: TextDecoder, bytes?: Uint8Array): string {\n try {\n return bytes === undefined ? decoder.decode() : decoder.decode(bytes, { stream: true })\n } catch (cause) {\n throw wireInvalid(cause)\n }\n}\n","import {\n createRunlaneIdSchema,\n ErrorCode,\n RealtimeConnectionState,\n realtimeDefaults,\n recordRunlaneTelemetryMetric,\n runCursorSchema,\n RunlaneError,\n RunlaneMetricAttribute,\n RunlaneMetricName,\n RunReaderKind,\n runRealtimeControlSchema,\n runRealtimeErrorSchema,\n runStatusValues,\n runStreamNameSchema,\n runStreamWireSchema,\n RunSubscriptionEventType,\n runSubscriptionWireSchema,\n type RealtimeConnectionChange,\n type RunCursor,\n type RunId,\n type RunlaneIdInput,\n type RunlaneTelemetryRecordingContext,\n type RunStreamName,\n type RunStreamPart,\n type RunStreamRecord,\n type RunSubscribeOptions,\n type RunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\nimport {\n createFunctionSchema,\n createUrlSchema,\n objectSurfaceSchema,\n waitForAbortableTimeout,\n type Awaitable,\n} from '@runlane/utilities/browser'\nimport * as z from 'zod'\n\nimport { ConnectionObserver } from './connection-observer.js'\nimport { invalidOptions, wireInvalid } from './errors.js'\nimport { readSse, type SseEvent } from './sse.js'\nimport { realtimeClock, realtimeTiming } from './time.js'\nimport { parseWireJson, streamSelectionQuery } from './wire.js'\n\n/** Run and cancellation context for an initial token request or a refresh after denial. */\nexport interface RunTokenRequest {\n readonly runId: RunId\n readonly signal: AbortSignal\n}\n\n/** Browser/Node Fetch client; the application owns its token refresh callback and lifetime. */\nexport interface RunlaneClientOptions extends RunlaneTelemetryRecordingContext {\n readonly baseUrl: string\n /** Requests an initial token when missing, or refreshes once after HTTP access denial. */\n readonly refreshAccessToken?: (request: RunTokenRequest) => Awaitable<string>\n readonly fetch?: typeof globalThis.fetch\n}\n/** Initial token, resume cursor, cancellation, and connection notifications shared by both readers. */\nexport type RunlaneClientReadOptions = Pick<RunSubscribeOptions, 'after' | 'signal'> & {\n readonly accessToken?: string\n readonly onConnectionChange?: (change: RealtimeConnectionChange) => void\n}\n/** Subscription controls; omit streams to read all streams permitted by the grant. */\nexport type RunlaneClientSubscribeOptions = RunlaneClientReadOptions & {\n readonly streams?: false\n}\n/** Pull-based readers validate input on the first next(), before effects. Task generics add types only; abort ends normally. */\nexport interface RunlaneClient {\n subscribe<TTask = unknown>(\n runId: RunlaneIdInput,\n options?: RunlaneClientSubscribeOptions,\n ): AsyncGenerator<RunSubscriptionEvent<TTask>, void>\n /** Supply TName explicitly for an exact chunk type when TTask declares several streams. */\n readStream<TTask = unknown, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n options?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunStreamRecord<RunStreamPart<TTask, TName>>, void>\n}\nconst realtimeErrorEnvelopeSchema = z.object({ error: runRealtimeErrorSchema })\n\ntype Delivered =\n | {\n readonly kind: RunReaderKind.Subscription\n readonly value: RunSubscriptionEvent\n readonly cursor: RunCursor\n readonly terminal?: boolean\n }\n | {\n readonly kind: RunReaderKind.Stream\n readonly value: RunStreamRecord\n readonly cursor?: RunCursor\n readonly terminal?: false\n }\n | { readonly kind: 'control'; readonly value?: never; readonly cursor?: RunCursor; readonly terminal?: boolean }\n\nconst clientOptionsSchema = z.strictObject({\n baseUrl: createUrlSchema('baseUrl must be an absolute HTTP or HTTPS URL.')\n .refine((base) => base.protocol === 'https:' || base.protocol === 'http:', 'baseUrl must use HTTP or HTTPS.')\n .refine(\n (base) => !base.username && !base.password && !base.search && !base.hash,\n 'baseUrl must not contain credentials, a query, or a fragment.',\n ),\n refreshAccessToken: createFunctionSchema<NonNullable<RunlaneClientOptions['refreshAccessToken']>>(\n 'refreshAccessToken must be a function.',\n ).optional(),\n fetch: createFunctionSchema<typeof fetch>('fetch must be a function.').optional(),\n telemetry: objectSurfaceSchema.optional(),\n onTelemetryError: createFunctionSchema<NonNullable<RunlaneClientOptions['onTelemetryError']>>(\n 'onTelemetryError must be a function.',\n ).optional(),\n})\n\nconst readerFields = {\n runId: createRunlaneIdSchema<'run'>('Run id'),\n after: runCursorSchema.optional(),\n}\nconst readerRequestSchema = z.discriminatedUnion('kind', [\n z.object({\n ...readerFields,\n kind: z.literal(RunReaderKind.Subscription),\n name: z.undefined(),\n streams: z.literal(false).optional(),\n }),\n z.object({ ...readerFields, kind: z.literal(RunReaderKind.Stream), name: runStreamNameSchema }),\n])\n\n/** Creates a reusable authenticated streaming client with bounded parsing and reconnect delays. */\nexport function createRunlaneClient(options: RunlaneClientOptions): RunlaneClient {\n const checked = clientOptionsSchema.safeParse(options)\n if (!checked.success) throw invalidOptions(checked.error, 'client')\n const base = checked.data.baseUrl\n const fetcher = options.fetch ?? globalThis.fetch\n function subscribe<TTask = unknown>(\n runId: RunlaneIdInput,\n input?: RunlaneClientSubscribeOptions,\n ): AsyncGenerator<RunSubscriptionEvent<TTask>, void>\n async function* subscribe(\n runId: RunlaneIdInput,\n input?: RunlaneClientSubscribeOptions,\n ): AsyncGenerator<RunSubscriptionEvent, void> {\n for await (const event of connect(options, fetcher, base, runId, undefined, RunReaderKind.Subscription, input)) {\n if (event.kind === RunReaderKind.Subscription) yield event.value\n }\n }\n function readStream<TTask = unknown, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n input?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunStreamRecord<RunStreamPart<TTask, TName>>, void>\n async function* readStream(\n runId: RunlaneIdInput,\n name: string,\n input?: RunlaneClientReadOptions,\n ): AsyncGenerator<RunStreamRecord, void> {\n for await (const event of connect(options, fetcher, base, runId, name, RunReaderKind.Stream, input)) {\n if (event.kind === RunReaderKind.Stream) yield event.value\n }\n }\n return { subscribe, readStream }\n}\n\nasync function* connect(\n options: RunlaneClientOptions,\n fetcher: typeof fetch,\n base: URL,\n runId: RunlaneIdInput,\n name: string | undefined,\n kind: RunReaderKind.Subscription | RunReaderKind.Stream,\n input: RunlaneClientSubscribeOptions = {},\n): AsyncGenerator<Delivered, void> {\n const checked = readerRequestSchema.safeParse({ kind, runId, name, after: input.after, streams: input.streams })\n if (!checked.success)\n throw new RunlaneError({\n code: checked.error.issues[0]?.path[0] === 'after' ? ErrorCode.RunCursorInvalid : ErrorCode.ValidationFailed,\n message: 'Run id, stream name, stream selection, or after cursor is invalid.',\n cause: checked.error,\n })\n const request = checked.data\n const url = makeUrl(base, request.runId, request.name)\n if (request.kind === RunReaderKind.Subscription && request.streams === false)\n url.searchParams.set(streamSelectionQuery.name, streamSelectionQuery.none)\n const signal = input.signal ?? new AbortController().signal\n const observer = new ConnectionObserver(input.onConnectionChange)\n let failure: { readonly cause: RunlaneError } | undefined\n try {\n yield* followConnections(\n options,\n fetcher,\n url,\n request.runId,\n kind,\n request.after,\n input.accessToken,\n signal,\n observer,\n )\n } catch (cause) {\n const error =\n cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Realtime reader failed.',\n cause,\n })\n failure = { cause: error }\n throw error\n } finally {\n observer.close(signal, failure)\n }\n}\n\nasync function* followConnections(\n options: RunlaneClientOptions,\n fetcher: typeof fetch,\n url: URL,\n runId: RunId,\n kind: RunReaderKind.Subscription | RunReaderKind.Stream,\n after: RunCursor | undefined,\n accessToken: string | undefined,\n signal: AbortSignal,\n observer: ConnectionObserver,\n): AsyncGenerator<Delivered, void> {\n const retry = new ReconnectState(after, accessToken, options.refreshAccessToken)\n const named = kind === RunReaderKind.Stream\n const operation = named ? 'read' : 'subscribe'\n while (!signal.aborted) {\n observer.emit({ state: RealtimeConnectionState.Connecting })\n for await (const delivered of connectionEvents(fetcher, url, runId, retry, signal, named, observer)) {\n const duplicate = delivered.kind === RunReaderKind.Stream && !retry.deliverRecord(delivered.value.id)\n if (!duplicate && delivered.value !== undefined) yield delivered\n // Resume only after the caller has received the value.\n retry.delivered(delivered.cursor)\n if (delivered.terminal) return\n }\n const scheduled = retry.schedule(signal, observer)\n if (scheduled === undefined) return\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeReconnectCount,\n value: 1,\n attributes: { [RunlaneMetricAttribute.Operation]: operation },\n })\n await waitForAbortableTimeout(scheduled, signal)\n }\n}\n\n// One owner for the current token, delivered checkpoint, and consecutive-failure budget.\nclass ReconnectState {\n cursor: RunCursor | undefined\n private readonly records = new Set<RunStreamRecord['id']>()\n private delay = realtimeTiming.clientReconnectDelay\n private attempts = 0\n private failures = 0\n private openedAt: number | undefined\n lastFailure: RunlaneError | undefined\n private needsRefresh: boolean\n private refreshedAfterDenial = false\n constructor(\n after: RunCursor | undefined,\n public accessToken: string | undefined,\n private readonly refreshAccessToken: RunlaneClientOptions['refreshAccessToken'],\n ) {\n this.cursor = after\n this.needsRefresh = accessToken === undefined && refreshAccessToken !== undefined\n }\n async readyToken(runId: RunId, signal: AbortSignal): Promise<boolean> {\n if (!this.needsRefresh || this.refreshAccessToken === undefined) return true\n try {\n signal.throwIfAborted()\n const token = await awaitToken(this.refreshAccessToken({ runId, signal }), signal)\n signal.throwIfAborted()\n this.accessToken = token\n this.needsRefresh = false\n return true\n } catch (cause) {\n if (signal.aborted) return false\n if (cause instanceof RunlaneError && !cause.retryable) throw cause\n this.lastFailure =\n cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime token refresh is temporarily unavailable.',\n retryable: true,\n cause,\n })\n return false\n }\n }\n refreshAfterDenial(status: number, error: RunlaneError): boolean {\n if (status !== 403 || error.code !== ErrorCode.AccessDenied) return false\n if (this.refreshAccessToken === undefined || this.refreshedAfterDenial) return false\n this.needsRefresh = true\n this.refreshedAfterDenial = true\n return true\n }\n open(): void {\n this.refreshedAfterDenial = false\n this.attempts = 0\n this.delay = realtimeTiming.clientReconnectDelay\n this.openedAt = realtimeClock.now()\n }\n deliverRecord(id: RunStreamRecord['id']): boolean {\n if (this.records.has(id)) return false\n this.records.add(id)\n return true\n }\n delivered(cursor: RunCursor | undefined): void {\n if (cursor === undefined || cursor === this.cursor) return\n this.cursor = cursor\n this.records.clear()\n this.failures = 0\n this.lastFailure = undefined\n this.attempts = 0\n this.delay = realtimeTiming.clientReconnectDelay\n }\n schedule(signal: AbortSignal, observer: ConnectionObserver): number | undefined {\n if (signal.aborted) return undefined\n // A healthy idle connection can rotate for hours without producing a new run cursor.\n if (this.openedAt !== undefined && realtimeClock.now() - this.openedAt >= realtimeTiming.heartbeatInterval) {\n this.failures = 0\n this.lastFailure = undefined\n }\n this.openedAt = undefined\n if (++this.failures >= realtimeDefaults.clientMaxReconnectAttempts)\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime connection repeatedly failed without progress.',\n retryable: false,\n cause: this.lastFailure,\n ...(this.cursor === undefined ? {} : { meta: { cursor: this.cursor } }),\n })\n const scheduled = observer.reconnect(signal, ++this.attempts, this.delay)\n this.delay = Math.min(realtimeTiming.clientMaxReconnectDelay, this.delay * 2)\n return scheduled\n }\n}\n\nasync function* connectionEvents(\n fetcher: typeof fetch,\n url: URL,\n runId: RunId,\n retry: ReconnectState,\n signal: AbortSignal,\n named: boolean,\n observer: ConnectionObserver,\n): AsyncGenerator<Delivered, void> {\n // Permanent refresher errors bypass the retryable I/O catch.\n if (!(await retry.readyToken(runId, signal))) return\n yield* readConnection(fetcher, url, retry, signal, named, observer)\n}\n\nasync function* readConnection(\n fetcher: typeof fetch,\n url: URL,\n retry: ReconnectState,\n signal: AbortSignal,\n named: boolean,\n observer: ConnectionObserver,\n): AsyncGenerator<Delivered, void> {\n let body: ReadableStream<Uint8Array> | undefined\n try {\n body = await requestStream(fetcher, url, retry, signal)\n if (body === undefined) return\n retry.open()\n observer.emit({ state: RealtimeConnectionState.Open })\n for await (const event of readSse(body, signal)) yield decodeEvent(event, named)\n } catch (cause) {\n if (observer.failed) throw cause\n if (signal.aborted) return\n if (cause instanceof RunlaneError && cause.retryable) {\n retry.lastFailure = cause\n return\n }\n throw cause\n } finally {\n if (body !== undefined && !body.locked) await body.cancel().catch(() => undefined)\n }\n}\n\nasync function requestStream(\n fetcher: typeof fetch,\n url: URL,\n retry: ReconnectState,\n signal: AbortSignal,\n): Promise<ReadableStream<Uint8Array> | undefined> {\n signal.throwIfAborted()\n const headers = new Headers({ accept: 'text/event-stream' })\n if (retry.accessToken !== undefined) headers.set('authorization', `Bearer ${retry.accessToken}`)\n if (retry.cursor !== undefined) headers.set('last-event-id', retry.cursor)\n const response = await fetchResponse(fetcher, url, headers, signal, retry)\n if (response === undefined) return undefined\n if (response.body === null || !/^text\\/event-stream(?:\\s*;|$)/i.test(response.headers.get('content-type') ?? ''))\n throw wireInvalid()\n return response.body\n}\n\nasync function awaitToken(token: Awaitable<string>, signal: AbortSignal): Promise<string> {\n const aborted = Promise.withResolvers<never>()\n const abort = () => aborted.reject(signal.reason)\n signal.addEventListener('abort', abort, { once: true })\n if (signal.aborted) abort()\n try {\n // The race observes late token rejection even after abort wins.\n return await Promise.race([token, aborted.promise])\n } finally {\n signal.removeEventListener('abort', abort)\n }\n}\n\nasync function fetchResponse(\n fetcher: typeof fetch,\n url: URL,\n headers: Headers,\n signal: AbortSignal,\n retry: ReconnectState,\n): Promise<Response | undefined> {\n let response: Response\n try {\n response = await fetcher(url, { headers, signal, cache: 'no-store', redirect: 'error' })\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime connection was interrupted.',\n retryable: true,\n cause,\n })\n }\n if (response.status >= 500 && response.headers.get('content-type')?.includes('application/json'))\n throw await responseError(response, signal)\n if (response.status === 429 || response.status >= 500) {\n await response.body?.cancel()\n throw new RunlaneError({\n code: ErrorCode.Disconnected,\n message: 'Realtime server is temporarily unavailable.',\n retryable: true,\n })\n }\n if (!response.ok) {\n const error = await responseError(response, signal)\n if (retry.refreshAfterDenial(response.status, error)) return undefined\n throw error\n }\n return response\n}\n\nfunction decodeEvent(event: SseEvent, named: boolean): Delivered {\n const raw = parseWireJson(event.data)\n if (event.event === 'error') {\n const result = runRealtimeErrorSchema.safeParse(raw)\n if (!result.success) throw wireInvalid(result.error)\n const { cursor, ...error } = result.data\n throw new RunlaneError({ ...error, ...(cursor === undefined ? {} : { meta: { cursor } }) })\n }\n if (event.event === 'end' || event.event === 'checkpoint') {\n if (!runRealtimeControlSchema.safeParse(raw).success) throw wireInvalid()\n if (event.event === 'end') return { kind: 'control', terminal: true }\n const cursor = runCursorSchema.safeParse(event.id)\n if (!cursor.success) throw wireInvalid(cursor.error)\n return { kind: 'control', cursor: cursor.data }\n }\n return named ? decodeNamedEvent(event, raw) : decodeSubscriptionEvent(event, raw)\n}\n\nfunction decodeNamedEvent(event: SseEvent, raw: unknown): Delivered {\n const result = runStreamWireSchema.safeParse(raw)\n if (\n !result.success ||\n event.event !== 'stream' ||\n (result.data.cursor !== undefined && result.data.cursor !== event.id)\n )\n throw wireInvalid()\n return { kind: RunReaderKind.Stream, value: result.data.record, cursor: result.data.cursor }\n}\n\nfunction decodeSubscriptionEvent(event: SseEvent, raw: unknown): Delivered {\n const result = runSubscriptionWireSchema.safeParse(raw)\n if (!result.success || String(result.data.type) !== event.event || result.data.cursor !== event.id)\n throw wireInvalid()\n return {\n kind: RunReaderKind.Subscription,\n value: result.data,\n cursor: result.data.cursor,\n terminal: result.data.type === RunSubscriptionEventType.Status && runStatusValues.isTerminal(result.data.status),\n }\n}\n\nasync function responseError(response: Response, signal: AbortSignal): Promise<RunlaneError> {\n if (response.body === null) throw wireInvalid()\n const reader = response.body.getReader()\n const decoder = new TextDecoder('utf-8', { fatal: true })\n let text = ''\n let bytes = 0\n const abort = () => {\n void reader.cancel().catch(() => undefined)\n }\n signal.addEventListener('abort', abort, { once: true })\n try {\n while (!signal.aborted) {\n const chunk = await reader.read()\n if (chunk.done) break\n bytes += chunk.value.byteLength\n if (bytes > realtimeDefaults.maxBufferedBytes) throw wireInvalid()\n text += decoder.decode(chunk.value, { stream: true })\n }\n const raw = parseWireJson(text + decoder.decode())\n const result = realtimeErrorEnvelopeSchema.safeParse(raw)\n if (!result.success) throw wireInvalid(result.error)\n const { cursor, ...error } = result.data.error\n return new RunlaneError({\n ...error,\n ...(cursor === undefined ? {} : { meta: { cursor } }),\n retryable: response.status >= 500 && result.data.error.retryable,\n })\n } finally {\n signal.removeEventListener('abort', abort)\n await reader.cancel().catch(() => undefined)\n reader.releaseLock()\n }\n}\n\nfunction makeUrl(base: URL, runId: RunId, name?: string): URL {\n try {\n const path = name === undefined ? 'events' : `streams/${encodeURIComponent(name)}`\n return new URL(`${base.href.replace(/\\/$/, '')}/runs/${encodeURIComponent(runId)}/${path}`)\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.ValidationFailed,\n message: 'Run id or stream name cannot be encoded in a URL.',\n cause,\n })\n }\n}\n\nexport {\n ErrorCode,\n RunlaneError,\n RunSubscriptionEventType,\n RunStreamRecordType,\n type RunSubscriptionEvent,\n type RunStreamRecord,\n type RunCursor,\n} from '@runlane/contracts/realtime'\n\nexport type {\n RunTypes,\n RunOutput,\n RunMeta,\n RunStreams,\n RunStreamName,\n RunStreamPart,\n} from '@runlane/contracts/realtime'\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_time = require("./time-BhORZuav.cjs");
2
+ const require_wire = require("./wire-xCr4QuVn.cjs");
3
3
  let _runlane_contracts_realtime = require("@runlane/contracts/realtime");
4
4
  let _runlane_utilities_browser = require("@runlane/utilities/browser");
5
5
  let eventsource_encoder = require("eventsource-encoder");
6
6
  let zod = require("zod");
7
- zod = require_time.__toESM(zod, 1);
7
+ zod = require_wire.__toESM(zod, 1);
8
8
  let jose = require("jose");
9
9
  // src/access.ts
10
10
  function intersectGrants(app, token) {
@@ -91,7 +91,7 @@ async function signRunToken(environment, tokens, input) {
91
91
  });
92
92
  requireHmacKey(tokens.key, "sign");
93
93
  const ttl = tokenLifetime(options.expiresIn?.milliseconds);
94
- const issuedAt = Math.floor(require_time.realtimeClock.now() / 1e3);
94
+ const issuedAt = Math.floor(require_wire.realtimeClock.now() / 1e3);
95
95
  const claims = _runlane_contracts_realtime.verifiedRunTokenSchema.parse({
96
96
  environment,
97
97
  runId: options.runId,
@@ -118,8 +118,8 @@ async function signRunToken(environment, tokens, input) {
118
118
  });
119
119
  }
120
120
  }
121
- function tokenLifetime(ttl = require_time.realtimeTiming.tokenTTL) {
122
- if (ttl < 1e3 || ttl > require_time.realtimeTiming.maxTokenTTL || ttl % 1e3 !== 0) throw new _runlane_contracts_realtime.RunlaneError({
121
+ function tokenLifetime(ttl = require_wire.realtimeTiming.tokenTTL) {
122
+ if (ttl < 1e3 || ttl > require_wire.realtimeTiming.maxTokenTTL || ttl % 1e3 !== 0) throw new _runlane_contracts_realtime.RunlaneError({
123
123
  code: _runlane_contracts_realtime.ErrorCode.ConfigurationInvalid,
124
124
  message: "expiresIn must be whole seconds between 1s and realtime.maxTokenTTL.",
125
125
  retryable: false
@@ -145,7 +145,7 @@ async function verifyRunToken(token, options, diagnostics = {}, operation = "ver
145
145
  reason = "claims";
146
146
  decodeBase64(body);
147
147
  const claims = _runlane_contracts_realtime.verifiedRunTokenSchema.parse(JSON.parse(decoder.decode(verified.payload)));
148
- const now = Math.floor(require_time.realtimeClock.now() / 1e3);
148
+ const now = Math.floor(require_wire.realtimeClock.now() / 1e3);
149
149
  const rejection = tokenClaimRejection(claims, options.audience, now);
150
150
  if (rejection !== void 0) {
151
151
  reason = rejection;
@@ -174,9 +174,9 @@ async function verifyRunToken(token, options, diagnostics = {}, operation = "ver
174
174
  }
175
175
  function tokenClaimRejection(claims, audience, now) {
176
176
  if (claims.audience !== audience) return "audience";
177
- if (claims.issuedAt > now + require_time.realtimeTiming.issuedAtLeeway / 1e3) return "issued_at";
177
+ if (claims.issuedAt > now + require_wire.realtimeTiming.issuedAtLeeway / 1e3) return "issued_at";
178
178
  if (claims.expiresAt <= now) return "expired";
179
- if (claims.expiresAt <= claims.issuedAt || (claims.expiresAt - claims.issuedAt) * 1e3 > require_time.realtimeTiming.maxTokenTTL) return "lifetime";
179
+ if (claims.expiresAt <= claims.issuedAt || (claims.expiresAt - claims.issuedAt) * 1e3 > require_wire.realtimeTiming.maxTokenTTL) return "lifetime";
180
180
  }
181
181
  /** Shared key policy for signing, verification, and handler startup checks. */
182
182
  function requireHmacKey(key, usage) {
@@ -214,7 +214,7 @@ const handlerOptionsSchema = zod.strictObject({
214
214
  /** Mounts authorized, bounded SSE reads without starting workers or adding runtime storage work. */
215
215
  function createRunlaneRealtime(options) {
216
216
  const checked = handlerOptionsSchema.safeParse(options);
217
- if (!checked.success) throw require_time.invalidOptions(checked.error, "handler");
217
+ if (!checked.success) throw require_wire.invalidOptions(checked.error, "handler");
218
218
  const tokens = checked.data.tokens;
219
219
  if (tokens !== void 0) {
220
220
  requireHmacKey(tokens.key, "sign");
@@ -321,7 +321,7 @@ function parseRoute(request) {
321
321
  retryable: false
322
322
  });
323
323
  const url = new URL(request.url);
324
- if (Array.from(url.searchParams.keys()).some((key) => key !== "after") || url.searchParams.getAll("after").length > 1) throw invalidRequest("Only one after query parameter is accepted; use Authorization for credentials.");
324
+ if (Array.from(url.searchParams.keys()).some((key) => key !== "after" && key !== require_wire.streamSelectionQuery.name) || url.searchParams.getAll("after").length > 1) throw invalidRequest("Only one after and one streams query parameter are accepted; use Authorization for credentials.");
325
325
  const match = /\/runs\/([^/]+)\/(events|streams\/([^/]+))$/.exec(url.pathname);
326
326
  if (match?.[1] === void 0) throw new _runlane_contracts_realtime.RunlaneError({
327
327
  code: _runlane_contracts_realtime.ErrorCode.RunNotFound,
@@ -336,9 +336,12 @@ function parseRoute(request) {
336
336
  } catch (cause) {
337
337
  throw invalidRequest("Run id or stream name is invalid.", cause);
338
338
  }
339
+ const selections = url.searchParams.getAll(require_wire.streamSelectionQuery.name);
340
+ if (selections.length > 1 || selections.length === 1 && (name !== void 0 || selections[0] !== require_wire.streamSelectionQuery.none)) throw invalidRequest("Stream selection requires one streams=none parameter on the events route.");
339
341
  return {
340
342
  runId,
341
343
  ...name === void 0 ? {} : { name },
344
+ ...selections.length === 1 ? { streams: false } : {},
342
345
  ...parseResumeCursor(request, url)
343
346
  };
344
347
  }
@@ -352,14 +355,14 @@ function parseResumeCursor(request, url) {
352
355
  return parsed?.success ? { after: parsed.data } : {};
353
356
  }
354
357
  async function openConnection(options, request, route, grant, token) {
355
- const started = require_time.realtimeClock.now();
358
+ const started = require_wire.realtimeClock.now();
356
359
  const operation = route.name === void 0 ? "subscribe" : "read";
357
360
  const control = new AbortController();
358
- const expires = Math.min(started + require_time.realtimeTiming.connectionDuration, token === void 0 ? Infinity : token.expiresAt * 1e3);
361
+ const expires = Math.min(started + require_wire.realtimeTiming.connectionDuration, token === void 0 ? Infinity : token.expiresAt * 1e3);
359
362
  const stop = () => control.abort();
360
363
  request.signal.addEventListener("abort", stop, { once: true });
361
364
  if (request.signal.aborted) stop();
362
- const expiry = setTimeout(stop, Math.max(0, expires - require_time.realtimeClock.now()));
365
+ const expiry = setTimeout(stop, Math.max(0, expires - require_wire.realtimeClock.now()));
363
366
  const frames = sourceFrames(options.runlane, route, grant, control.signal);
364
367
  let pending;
365
368
  let cursor = route.after;
@@ -373,12 +376,12 @@ async function openConnection(options, request, route, grant, token) {
373
376
  frames.return().catch(() => void 0);
374
377
  (0, _runlane_contracts_realtime.recordRunlaneTelemetryMetric)(options, {
375
378
  name: _runlane_contracts_realtime.RunlaneMetricName.RealtimeConnectionDurationMs,
376
- value: Math.max(0, require_time.realtimeClock.now() - started),
379
+ value: Math.max(0, require_wire.realtimeClock.now() - started),
377
380
  attributes: { [_runlane_contracts_realtime.RunlaneMetricAttribute.Operation]: operation }
378
381
  });
379
382
  };
380
383
  const next = async () => {
381
- if (control.signal.aborted || require_time.realtimeClock.now() >= expires) return void 0;
384
+ if (control.signal.aborted || require_wire.realtimeClock.now() >= expires) return void 0;
382
385
  pending ??= (0, _runlane_contracts_realtime.runWithRunlaneTelemetrySpan)(options, { name: route.name === void 0 ? _runlane_contracts_realtime.RunlaneSpanName.RealtimeSubscribe : _runlane_contracts_realtime.RunlaneSpanName.RealtimeRead }, () => frames.next()).catch((cause) => {
383
386
  const { cursor, ...error } = publicSourceError(cause);
384
387
  throw new _runlane_contracts_realtime.RunlaneError({
@@ -391,8 +394,8 @@ async function openConnection(options, request, route, grant, token) {
391
394
  const abortTimer = () => timer.abort();
392
395
  control.signal.addEventListener("abort", abortTimer, { once: true });
393
396
  try {
394
- const result = await Promise.race([pending, (0, _runlane_utilities_browser.waitForAbortableTimeout)(require_time.realtimeTiming.heartbeatInterval, timer.signal).then(() => void 0)]);
395
- if (require_time.realtimeClock.now() >= expires) return void 0;
397
+ const result = await Promise.race([pending, (0, _runlane_utilities_browser.waitForAbortableTimeout)(require_wire.realtimeTiming.heartbeatInterval, timer.signal).then(() => void 0)]);
398
+ if (require_wire.realtimeClock.now() >= expires) return void 0;
396
399
  if (result === void 0) return encoder.encode((0, eventsource_encoder.encode)({ comment: "heartbeat" }));
397
400
  pending = void 0;
398
401
  if (result.done) return void 0;
@@ -424,7 +427,7 @@ async function openConnection(options, request, route, grant, token) {
424
427
  },
425
428
  async pull(controller) {
426
429
  try {
427
- const bytes = control.signal.aborted || require_time.realtimeClock.now() >= expires ? void 0 : first ?? await next();
430
+ const bytes = control.signal.aborted || require_wire.realtimeClock.now() >= expires ? void 0 : first ?? await next();
428
431
  if (closed) return;
429
432
  first = void 0;
430
433
  if (bytes === void 0) {
@@ -473,7 +476,7 @@ async function* sourceFrames(source, route, grant, signal) {
473
476
  for await (const event of source.runs.subscribe(route.runId, {
474
477
  after: route.after,
475
478
  signal,
476
- streams: grant.permissions.includes(_runlane_contracts_realtime.RunReadPermission.Streams) ? grant.streams : false
479
+ streams: route.streams === false || !grant.permissions.includes(_runlane_contracts_realtime.RunReadPermission.Streams) ? false : grant.streams
477
480
  })) {
478
481
  if (signal.aborted) return;
479
482
  const projected = projectEvent(event, grant);
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["RunReadPermission","RunSubscriptionEventType","runRealtimeSnapshotSchema","z","encoder","verifiedRunTokenSchema","runAccessGrantSchema","createDurationSchema","realtimeClock","RunReadPermission","CompactSign","RunlaneError","ErrorCode","realtimeTiming","compactVerify","RunlaneMetricName","RunlaneMetricAttribute","RunlaneLogEventType","RunlaneLogLevel","RunlaneLogAttribute","base64url","createRunlaneIdSchema","createDurationSchema","contractDefaults","z","objectSurfaceSchema","createFunctionSchema","invalidOptions","RunlaneError","ErrorCode","RunlaneMetricName","RunlaneMetricAttribute","RunlaneLogEventType","RunlaneLogLevel","RunlaneLogAttribute","runAccessGrantSchema","runStreamNameSchema","runCursorSchema","realtimeClock","realtimeTiming","runWithRunlaneTelemetrySpan","RunlaneSpanName","waitForAbortableTimeout","encode","realtimeDefaults","RunReadPermission","runStatusValues","runRealtimeErrorCodeSchema"],"sources":["../src/access.ts","../src/tokens.ts","../src/handler.ts"],"sourcesContent":["import {\n RunReadPermission,\n runRealtimeSnapshotSchema,\n RunSubscriptionEventType,\n type RunAccessGrant,\n type RunSubscriptionEvent,\n type RuntimeRunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\n\nexport function intersectGrants(app: RunAccessGrant, token?: RunAccessGrant): RunAccessGrant {\n if (token === undefined) return app\n const permissions = app.permissions.filter((permission) => token.permissions.includes(permission))\n let streams = app.streams ?? token.streams\n if (app.streams !== undefined && token.streams !== undefined)\n streams = app.streams.filter((name) => token.streams?.includes(name))\n return { permissions, ...(streams === undefined ? {} : { streams }) }\n}\n\nexport function permitsStream(grant: RunAccessGrant, name: string): boolean {\n return (\n grant.permissions.includes(RunReadPermission.Streams) &&\n (grant.streams === undefined || grant.streams.includes(name))\n )\n}\n\nexport function projectEvent(\n event: RuntimeRunSubscriptionEvent,\n grant: RunAccessGrant,\n): RunSubscriptionEvent | undefined {\n if (event.type === RunSubscriptionEventType.Stream) return permitsStream(grant, event.record.name) ? event : undefined\n if (event.type === RunSubscriptionEventType.Meta)\n return grant.permissions.includes(RunReadPermission.Meta) ? event : undefined\n if (!grant.permissions.includes(RunReadPermission.Status)) return undefined\n if (event.type === RunSubscriptionEventType.Status) {\n const status = { ...event }\n if (!grant.permissions.includes(RunReadPermission.Output)) {\n delete status.output\n delete status.failure\n }\n if (status.failure !== undefined) {\n status.failure = runRealtimeSnapshotSchema.shape.failure.parse(status.failure)\n }\n return status\n }\n return { ...event, run: projectSnapshot(event.run, grant) }\n}\n\nfunction projectSnapshot(\n source: Extract<RunSubscriptionEvent, { type: RunSubscriptionEventType.Snapshot }>['run'],\n grant: RunAccessGrant,\n) {\n const { meta, output, failure, ...status } = runRealtimeSnapshotSchema.parse(source)\n return {\n ...status,\n ...(grant.permissions.includes(RunReadPermission.Meta) && meta !== undefined ? { meta } : {}),\n ...(grant.permissions.includes(RunReadPermission.Output)\n ? {\n ...(output === undefined ? {} : { output }),\n ...(failure === undefined ? {} : { failure }),\n }\n : {}),\n }\n}\n","import {\n createDurationSchema,\n ErrorCode,\n recordRunlaneTelemetryLog,\n recordRunlaneTelemetryMetric,\n runAccessGrantSchema,\n RunlaneError,\n RunlaneLogAttribute,\n RunlaneLogEventType,\n RunlaneLogLevel,\n RunlaneMetricAttribute,\n RunlaneMetricName,\n RunReadPermission,\n verifiedRunTokenSchema,\n type DurationString,\n type Environment,\n type RunlaneTelemetryRecordingContext,\n type VerifiedRunToken,\n} from '@runlane/contracts/realtime'\nimport { base64url, CompactSign, compactVerify } from 'jose'\nimport { z } from 'zod'\n\nimport { realtimeClock, realtimeTiming } from './time.js'\n\nconst tokenPartsSchema = z.tuple([z.string(), z.string(), z.string()])\nconst headerSchema = z.strictObject({ alg: z.literal('HS256'), typ: z.literal('JWT'), kid: z.string().min(1).max(128) })\nconst algorithmSchema = z.object({\n name: z.literal('HMAC'),\n hash: z.object({ name: z.literal('SHA-256') }),\n length: z.number().int().min(256),\n})\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder('utf-8', { fatal: true })\nconst maximumTokenLength = 16_384\n\n/** Strict construction schema; claim and header constraints remain the canonical owners. */\nexport const realtimeTokenOptionsSchema = z\n .strictObject({\n audience: verifiedRunTokenSchema.shape.audience,\n keyId: headerSchema.shape.kid,\n key: z.instanceof(CryptoKey),\n verifyKeys: z.record(headerSchema.shape.kid, z.instanceof(CryptoKey)).readonly().optional(),\n })\n .refine(\n (tokens) => !Object.hasOwn(tokens.verifyKeys ?? {}, tokens.keyId),\n 'tokens.keyId must not also appear in verifyKeys.',\n )\n\n/** Active signing/verification key and previous verify-only keys. Keep all keys server-side. */\nexport type RunlaneRealtimeTokenOptions = Readonly<z.output<typeof realtimeTokenOptionsSchema>>\n\nconst createTokenOptionsSchema = z.strictObject({\n // The complete claims schema validates and brands this identifier before signing.\n runId: z.string(),\n grant: runAccessGrantSchema.optional(),\n expiresIn: createDurationSchema('expiresIn must be whole seconds between 1s and realtime.maxTokenTTL.').optional(),\n})\n/** Minting inputs for one run. The app must check access before calling createToken. */\nexport type CreateRealtimeTokenOptions = Readonly<z.input<typeof createTokenOptionsSchema>> & {\n readonly expiresIn?: DurationString\n}\n\n/** Signs one run/environment/audience grant using an HMAC-SHA256 key of at least 256 bits. */\nexport async function signRunToken(\n environment: Environment,\n tokens: RunlaneRealtimeTokenOptions,\n input: CreateRealtimeTokenOptions,\n): Promise<string> {\n try {\n const options = createTokenOptionsSchema.parse(input)\n const header = headerSchema.parse({ alg: 'HS256', typ: 'JWT', kid: tokens.keyId })\n requireHmacKey(tokens.key, 'sign')\n const ttl = tokenLifetime(options.expiresIn?.milliseconds)\n const issuedAt = Math.floor(realtimeClock.now() / 1000)\n const claims = verifiedRunTokenSchema.parse({\n environment,\n runId: options.runId,\n grant: options.grant ?? { permissions: Object.values(RunReadPermission) },\n audience: tokens.audience,\n issuedAt,\n expiresAt: issuedAt + ttl / 1000,\n version: 1,\n })\n const token = await new CompactSign(encoder.encode(JSON.stringify(claims)))\n .setProtectedHeader(header)\n .sign(tokens.key)\n if (token.length > maximumTokenLength) throw invalidToken()\n return token\n } catch (cause) {\n if (cause instanceof RunlaneError) throw cause\n const message =\n cause instanceof z.ZodError\n ? `Token ${cause.issues[0]?.path.join('.') ?? 'expiresIn'} is invalid: ${cause.issues[0]?.message ?? 'Check the token options.'}`\n : 'Token signing failed; check key and claims.'\n const field = cause instanceof z.ZodError ? cause.issues[0]?.path[0] : undefined\n let code = ErrorCode.InternalError\n if (cause instanceof z.ZodError) {\n code =\n field === 'environment' || field === 'runId' || field === 'grant'\n ? ErrorCode.ValidationFailed\n : ErrorCode.ConfigurationInvalid\n }\n throw new RunlaneError({ code, message, retryable: false, cause })\n }\n}\n\nfunction tokenLifetime(ttl = realtimeTiming.tokenTTL): number {\n if (ttl < 1000 || ttl > realtimeTiming.maxTokenTTL || ttl % 1000 !== 0)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'expiresIn must be whole seconds between 1s and realtime.maxTokenTTL.',\n retryable: false,\n })\n return ttl\n}\n\n/** Verifies a scoped token and records only bounded rejection reasons through the supplied telemetry. */\nexport async function verifyRunToken(\n token: string,\n options: { readonly audience: string; readonly keys: Readonly<Record<string, CryptoKey>> },\n diagnostics: RunlaneTelemetryRecordingContext = {},\n operation = 'verify',\n): Promise<VerifiedRunToken> {\n let reason: TokenRejectionReason = TokenRejectionReason.Malformed\n try {\n if (token.length > maximumTokenLength) throw invalidToken()\n const [head, body, signature] = tokenPartsSchema.parse(token.split('.'))\n reason = TokenRejectionReason.Header\n const header = headerSchema.parse(JSON.parse(decoder.decode(decodeBase64(head))))\n reason = TokenRejectionReason.UnknownKey\n const key = Object.hasOwn(options.keys, header.kid) ? options.keys[header.kid] : undefined\n if (key === undefined) throw invalidToken()\n reason = TokenRejectionReason.Key\n requireHmacKey(key, 'verify')\n reason = TokenRejectionReason.Signature\n decodeBase64(signature)\n const verified = await compactVerify(token, key, { algorithms: ['HS256'] })\n reason = TokenRejectionReason.Claims\n decodeBase64(body)\n const claims = verifiedRunTokenSchema.parse(JSON.parse(decoder.decode(verified.payload)))\n const now = Math.floor(realtimeClock.now() / 1000)\n const rejection = tokenClaimRejection(claims, options.audience, now)\n if (rejection !== undefined) {\n reason = rejection\n throw invalidToken()\n }\n return claims\n } catch (cause) {\n recordRunlaneTelemetryMetric(diagnostics, {\n name: RunlaneMetricName.RealtimeTokenRejectedCount,\n value: 1,\n attributes: { [RunlaneMetricAttribute.Operation]: operation, [RunlaneMetricAttribute.Reason]: reason },\n })\n recordRunlaneTelemetryLog(diagnostics, {\n type: RunlaneLogEventType.RealtimeTokenRejected,\n level: RunlaneLogLevel.Warn,\n attributes: { [RunlaneLogAttribute.Operation]: operation, [RunlaneLogAttribute.Reason]: reason },\n })\n throw invalidToken(cause)\n }\n}\n\nfunction tokenClaimRejection(\n claims: VerifiedRunToken,\n audience: string,\n now: number,\n): TokenRejectionReason | undefined {\n if (claims.audience !== audience) return TokenRejectionReason.Audience\n if (claims.issuedAt > now + realtimeTiming.issuedAtLeeway / 1000) return TokenRejectionReason.IssuedAt\n if (claims.expiresAt <= now) return TokenRejectionReason.Expired\n if (claims.expiresAt <= claims.issuedAt || (claims.expiresAt - claims.issuedAt) * 1000 > realtimeTiming.maxTokenTTL)\n return TokenRejectionReason.Lifetime\n return undefined\n}\n\n/** Shared key policy for signing, verification, and handler startup checks. */\nexport function requireHmacKey(key: CryptoKey, usage: KeyUsage): void {\n if (\n !(key instanceof CryptoKey) ||\n key.type !== 'secret' ||\n !algorithmSchema.safeParse(key.algorithm).success ||\n !key.usages.includes(usage)\n )\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: `key must be an HMAC-SHA256 key of at least 256 bits with ${usage} usage.`,\n retryable: false,\n })\n}\n\nfunction decodeBase64(value: string): Uint8Array {\n if (!/^[A-Za-z0-9_-]+$/.test(value)) throw invalidToken()\n const bytes = base64url.decode(value)\n if (base64url.encode(bytes) !== value) throw invalidToken()\n return bytes\n}\n\nfunction invalidToken(cause?: unknown): RunlaneError {\n return new RunlaneError({\n code: ErrorCode.AccessDenied,\n message: 'Run access denied.',\n retryable: false,\n cause,\n })\n}\n\nenum TokenRejectionReason {\n Malformed = 'malformed',\n Header = 'header',\n UnknownKey = 'unknown_key',\n Key = 'key',\n Signature = 'signature',\n Claims = 'claims',\n Audience = 'audience',\n IssuedAt = 'issued_at',\n Expired = 'expired',\n Lifetime = 'lifetime',\n}\n","import {\n contractDefaults,\n createDurationSchema,\n createRunlaneIdSchema,\n ErrorCode,\n realtimeDefaults,\n recordRunlaneTelemetryLog,\n recordRunlaneTelemetryMetric,\n runAccessGrantSchema,\n runCursorSchema,\n RunlaneError,\n RunlaneLogAttribute,\n RunlaneLogEventType,\n RunlaneLogLevel,\n RunlaneMetricAttribute,\n RunlaneMetricName,\n RunlaneSpanName,\n RunReadPermission,\n runRealtimeErrorCodeSchema,\n runStatusValues,\n runStreamNameSchema,\n runWithRunlaneTelemetrySpan,\n type RealtimeErrorData,\n type RunAccessGrant,\n type RunCursor,\n type RunId,\n type RunlaneTelemetryRecordingContext,\n type RunReadPage,\n type RunStreamRecord,\n type VerifiedRunToken,\n} from '@runlane/contracts/realtime'\nimport {\n createFunctionSchema,\n objectSurfaceSchema,\n waitForAbortableTimeout,\n type Awaitable,\n} from '@runlane/utilities/browser'\nimport { encode } from 'eventsource-encoder'\nimport * as z from 'zod'\n\nimport { intersectGrants, permitsStream, projectEvent } from './access.js'\nimport { invalidOptions } from './errors.js'\nimport { type RunlaneRealtimeSource } from './source.js'\nimport { realtimeClock, realtimeTiming } from './time.js'\nimport {\n realtimeTokenOptionsSchema,\n requireHmacKey,\n signRunToken,\n verifyRunToken,\n type CreateRealtimeTokenOptions,\n type RunlaneRealtimeTokenOptions,\n} from './tokens.js'\n\n/** Server options require authorize, tokens, or both. Configured authorization limits every valid connection. */\nexport interface RunlaneRealtimeOptions extends RunlaneTelemetryRecordingContext {\n readonly runlane: RunlaneRealtimeSource\n readonly authorize?: (input: {\n readonly request: Request\n readonly runId: RunId\n readonly token?: VerifiedRunToken\n }) => Awaitable<RunAccessGrant | undefined>\n readonly tokens?: RunlaneRealtimeTokenOptions\n}\n/** Authorization callback for a separately defined policy; undefined denies access to the run. */\nexport type RunlaneRealtimeAuthorize = NonNullable<RunlaneRealtimeOptions['authorize']>\n\n/** Server entry for authorized reads and request-independent token minting. */\nexport interface RunlaneRealtime {\n /** Serves SSE reads; authorize, when configured, limits every valid connection. */\n readonly handle: (request: Request) => Promise<Response>\n /** Mint after the app checks access. Does not call authorize or check run existence. */\n readonly createToken: (options: CreateRealtimeTokenOptions) => Promise<string>\n}\ninterface Route {\n readonly runId: RunId\n readonly name?: string\n readonly after?: RunCursor\n}\ninterface Frame {\n readonly event: string\n readonly cursor?: RunCursor\n readonly data: object\n}\nconst encoder = new TextEncoder()\nconst runIdSchema = createRunlaneIdSchema<'run'>('Run id')\nconst pollInterval = createDurationSchema().parse(contractDefaults.streams.pollInterval).milliseconds\n\nconst handlerOptionsSchema = z\n .strictObject({\n runlane: objectSurfaceSchema,\n authorize: createFunctionSchema<RunlaneRealtimeAuthorize>('authorize must be a function.').optional(),\n tokens: realtimeTokenOptionsSchema.optional(),\n telemetry: objectSurfaceSchema.optional(),\n onTelemetryError: createFunctionSchema<NonNullable<RunlaneRealtimeOptions['onTelemetryError']>>(\n 'onTelemetryError must be a function.',\n ).optional(),\n })\n .refine(\n (options) => options.authorize !== undefined || options.tokens !== undefined,\n 'Configure authorize for session access, tokens for bearer access, or both.',\n )\n\n/** Mounts authorized, bounded SSE reads without starting workers or adding runtime storage work. */\nexport function createRunlaneRealtime(options: RunlaneRealtimeOptions): RunlaneRealtime {\n const checked = handlerOptionsSchema.safeParse(options)\n if (!checked.success) throw invalidOptions(checked.error, 'handler')\n const tokens = checked.data.tokens\n if (tokens !== undefined) {\n requireHmacKey(tokens.key, 'sign')\n requireHmacKey(tokens.key, 'verify')\n for (const key of Object.values(tokens.verifyKeys ?? {})) requireHmacKey(key, 'verify')\n }\n const verification =\n tokens === undefined\n ? undefined\n : {\n audience: tokens.audience,\n keys: { ...tokens.verifyKeys, [tokens.keyId]: tokens.key },\n }\n return {\n async createToken(input) {\n if (tokens === undefined)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Configure tokens before calling createToken.',\n })\n return signRunToken(options.runlane.environment, tokens, input)\n },\n async handle(request) {\n let operation = 'subscribe'\n try {\n const route = parseRoute(request)\n operation = route.name === undefined ? 'subscribe' : 'read'\n const token = await requestToken(request, route.runId, options, verification)\n const grant = await authorizeGrant(options, request, route, token, operation)\n return await openConnection(options, request, route, grant, token)\n } catch (cause) {\n const error = publicError(cause)\n if (error.code === ErrorCode.AccessDenied) recordDenial(options, cause, operation)\n return Response.json(\n { error },\n {\n status: request.method === 'GET' ? httpStatus(error.code) : 405,\n headers: { 'cache-control': 'no-store', ...(request.method === 'GET' ? {} : { allow: 'GET' }) },\n },\n )\n }\n },\n }\n}\n\nasync function authorizeGrant(\n options: RunlaneRealtimeOptions,\n request: Request,\n route: Route,\n token: VerifiedRunToken | undefined,\n operation: string,\n): Promise<RunAccessGrant> {\n let app: RunAccessGrant | undefined\n try {\n app =\n options.authorize === undefined\n ? token?.grant\n : await options.authorize({ request, runId: route.runId, ...(token === undefined ? {} : { token }) })\n } catch (cause) {\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeAuthorizationErrorCount,\n value: 1,\n attributes: {\n [RunlaneMetricAttribute.Operation]: operation,\n [RunlaneMetricAttribute.Reason]: 'authorize_exception',\n },\n })\n recordRunlaneTelemetryLog(options, {\n type: RunlaneLogEventType.RealtimeAuthorizationFailed,\n level: RunlaneLogLevel.Error,\n attributes: {\n [RunlaneLogAttribute.Operation]: operation,\n [RunlaneLogAttribute.Reason]: 'authorize_exception',\n },\n })\n throw new RunlaneError({ code: ErrorCode.InternalError, message: 'Realtime authorization failed.', cause })\n }\n if (app === undefined) throw accessDenied()\n const parsed = runAccessGrantSchema.safeParse(app)\n if (!parsed.success) throw invalidRequest('Realtime grant is invalid.', parsed.error)\n const grant = intersectGrants(parsed.data, token?.grant)\n if (grant.permissions.length === 0 || (route.name !== undefined && !permitsStream(grant, route.name)))\n throw accessDenied()\n return grant\n}\n\nfunction recordDenial(options: RunlaneRealtimeOptions, cause: unknown, operation: string): void {\n const reason =\n accessDenialReasonSchema.safeParse(cause instanceof RunlaneError ? cause.meta?.reason : undefined).data ??\n 'token_rejected'\n if (reason !== 'token_rejected')\n recordRunlaneTelemetryLog(options, {\n type: RunlaneLogEventType.RealtimeAuthorizationDenied,\n level: RunlaneLogLevel.Warn,\n attributes: { [RunlaneLogAttribute.Operation]: operation, [RunlaneLogAttribute.Reason]: reason },\n })\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeAuthorizationDeniedCount,\n value: 1,\n attributes: { [RunlaneMetricAttribute.Operation]: operation, [RunlaneMetricAttribute.Reason]: reason },\n })\n}\n\nfunction parseRoute(request: Request): Route {\n if (request.method !== 'GET')\n throw new RunlaneError({\n code: ErrorCode.ValidationFailed,\n message: 'Realtime routes require GET.',\n retryable: false,\n })\n const url = new URL(request.url)\n if (Array.from(url.searchParams.keys()).some((key) => key !== 'after') || url.searchParams.getAll('after').length > 1)\n throw invalidRequest('Only one after query parameter is accepted; use Authorization for credentials.')\n const match = /\\/runs\\/([^/]+)\\/(events|streams\\/([^/]+))$/.exec(url.pathname)\n if (match?.[1] === undefined)\n throw new RunlaneError({\n code: ErrorCode.RunNotFound,\n message: 'Realtime route was not found.',\n retryable: false,\n })\n let runId: RunId\n let name: string | undefined\n try {\n runId = runIdSchema.parse(decodeURIComponent(match[1]))\n name = match[3] === undefined ? undefined : runStreamNameSchema.parse(decodeURIComponent(match[3]))\n } catch (cause) {\n throw invalidRequest('Run id or stream name is invalid.', cause)\n }\n return { runId, ...(name === undefined ? {} : { name }), ...parseResumeCursor(request, url) }\n}\n\nfunction parseResumeCursor(request: Request, url: URL): Pick<Route, 'after'> {\n const header = request.headers.get('last-event-id')\n const query = url.searchParams.get('after')\n if (header !== null && query !== null && header !== query) throw invalidCursor()\n const raw = header ?? query\n const parsed = raw === null ? undefined : runCursorSchema.safeParse(raw)\n if (parsed !== undefined && !parsed.success) throw invalidCursor()\n return parsed?.success ? { after: parsed.data } : {}\n}\n\nasync function openConnection(\n options: RunlaneRealtimeOptions,\n request: Request,\n route: Route,\n grant: RunAccessGrant,\n token?: VerifiedRunToken,\n): Promise<Response> {\n const started = realtimeClock.now()\n const operation = route.name === undefined ? 'subscribe' : 'read'\n const control = new AbortController()\n const expires = Math.min(\n started + realtimeTiming.connectionDuration,\n token === undefined ? Infinity : token.expiresAt * 1000,\n )\n const stop = () => control.abort()\n request.signal.addEventListener('abort', stop, { once: true })\n if (request.signal.aborted) stop()\n const expiry = setTimeout(stop, Math.max(0, expires - realtimeClock.now()))\n const frames = sourceFrames(options.runlane, route, grant, control.signal)\n let pending: Promise<IteratorResult<Frame, void>> | undefined\n let cursor = route.after\n let closed = false\n const finish = () => {\n if (closed) return\n closed = true\n clearTimeout(expiry)\n request.signal.removeEventListener('abort', stop)\n control.abort()\n void frames.return().catch(() => undefined)\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeConnectionDurationMs,\n value: Math.max(0, realtimeClock.now() - started),\n attributes: { [RunlaneMetricAttribute.Operation]: operation },\n })\n }\n const next = async (): Promise<Uint8Array | undefined> => {\n if (control.signal.aborted || realtimeClock.now() >= expires) return undefined\n pending ??= runWithRunlaneTelemetrySpan(\n options,\n { name: route.name === undefined ? RunlaneSpanName.RealtimeSubscribe : RunlaneSpanName.RealtimeRead },\n () => frames.next(),\n ).catch((cause: unknown) => {\n // Record the source failure before converting it to the public transport contract.\n const { cursor, ...error } = publicSourceError(cause)\n throw new RunlaneError({ ...error, cause, ...(cursor === undefined ? {} : { meta: { cursor } }) })\n })\n const timer = new AbortController()\n const abortTimer = () => timer.abort()\n control.signal.addEventListener('abort', abortTimer, { once: true })\n try {\n const result = await Promise.race([\n pending,\n waitForAbortableTimeout(realtimeTiming.heartbeatInterval, timer.signal).then(() => undefined),\n ])\n if (realtimeClock.now() >= expires) return undefined\n if (result === undefined) return encoder.encode(encode({ comment: 'heartbeat' }))\n pending = undefined\n if (result.done) return undefined\n const frame = result.value\n const bytes = encodeFrame(frame, cursor)\n cursor = frame.cursor ?? cursor\n return bytes\n } finally {\n timer.abort()\n control.signal.removeEventListener('abort', abortTimer)\n }\n }\n let first: Uint8Array | undefined\n try {\n first = await next()\n } catch (cause) {\n finish()\n throw cause\n }\n // No automatic prefetch: only a reader pull advances the core iterator.\n const body = new ReadableStream<Uint8Array>(\n {\n start(controller) {\n const end = () => {\n if (closed) return\n controller.close()\n finish()\n }\n control.signal.addEventListener('abort', end, { once: true })\n if (control.signal.aborted) end()\n },\n async pull(controller) {\n try {\n const bytes = control.signal.aborted || realtimeClock.now() >= expires ? undefined : (first ?? (await next()))\n if (closed) return\n first = undefined\n if (bytes === undefined) {\n controller.close()\n finish()\n return\n }\n controller.enqueue(bytes)\n } catch (cause) {\n if (closed) return\n controller.enqueue(\n encoder.encode(encode({ event: 'error', data: JSON.stringify(publicError(cause, cursor)) })),\n )\n controller.close()\n finish()\n }\n },\n cancel() {\n finish()\n },\n },\n { highWaterMark: 0 },\n )\n return new Response(body, {\n headers: {\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-store',\n 'x-accel-buffering': 'no',\n },\n })\n}\n\nfunction encodeFrame(frame: Frame, cursor?: RunCursor): Uint8Array {\n const data = JSON.stringify(frame.data)\n const bytes = encoder.encode(\n encode({ event: frame.event, data, ...(frame.cursor === undefined ? {} : { id: frame.cursor }) }),\n )\n if (bytes.byteLength > realtimeDefaults.maxEventBytes)\n throw new RunlaneError({\n code: ErrorCode.EventTooLarge,\n message: 'Realtime event exceeds the serialized UTF-8 byte limit.',\n retryable: false,\n ...(cursor === undefined ? {} : { meta: { cursor } }),\n })\n return bytes\n}\n\nasync function* sourceFrames(\n source: RunlaneRealtimeSource,\n route: Route,\n grant: RunAccessGrant,\n signal: AbortSignal,\n): AsyncGenerator<Frame, void> {\n if (route.name === undefined) {\n for await (const event of source.runs.subscribe(route.runId, {\n after: route.after,\n signal,\n streams: grant.permissions.includes(RunReadPermission.Streams) ? grant.streams : false,\n })) {\n if (signal.aborted) return\n const projected = projectEvent(event, grant)\n yield projected === undefined\n ? { event: 'checkpoint', cursor: event.cursor, data: {} }\n : { event: projected.type, cursor: projected.cursor, data: projected }\n }\n if (!signal.aborted) yield { event: 'end', data: {} }\n return\n }\n yield* namedFrames(source, route.runId, route.name, route.after, signal)\n}\n\nasync function* namedFrames(\n source: RunlaneRealtimeSource,\n runId: RunId,\n name: string,\n initial: RunCursor | undefined,\n signal: AbortSignal,\n): AsyncGenerator<Frame, void> {\n let after = initial\n let terminal = false\n while (!signal.aborted) {\n const controller = new AbortController()\n const waiting = source.lane?.runSignal\n ?.wait({\n environment: source.environment,\n runId,\n signal: AbortSignal.any([signal, controller.signal]),\n timeoutMs: pollInterval,\n })\n .catch((cause: unknown) => {\n throw new RunlaneError({\n code: ErrorCode.AdapterContractViolation,\n message: 'Lane run signal wait must not reject.',\n cause,\n })\n })\n // A provider may reject before the page read finishes or while the consumer pauses.\n void waiting?.catch(() => undefined)\n try {\n const page = await source.runs.readStream(runId, name, { after, limit: contractDefaults.pagination.defaultLimit })\n yield* namedPageFrames(page, after)\n after = page.cursor\n if (page.hasMore) continue\n if (terminal && page.records.length === 0) {\n yield { event: 'end', data: {} }\n return\n }\n terminal = await readNamedRunTerminal(source, runId)\n if (terminal) continue\n if (page.records.length === 0) await (waiting ?? waitForAbortableTimeout(pollInterval, signal))\n } finally {\n controller.abort()\n await waiting\n }\n }\n}\n\nasync function readNamedRunTerminal(source: RunlaneRealtimeSource, runId: RunId): Promise<boolean> {\n const run = await source.runs.get(runId)\n if (run === undefined)\n throw new RunlaneError({\n code: ErrorCode.RunNotFound,\n message: 'Run was not found.',\n retryable: false,\n })\n return runStatusValues.isTerminal(run.status)\n}\n\nfunction* namedPageFrames(page: RunReadPage<RunStreamRecord>, after?: RunCursor): Generator<Frame, void> {\n for (const [index, record] of page.records.entries()) {\n const last = index === page.records.length - 1\n yield {\n event: 'stream',\n ...(last ? { cursor: page.cursor } : {}),\n data: { ...(last ? { cursor: page.cursor } : {}), record },\n }\n }\n if (page.records.length === 0 && page.cursor !== after) yield { event: 'checkpoint', cursor: page.cursor, data: {} }\n}\n\nfunction publicError(cause: unknown, cursor?: RunCursor): RealtimeErrorData {\n const checkpoint =\n cursor ?? (cause instanceof RunlaneError ? runCursorSchema.safeParse(cause.meta?.cursor).data : undefined)\n const resume = checkpoint === undefined ? {} : { cursor: checkpoint }\n if (cause instanceof RunlaneError) {\n const code = runRealtimeErrorCodeSchema.safeParse(cause.code)\n if (code.success && code.data !== ErrorCode.InternalError) {\n return { code: code.data, message: cause.message, retryable: cause.retryable, ...resume }\n }\n }\n return {\n code: ErrorCode.InternalError,\n message: 'Realtime request failed.',\n retryable: true,\n ...resume,\n }\n}\nfunction publicSourceError(cause: unknown): RealtimeErrorData {\n const error = publicError(cause)\n if (cause instanceof RunlaneError) {\n switch (cause.code) {\n case ErrorCode.ConfigurationInvalid:\n case ErrorCode.ValidationFailed:\n return { ...error, code: ErrorCode.ValidationFailed, message: 'Realtime request is invalid.', retryable: false }\n case ErrorCode.RunCursorInvalid:\n return { ...error, code: ErrorCode.RunCursorInvalid, message: 'Run cursor is invalid.', retryable: false }\n case ErrorCode.RunNotFound:\n return { ...error, code: ErrorCode.RunNotFound, message: 'Run was not found.', retryable: false }\n case ErrorCode.OperationCancelled:\n return {\n ...error,\n code: ErrorCode.Disconnected,\n message: 'Realtime runtime is closing; reconnect to resume.',\n retryable: true,\n }\n }\n }\n return error\n}\nfunction httpStatus(code: ErrorCode): number {\n if (code === ErrorCode.AccessDenied) return 403\n if (code === ErrorCode.RunNotFound) return 404\n if (code === ErrorCode.ValidationFailed || code === ErrorCode.RunCursorInvalid) return 400\n return 500\n}\nenum AccessDenialReason {\n ApplicationDenied = 'application_denied',\n AuthorizationHeader = 'authorization_header',\n VerificationUnconfigured = 'verification_unconfigured',\n TokenScope = 'token_scope',\n}\nconst accessDenialReasonSchema = z.enum(AccessDenialReason)\n\nfunction accessDenied(reason: AccessDenialReason = AccessDenialReason.ApplicationDenied): RunlaneError {\n return new RunlaneError({ code: ErrorCode.AccessDenied, message: 'Run access denied.', meta: { reason } })\n}\nfunction invalidRequest(message: string, cause?: unknown): RunlaneError {\n return new RunlaneError({ code: ErrorCode.ValidationFailed, message, retryable: false, cause })\n}\nfunction invalidCursor(): RunlaneError {\n return new RunlaneError({\n code: ErrorCode.RunCursorInvalid,\n message: 'Run cursor is invalid or conflicts with after.',\n retryable: false,\n })\n}\n\nasync function requestToken(\n request: Request,\n runId: RunId,\n options: RunlaneRealtimeOptions,\n verification: Parameters<typeof verifyRunToken>[1] | undefined,\n): Promise<VerifiedRunToken | undefined> {\n const supplied = request.headers.get('authorization')\n if (supplied === null) return undefined\n const match = /^Bearer ([a-z0-9_.-]+)$/i.exec(supplied)\n if (match?.[1] === undefined) throw accessDenied(AccessDenialReason.AuthorizationHeader)\n if (verification === undefined) throw accessDenied(AccessDenialReason.VerificationUnconfigured)\n const token = await verifyRunToken(\n match[1],\n verification,\n options,\n new URL(request.url).pathname.endsWith('/events') ? 'subscribe' : 'read',\n )\n if (token.runId !== runId || token.environment.name !== options.runlane.environment.name)\n throw accessDenied(AccessDenialReason.TokenScope)\n return token\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["RunReadPermission","RunSubscriptionEventType","runRealtimeSnapshotSchema","z","encoder","verifiedRunTokenSchema","runAccessGrantSchema","createDurationSchema","realtimeClock","RunReadPermission","CompactSign","RunlaneError","ErrorCode","realtimeTiming","compactVerify","RunlaneMetricName","RunlaneMetricAttribute","RunlaneLogEventType","RunlaneLogLevel","RunlaneLogAttribute","base64url","createRunlaneIdSchema","createDurationSchema","contractDefaults","z","objectSurfaceSchema","createFunctionSchema","invalidOptions","RunlaneError","ErrorCode","RunlaneMetricName","RunlaneMetricAttribute","RunlaneLogEventType","RunlaneLogLevel","RunlaneLogAttribute","runAccessGrantSchema","streamSelectionQuery","runStreamNameSchema","runCursorSchema","realtimeClock","realtimeTiming","runWithRunlaneTelemetrySpan","RunlaneSpanName","waitForAbortableTimeout","encode","realtimeDefaults","RunReadPermission","runStatusValues","runRealtimeErrorCodeSchema"],"sources":["../src/access.ts","../src/tokens.ts","../src/handler.ts"],"sourcesContent":["import {\n RunReadPermission,\n runRealtimeSnapshotSchema,\n RunSubscriptionEventType,\n type RunAccessGrant,\n type RunSubscriptionEvent,\n type RuntimeRunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\n\nexport function intersectGrants(app: RunAccessGrant, token?: RunAccessGrant): RunAccessGrant {\n if (token === undefined) return app\n const permissions = app.permissions.filter((permission) => token.permissions.includes(permission))\n let streams = app.streams ?? token.streams\n if (app.streams !== undefined && token.streams !== undefined)\n streams = app.streams.filter((name) => token.streams?.includes(name))\n return { permissions, ...(streams === undefined ? {} : { streams }) }\n}\n\nexport function permitsStream(grant: RunAccessGrant, name: string): boolean {\n return (\n grant.permissions.includes(RunReadPermission.Streams) &&\n (grant.streams === undefined || grant.streams.includes(name))\n )\n}\n\nexport function projectEvent(\n event: RuntimeRunSubscriptionEvent,\n grant: RunAccessGrant,\n): RunSubscriptionEvent | undefined {\n if (event.type === RunSubscriptionEventType.Stream) return permitsStream(grant, event.record.name) ? event : undefined\n if (event.type === RunSubscriptionEventType.Meta)\n return grant.permissions.includes(RunReadPermission.Meta) ? event : undefined\n if (!grant.permissions.includes(RunReadPermission.Status)) return undefined\n if (event.type === RunSubscriptionEventType.Status) {\n const status = { ...event }\n if (!grant.permissions.includes(RunReadPermission.Output)) {\n delete status.output\n delete status.failure\n }\n if (status.failure !== undefined) {\n status.failure = runRealtimeSnapshotSchema.shape.failure.parse(status.failure)\n }\n return status\n }\n return { ...event, run: projectSnapshot(event.run, grant) }\n}\n\nfunction projectSnapshot(\n source: Extract<RunSubscriptionEvent, { type: RunSubscriptionEventType.Snapshot }>['run'],\n grant: RunAccessGrant,\n) {\n const { meta, output, failure, ...status } = runRealtimeSnapshotSchema.parse(source)\n return {\n ...status,\n ...(grant.permissions.includes(RunReadPermission.Meta) && meta !== undefined ? { meta } : {}),\n ...(grant.permissions.includes(RunReadPermission.Output)\n ? {\n ...(output === undefined ? {} : { output }),\n ...(failure === undefined ? {} : { failure }),\n }\n : {}),\n }\n}\n","import {\n createDurationSchema,\n ErrorCode,\n recordRunlaneTelemetryLog,\n recordRunlaneTelemetryMetric,\n runAccessGrantSchema,\n RunlaneError,\n RunlaneLogAttribute,\n RunlaneLogEventType,\n RunlaneLogLevel,\n RunlaneMetricAttribute,\n RunlaneMetricName,\n RunReadPermission,\n verifiedRunTokenSchema,\n type DurationString,\n type Environment,\n type RunlaneTelemetryRecordingContext,\n type VerifiedRunToken,\n} from '@runlane/contracts/realtime'\nimport { base64url, CompactSign, compactVerify } from 'jose'\nimport { z } from 'zod'\n\nimport { realtimeClock, realtimeTiming } from './time.js'\n\nconst tokenPartsSchema = z.tuple([z.string(), z.string(), z.string()])\nconst headerSchema = z.strictObject({ alg: z.literal('HS256'), typ: z.literal('JWT'), kid: z.string().min(1).max(128) })\nconst algorithmSchema = z.object({\n name: z.literal('HMAC'),\n hash: z.object({ name: z.literal('SHA-256') }),\n length: z.number().int().min(256),\n})\nconst encoder = new TextEncoder()\nconst decoder = new TextDecoder('utf-8', { fatal: true })\nconst maximumTokenLength = 16_384\n\n/** Strict construction schema; claim and header constraints remain the canonical owners. */\nexport const realtimeTokenOptionsSchema = z\n .strictObject({\n audience: verifiedRunTokenSchema.shape.audience,\n keyId: headerSchema.shape.kid,\n key: z.instanceof(CryptoKey),\n verifyKeys: z.record(headerSchema.shape.kid, z.instanceof(CryptoKey)).readonly().optional(),\n })\n .refine(\n (tokens) => !Object.hasOwn(tokens.verifyKeys ?? {}, tokens.keyId),\n 'tokens.keyId must not also appear in verifyKeys.',\n )\n\n/** Active signing/verification key and previous verify-only keys. Keep all keys server-side. */\nexport type RunlaneRealtimeTokenOptions = Readonly<z.output<typeof realtimeTokenOptionsSchema>>\n\nconst createTokenOptionsSchema = z.strictObject({\n // The complete claims schema validates and brands this identifier before signing.\n runId: z.string(),\n grant: runAccessGrantSchema.optional(),\n expiresIn: createDurationSchema('expiresIn must be whole seconds between 1s and realtime.maxTokenTTL.').optional(),\n})\n/** Minting inputs for one run. The app must check access before calling createToken. */\nexport type CreateRealtimeTokenOptions = Readonly<z.input<typeof createTokenOptionsSchema>> & {\n readonly expiresIn?: DurationString\n}\n\n/** Signs one run/environment/audience grant using an HMAC-SHA256 key of at least 256 bits. */\nexport async function signRunToken(\n environment: Environment,\n tokens: RunlaneRealtimeTokenOptions,\n input: CreateRealtimeTokenOptions,\n): Promise<string> {\n try {\n const options = createTokenOptionsSchema.parse(input)\n const header = headerSchema.parse({ alg: 'HS256', typ: 'JWT', kid: tokens.keyId })\n requireHmacKey(tokens.key, 'sign')\n const ttl = tokenLifetime(options.expiresIn?.milliseconds)\n const issuedAt = Math.floor(realtimeClock.now() / 1000)\n const claims = verifiedRunTokenSchema.parse({\n environment,\n runId: options.runId,\n grant: options.grant ?? { permissions: Object.values(RunReadPermission) },\n audience: tokens.audience,\n issuedAt,\n expiresAt: issuedAt + ttl / 1000,\n version: 1,\n })\n const token = await new CompactSign(encoder.encode(JSON.stringify(claims)))\n .setProtectedHeader(header)\n .sign(tokens.key)\n if (token.length > maximumTokenLength) throw invalidToken()\n return token\n } catch (cause) {\n if (cause instanceof RunlaneError) throw cause\n const message =\n cause instanceof z.ZodError\n ? `Token ${cause.issues[0]?.path.join('.') ?? 'expiresIn'} is invalid: ${cause.issues[0]?.message ?? 'Check the token options.'}`\n : 'Token signing failed; check key and claims.'\n const field = cause instanceof z.ZodError ? cause.issues[0]?.path[0] : undefined\n let code = ErrorCode.InternalError\n if (cause instanceof z.ZodError) {\n code =\n field === 'environment' || field === 'runId' || field === 'grant'\n ? ErrorCode.ValidationFailed\n : ErrorCode.ConfigurationInvalid\n }\n throw new RunlaneError({ code, message, retryable: false, cause })\n }\n}\n\nfunction tokenLifetime(ttl = realtimeTiming.tokenTTL): number {\n if (ttl < 1000 || ttl > realtimeTiming.maxTokenTTL || ttl % 1000 !== 0)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'expiresIn must be whole seconds between 1s and realtime.maxTokenTTL.',\n retryable: false,\n })\n return ttl\n}\n\n/** Verifies a scoped token and records only bounded rejection reasons through the supplied telemetry. */\nexport async function verifyRunToken(\n token: string,\n options: { readonly audience: string; readonly keys: Readonly<Record<string, CryptoKey>> },\n diagnostics: RunlaneTelemetryRecordingContext = {},\n operation = 'verify',\n): Promise<VerifiedRunToken> {\n let reason: TokenRejectionReason = TokenRejectionReason.Malformed\n try {\n if (token.length > maximumTokenLength) throw invalidToken()\n const [head, body, signature] = tokenPartsSchema.parse(token.split('.'))\n reason = TokenRejectionReason.Header\n const header = headerSchema.parse(JSON.parse(decoder.decode(decodeBase64(head))))\n reason = TokenRejectionReason.UnknownKey\n const key = Object.hasOwn(options.keys, header.kid) ? options.keys[header.kid] : undefined\n if (key === undefined) throw invalidToken()\n reason = TokenRejectionReason.Key\n requireHmacKey(key, 'verify')\n reason = TokenRejectionReason.Signature\n decodeBase64(signature)\n const verified = await compactVerify(token, key, { algorithms: ['HS256'] })\n reason = TokenRejectionReason.Claims\n decodeBase64(body)\n const claims = verifiedRunTokenSchema.parse(JSON.parse(decoder.decode(verified.payload)))\n const now = Math.floor(realtimeClock.now() / 1000)\n const rejection = tokenClaimRejection(claims, options.audience, now)\n if (rejection !== undefined) {\n reason = rejection\n throw invalidToken()\n }\n return claims\n } catch (cause) {\n recordRunlaneTelemetryMetric(diagnostics, {\n name: RunlaneMetricName.RealtimeTokenRejectedCount,\n value: 1,\n attributes: { [RunlaneMetricAttribute.Operation]: operation, [RunlaneMetricAttribute.Reason]: reason },\n })\n recordRunlaneTelemetryLog(diagnostics, {\n type: RunlaneLogEventType.RealtimeTokenRejected,\n level: RunlaneLogLevel.Warn,\n attributes: { [RunlaneLogAttribute.Operation]: operation, [RunlaneLogAttribute.Reason]: reason },\n })\n throw invalidToken(cause)\n }\n}\n\nfunction tokenClaimRejection(\n claims: VerifiedRunToken,\n audience: string,\n now: number,\n): TokenRejectionReason | undefined {\n if (claims.audience !== audience) return TokenRejectionReason.Audience\n if (claims.issuedAt > now + realtimeTiming.issuedAtLeeway / 1000) return TokenRejectionReason.IssuedAt\n if (claims.expiresAt <= now) return TokenRejectionReason.Expired\n if (claims.expiresAt <= claims.issuedAt || (claims.expiresAt - claims.issuedAt) * 1000 > realtimeTiming.maxTokenTTL)\n return TokenRejectionReason.Lifetime\n return undefined\n}\n\n/** Shared key policy for signing, verification, and handler startup checks. */\nexport function requireHmacKey(key: CryptoKey, usage: KeyUsage): void {\n if (\n !(key instanceof CryptoKey) ||\n key.type !== 'secret' ||\n !algorithmSchema.safeParse(key.algorithm).success ||\n !key.usages.includes(usage)\n )\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: `key must be an HMAC-SHA256 key of at least 256 bits with ${usage} usage.`,\n retryable: false,\n })\n}\n\nfunction decodeBase64(value: string): Uint8Array {\n if (!/^[A-Za-z0-9_-]+$/.test(value)) throw invalidToken()\n const bytes = base64url.decode(value)\n if (base64url.encode(bytes) !== value) throw invalidToken()\n return bytes\n}\n\nfunction invalidToken(cause?: unknown): RunlaneError {\n return new RunlaneError({\n code: ErrorCode.AccessDenied,\n message: 'Run access denied.',\n retryable: false,\n cause,\n })\n}\n\nenum TokenRejectionReason {\n Malformed = 'malformed',\n Header = 'header',\n UnknownKey = 'unknown_key',\n Key = 'key',\n Signature = 'signature',\n Claims = 'claims',\n Audience = 'audience',\n IssuedAt = 'issued_at',\n Expired = 'expired',\n Lifetime = 'lifetime',\n}\n","import {\n contractDefaults,\n createDurationSchema,\n createRunlaneIdSchema,\n ErrorCode,\n realtimeDefaults,\n recordRunlaneTelemetryLog,\n recordRunlaneTelemetryMetric,\n runAccessGrantSchema,\n runCursorSchema,\n RunlaneError,\n RunlaneLogAttribute,\n RunlaneLogEventType,\n RunlaneLogLevel,\n RunlaneMetricAttribute,\n RunlaneMetricName,\n RunlaneSpanName,\n RunReadPermission,\n runRealtimeErrorCodeSchema,\n runStatusValues,\n runStreamNameSchema,\n runWithRunlaneTelemetrySpan,\n type RealtimeErrorData,\n type RunAccessGrant,\n type RunCursor,\n type RunId,\n type RunlaneTelemetryRecordingContext,\n type RunReadPage,\n type RunStreamRecord,\n type VerifiedRunToken,\n} from '@runlane/contracts/realtime'\nimport {\n createFunctionSchema,\n objectSurfaceSchema,\n waitForAbortableTimeout,\n type Awaitable,\n} from '@runlane/utilities/browser'\nimport { encode } from 'eventsource-encoder'\nimport * as z from 'zod'\n\nimport { intersectGrants, permitsStream, projectEvent } from './access.js'\nimport { invalidOptions } from './errors.js'\nimport { type RunlaneRealtimeSource } from './source.js'\nimport { realtimeClock, realtimeTiming } from './time.js'\nimport {\n realtimeTokenOptionsSchema,\n requireHmacKey,\n signRunToken,\n verifyRunToken,\n type CreateRealtimeTokenOptions,\n type RunlaneRealtimeTokenOptions,\n} from './tokens.js'\nimport { streamSelectionQuery } from './wire.js'\n\n/** Server options require authorize, tokens, or both. Configured authorization limits every valid connection. */\nexport interface RunlaneRealtimeOptions extends RunlaneTelemetryRecordingContext {\n readonly runlane: RunlaneRealtimeSource\n readonly authorize?: (input: {\n readonly request: Request\n readonly runId: RunId\n readonly token?: VerifiedRunToken\n }) => Awaitable<RunAccessGrant | undefined>\n readonly tokens?: RunlaneRealtimeTokenOptions\n}\n/** Authorization callback for a separately defined policy; undefined denies access to the run. */\nexport type RunlaneRealtimeAuthorize = NonNullable<RunlaneRealtimeOptions['authorize']>\n\n/** Server entry for authorized reads and request-independent token minting. */\nexport interface RunlaneRealtime {\n /** Serves SSE reads; authorize, when configured, limits every valid connection. */\n readonly handle: (request: Request) => Promise<Response>\n /** Mint after the app checks access. Does not call authorize or check run existence. */\n readonly createToken: (options: CreateRealtimeTokenOptions) => Promise<string>\n}\ninterface Route {\n readonly runId: RunId\n readonly name?: string\n readonly streams?: false\n readonly after?: RunCursor\n}\ninterface Frame {\n readonly event: string\n readonly cursor?: RunCursor\n readonly data: object\n}\nconst encoder = new TextEncoder()\nconst runIdSchema = createRunlaneIdSchema<'run'>('Run id')\nconst pollInterval = createDurationSchema().parse(contractDefaults.streams.pollInterval).milliseconds\n\nconst handlerOptionsSchema = z\n .strictObject({\n runlane: objectSurfaceSchema,\n authorize: createFunctionSchema<RunlaneRealtimeAuthorize>('authorize must be a function.').optional(),\n tokens: realtimeTokenOptionsSchema.optional(),\n telemetry: objectSurfaceSchema.optional(),\n onTelemetryError: createFunctionSchema<NonNullable<RunlaneRealtimeOptions['onTelemetryError']>>(\n 'onTelemetryError must be a function.',\n ).optional(),\n })\n .refine(\n (options) => options.authorize !== undefined || options.tokens !== undefined,\n 'Configure authorize for session access, tokens for bearer access, or both.',\n )\n\n/** Mounts authorized, bounded SSE reads without starting workers or adding runtime storage work. */\nexport function createRunlaneRealtime(options: RunlaneRealtimeOptions): RunlaneRealtime {\n const checked = handlerOptionsSchema.safeParse(options)\n if (!checked.success) throw invalidOptions(checked.error, 'handler')\n const tokens = checked.data.tokens\n if (tokens !== undefined) {\n requireHmacKey(tokens.key, 'sign')\n requireHmacKey(tokens.key, 'verify')\n for (const key of Object.values(tokens.verifyKeys ?? {})) requireHmacKey(key, 'verify')\n }\n const verification =\n tokens === undefined\n ? undefined\n : {\n audience: tokens.audience,\n keys: { ...tokens.verifyKeys, [tokens.keyId]: tokens.key },\n }\n return {\n async createToken(input) {\n if (tokens === undefined)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Configure tokens before calling createToken.',\n })\n return signRunToken(options.runlane.environment, tokens, input)\n },\n async handle(request) {\n let operation = 'subscribe'\n try {\n const route = parseRoute(request)\n operation = route.name === undefined ? 'subscribe' : 'read'\n const token = await requestToken(request, route.runId, options, verification)\n const grant = await authorizeGrant(options, request, route, token, operation)\n return await openConnection(options, request, route, grant, token)\n } catch (cause) {\n const error = publicError(cause)\n if (error.code === ErrorCode.AccessDenied) recordDenial(options, cause, operation)\n return Response.json(\n { error },\n {\n status: request.method === 'GET' ? httpStatus(error.code) : 405,\n headers: { 'cache-control': 'no-store', ...(request.method === 'GET' ? {} : { allow: 'GET' }) },\n },\n )\n }\n },\n }\n}\n\nasync function authorizeGrant(\n options: RunlaneRealtimeOptions,\n request: Request,\n route: Route,\n token: VerifiedRunToken | undefined,\n operation: string,\n): Promise<RunAccessGrant> {\n let app: RunAccessGrant | undefined\n try {\n app =\n options.authorize === undefined\n ? token?.grant\n : await options.authorize({ request, runId: route.runId, ...(token === undefined ? {} : { token }) })\n } catch (cause) {\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeAuthorizationErrorCount,\n value: 1,\n attributes: {\n [RunlaneMetricAttribute.Operation]: operation,\n [RunlaneMetricAttribute.Reason]: 'authorize_exception',\n },\n })\n recordRunlaneTelemetryLog(options, {\n type: RunlaneLogEventType.RealtimeAuthorizationFailed,\n level: RunlaneLogLevel.Error,\n attributes: {\n [RunlaneLogAttribute.Operation]: operation,\n [RunlaneLogAttribute.Reason]: 'authorize_exception',\n },\n })\n throw new RunlaneError({ code: ErrorCode.InternalError, message: 'Realtime authorization failed.', cause })\n }\n if (app === undefined) throw accessDenied()\n const parsed = runAccessGrantSchema.safeParse(app)\n if (!parsed.success) throw invalidRequest('Realtime grant is invalid.', parsed.error)\n const grant = intersectGrants(parsed.data, token?.grant)\n if (grant.permissions.length === 0 || (route.name !== undefined && !permitsStream(grant, route.name)))\n throw accessDenied()\n return grant\n}\n\nfunction recordDenial(options: RunlaneRealtimeOptions, cause: unknown, operation: string): void {\n const reason =\n accessDenialReasonSchema.safeParse(cause instanceof RunlaneError ? cause.meta?.reason : undefined).data ??\n 'token_rejected'\n if (reason !== 'token_rejected')\n recordRunlaneTelemetryLog(options, {\n type: RunlaneLogEventType.RealtimeAuthorizationDenied,\n level: RunlaneLogLevel.Warn,\n attributes: { [RunlaneLogAttribute.Operation]: operation, [RunlaneLogAttribute.Reason]: reason },\n })\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeAuthorizationDeniedCount,\n value: 1,\n attributes: { [RunlaneMetricAttribute.Operation]: operation, [RunlaneMetricAttribute.Reason]: reason },\n })\n}\n\nfunction parseRoute(request: Request): Route {\n if (request.method !== 'GET')\n throw new RunlaneError({\n code: ErrorCode.ValidationFailed,\n message: 'Realtime routes require GET.',\n retryable: false,\n })\n const url = new URL(request.url)\n if (\n Array.from(url.searchParams.keys()).some((key) => key !== 'after' && key !== streamSelectionQuery.name) ||\n url.searchParams.getAll('after').length > 1\n )\n throw invalidRequest(\n 'Only one after and one streams query parameter are accepted; use Authorization for credentials.',\n )\n const match = /\\/runs\\/([^/]+)\\/(events|streams\\/([^/]+))$/.exec(url.pathname)\n if (match?.[1] === undefined)\n throw new RunlaneError({\n code: ErrorCode.RunNotFound,\n message: 'Realtime route was not found.',\n retryable: false,\n })\n let runId: RunId\n let name: string | undefined\n try {\n runId = runIdSchema.parse(decodeURIComponent(match[1]))\n name = match[3] === undefined ? undefined : runStreamNameSchema.parse(decodeURIComponent(match[3]))\n } catch (cause) {\n throw invalidRequest('Run id or stream name is invalid.', cause)\n }\n const selections = url.searchParams.getAll(streamSelectionQuery.name)\n if (\n selections.length > 1 ||\n (selections.length === 1 && (name !== undefined || selections[0] !== streamSelectionQuery.none))\n )\n throw invalidRequest('Stream selection requires one streams=none parameter on the events route.')\n return {\n runId,\n ...(name === undefined ? {} : { name }),\n ...(selections.length === 1 ? { streams: false } : {}),\n ...parseResumeCursor(request, url),\n }\n}\n\nfunction parseResumeCursor(request: Request, url: URL): Pick<Route, 'after'> {\n const header = request.headers.get('last-event-id')\n const query = url.searchParams.get('after')\n if (header !== null && query !== null && header !== query) throw invalidCursor()\n const raw = header ?? query\n const parsed = raw === null ? undefined : runCursorSchema.safeParse(raw)\n if (parsed !== undefined && !parsed.success) throw invalidCursor()\n return parsed?.success ? { after: parsed.data } : {}\n}\n\nasync function openConnection(\n options: RunlaneRealtimeOptions,\n request: Request,\n route: Route,\n grant: RunAccessGrant,\n token?: VerifiedRunToken,\n): Promise<Response> {\n const started = realtimeClock.now()\n const operation = route.name === undefined ? 'subscribe' : 'read'\n const control = new AbortController()\n const expires = Math.min(\n started + realtimeTiming.connectionDuration,\n token === undefined ? Infinity : token.expiresAt * 1000,\n )\n const stop = () => control.abort()\n request.signal.addEventListener('abort', stop, { once: true })\n if (request.signal.aborted) stop()\n const expiry = setTimeout(stop, Math.max(0, expires - realtimeClock.now()))\n const frames = sourceFrames(options.runlane, route, grant, control.signal)\n let pending: Promise<IteratorResult<Frame, void>> | undefined\n let cursor = route.after\n let closed = false\n const finish = () => {\n if (closed) return\n closed = true\n clearTimeout(expiry)\n request.signal.removeEventListener('abort', stop)\n control.abort()\n void frames.return().catch(() => undefined)\n recordRunlaneTelemetryMetric(options, {\n name: RunlaneMetricName.RealtimeConnectionDurationMs,\n value: Math.max(0, realtimeClock.now() - started),\n attributes: { [RunlaneMetricAttribute.Operation]: operation },\n })\n }\n const next = async (): Promise<Uint8Array | undefined> => {\n if (control.signal.aborted || realtimeClock.now() >= expires) return undefined\n pending ??= runWithRunlaneTelemetrySpan(\n options,\n { name: route.name === undefined ? RunlaneSpanName.RealtimeSubscribe : RunlaneSpanName.RealtimeRead },\n () => frames.next(),\n ).catch((cause: unknown) => {\n // Record the source failure before converting it to the public transport contract.\n const { cursor, ...error } = publicSourceError(cause)\n throw new RunlaneError({ ...error, cause, ...(cursor === undefined ? {} : { meta: { cursor } }) })\n })\n const timer = new AbortController()\n const abortTimer = () => timer.abort()\n control.signal.addEventListener('abort', abortTimer, { once: true })\n try {\n const result = await Promise.race([\n pending,\n waitForAbortableTimeout(realtimeTiming.heartbeatInterval, timer.signal).then(() => undefined),\n ])\n if (realtimeClock.now() >= expires) return undefined\n if (result === undefined) return encoder.encode(encode({ comment: 'heartbeat' }))\n pending = undefined\n if (result.done) return undefined\n const frame = result.value\n const bytes = encodeFrame(frame, cursor)\n cursor = frame.cursor ?? cursor\n return bytes\n } finally {\n timer.abort()\n control.signal.removeEventListener('abort', abortTimer)\n }\n }\n let first: Uint8Array | undefined\n try {\n first = await next()\n } catch (cause) {\n finish()\n throw cause\n }\n // No automatic prefetch: only a reader pull advances the core iterator.\n const body = new ReadableStream<Uint8Array>(\n {\n start(controller) {\n const end = () => {\n if (closed) return\n controller.close()\n finish()\n }\n control.signal.addEventListener('abort', end, { once: true })\n if (control.signal.aborted) end()\n },\n async pull(controller) {\n try {\n const bytes = control.signal.aborted || realtimeClock.now() >= expires ? undefined : (first ?? (await next()))\n if (closed) return\n first = undefined\n if (bytes === undefined) {\n controller.close()\n finish()\n return\n }\n controller.enqueue(bytes)\n } catch (cause) {\n if (closed) return\n controller.enqueue(\n encoder.encode(encode({ event: 'error', data: JSON.stringify(publicError(cause, cursor)) })),\n )\n controller.close()\n finish()\n }\n },\n cancel() {\n finish()\n },\n },\n { highWaterMark: 0 },\n )\n return new Response(body, {\n headers: {\n 'content-type': 'text/event-stream; charset=utf-8',\n 'cache-control': 'no-store',\n 'x-accel-buffering': 'no',\n },\n })\n}\n\nfunction encodeFrame(frame: Frame, cursor?: RunCursor): Uint8Array {\n const data = JSON.stringify(frame.data)\n const bytes = encoder.encode(\n encode({ event: frame.event, data, ...(frame.cursor === undefined ? {} : { id: frame.cursor }) }),\n )\n if (bytes.byteLength > realtimeDefaults.maxEventBytes)\n throw new RunlaneError({\n code: ErrorCode.EventTooLarge,\n message: 'Realtime event exceeds the serialized UTF-8 byte limit.',\n retryable: false,\n ...(cursor === undefined ? {} : { meta: { cursor } }),\n })\n return bytes\n}\n\nasync function* sourceFrames(\n source: RunlaneRealtimeSource,\n route: Route,\n grant: RunAccessGrant,\n signal: AbortSignal,\n): AsyncGenerator<Frame, void> {\n if (route.name === undefined) {\n for await (const event of source.runs.subscribe(route.runId, {\n after: route.after,\n signal,\n streams:\n route.streams === false || !grant.permissions.includes(RunReadPermission.Streams) ? false : grant.streams,\n })) {\n if (signal.aborted) return\n const projected = projectEvent(event, grant)\n yield projected === undefined\n ? { event: 'checkpoint', cursor: event.cursor, data: {} }\n : { event: projected.type, cursor: projected.cursor, data: projected }\n }\n if (!signal.aborted) yield { event: 'end', data: {} }\n return\n }\n yield* namedFrames(source, route.runId, route.name, route.after, signal)\n}\n\nasync function* namedFrames(\n source: RunlaneRealtimeSource,\n runId: RunId,\n name: string,\n initial: RunCursor | undefined,\n signal: AbortSignal,\n): AsyncGenerator<Frame, void> {\n let after = initial\n let terminal = false\n while (!signal.aborted) {\n const controller = new AbortController()\n const waiting = source.lane?.runSignal\n ?.wait({\n environment: source.environment,\n runId,\n signal: AbortSignal.any([signal, controller.signal]),\n timeoutMs: pollInterval,\n })\n .catch((cause: unknown) => {\n throw new RunlaneError({\n code: ErrorCode.AdapterContractViolation,\n message: 'Lane run signal wait must not reject.',\n cause,\n })\n })\n // A provider may reject before the page read finishes or while the consumer pauses.\n void waiting?.catch(() => undefined)\n try {\n const page = await source.runs.readStream(runId, name, { after, limit: contractDefaults.pagination.defaultLimit })\n yield* namedPageFrames(page, after)\n after = page.cursor\n if (page.hasMore) continue\n if (terminal && page.records.length === 0) {\n yield { event: 'end', data: {} }\n return\n }\n terminal = await readNamedRunTerminal(source, runId)\n if (terminal) continue\n if (page.records.length === 0) await (waiting ?? waitForAbortableTimeout(pollInterval, signal))\n } finally {\n controller.abort()\n await waiting\n }\n }\n}\n\nasync function readNamedRunTerminal(source: RunlaneRealtimeSource, runId: RunId): Promise<boolean> {\n const run = await source.runs.get(runId)\n if (run === undefined)\n throw new RunlaneError({\n code: ErrorCode.RunNotFound,\n message: 'Run was not found.',\n retryable: false,\n })\n return runStatusValues.isTerminal(run.status)\n}\n\nfunction* namedPageFrames(page: RunReadPage<RunStreamRecord>, after?: RunCursor): Generator<Frame, void> {\n for (const [index, record] of page.records.entries()) {\n const last = index === page.records.length - 1\n yield {\n event: 'stream',\n ...(last ? { cursor: page.cursor } : {}),\n data: { ...(last ? { cursor: page.cursor } : {}), record },\n }\n }\n if (page.records.length === 0 && page.cursor !== after) yield { event: 'checkpoint', cursor: page.cursor, data: {} }\n}\n\nfunction publicError(cause: unknown, cursor?: RunCursor): RealtimeErrorData {\n const checkpoint =\n cursor ?? (cause instanceof RunlaneError ? runCursorSchema.safeParse(cause.meta?.cursor).data : undefined)\n const resume = checkpoint === undefined ? {} : { cursor: checkpoint }\n if (cause instanceof RunlaneError) {\n const code = runRealtimeErrorCodeSchema.safeParse(cause.code)\n if (code.success && code.data !== ErrorCode.InternalError) {\n return { code: code.data, message: cause.message, retryable: cause.retryable, ...resume }\n }\n }\n return {\n code: ErrorCode.InternalError,\n message: 'Realtime request failed.',\n retryable: true,\n ...resume,\n }\n}\nfunction publicSourceError(cause: unknown): RealtimeErrorData {\n const error = publicError(cause)\n if (cause instanceof RunlaneError) {\n switch (cause.code) {\n case ErrorCode.ConfigurationInvalid:\n case ErrorCode.ValidationFailed:\n return { ...error, code: ErrorCode.ValidationFailed, message: 'Realtime request is invalid.', retryable: false }\n case ErrorCode.RunCursorInvalid:\n return { ...error, code: ErrorCode.RunCursorInvalid, message: 'Run cursor is invalid.', retryable: false }\n case ErrorCode.RunNotFound:\n return { ...error, code: ErrorCode.RunNotFound, message: 'Run was not found.', retryable: false }\n case ErrorCode.OperationCancelled:\n return {\n ...error,\n code: ErrorCode.Disconnected,\n message: 'Realtime runtime is closing; reconnect to resume.',\n retryable: true,\n }\n }\n }\n return error\n}\nfunction httpStatus(code: ErrorCode): number {\n if (code === ErrorCode.AccessDenied) return 403\n if (code === ErrorCode.RunNotFound) return 404\n if (code === ErrorCode.ValidationFailed || code === ErrorCode.RunCursorInvalid) return 400\n return 500\n}\nenum AccessDenialReason {\n ApplicationDenied = 'application_denied',\n AuthorizationHeader = 'authorization_header',\n VerificationUnconfigured = 'verification_unconfigured',\n TokenScope = 'token_scope',\n}\nconst accessDenialReasonSchema = z.enum(AccessDenialReason)\n\nfunction accessDenied(reason: AccessDenialReason = AccessDenialReason.ApplicationDenied): RunlaneError {\n return new RunlaneError({ code: ErrorCode.AccessDenied, message: 'Run access denied.', meta: { reason } })\n}\nfunction invalidRequest(message: string, cause?: unknown): RunlaneError {\n return new RunlaneError({ code: ErrorCode.ValidationFailed, message, retryable: false, cause })\n}\nfunction invalidCursor(): RunlaneError {\n return new RunlaneError({\n code: ErrorCode.RunCursorInvalid,\n message: 'Run cursor is invalid or conflicts with after.',\n retryable: false,\n })\n}\n\nasync function requestToken(\n request: Request,\n runId: RunId,\n options: RunlaneRealtimeOptions,\n verification: Parameters<typeof verifyRunToken>[1] | undefined,\n): Promise<VerifiedRunToken | undefined> {\n const supplied = request.headers.get('authorization')\n if (supplied === null) return undefined\n const match = /^Bearer ([a-z0-9_.-]+)$/i.exec(supplied)\n if (match?.[1] === undefined) throw accessDenied(AccessDenialReason.AuthorizationHeader)\n if (verification === undefined) throw accessDenied(AccessDenialReason.VerificationUnconfigured)\n const token = await verifyRunToken(\n match[1],\n verification,\n options,\n new URL(request.url).pathname.endsWith('/events') ? 'subscribe' : 'read',\n )\n if (token.runId !== runId || token.environment.name !== options.runlane.environment.name)\n throw accessDenied(AccessDenialReason.TokenScope)\n return token\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":["RunReadPermission","VerifiedRunToken","RunAccessGrant"],"sources":["../src/source.ts","../src/tokens.ts","../src/handler.ts"],"mappings":";;;;;UAaiB;WACN,aAAa;;WAEb,OAAO,KAAK;WACZ;IACP,IAAI,OAAO,QAAQ,QAAQ;IAC3B,UAAU,OAAO,OAAO,UAAU,sBAAsB,eAAe;IACvE,WAAW,OAAO,OAAO,cAAc,UAAU,iBAAiB,QAAQ,YAAY;;;;cCgB7E,4BAA0B,EAAA;;;;;GAUpC,EAAA,KAAA;;KAGS,8BAA8B,SAAS,EAAE,cAAc;cAE7D,0BAAwB,EAAA;;;;;;;;;;GAK5B,EAAA,KAAA;;KAEU,6BAA6B,SAAS,EAAE,aAAa;WACtD,YAAY;;;UCLN,+BAA+B;WACrC,SAAS;WACT,aAAa;aACX,SAAS;aACT,OAAO;aACP,QAAQC;QACb,UAAUC;WACP,SAAS;;;KAGR,2BAA2B,YAAY;;UAGlC;;WAEN,SAAS,SAAS,YAAY,QAAQ;;WAEtC,cAAc,SAAS,+BAA+B;;;wBAgCjD,sBAAsB,SAAS,yBAAyB"}
1
+ {"version":3,"file":"index.d.cts","names":["RunReadPermission","VerifiedRunToken","RunAccessGrant"],"sources":["../src/source.ts","../src/tokens.ts","../src/handler.ts"],"mappings":";;;;;UAaiB;WACN,aAAa;;WAEb,OAAO,KAAK;WACZ;IACP,IAAI,OAAO,QAAQ,QAAQ;IAC3B,UAAU,OAAO,OAAO,UAAU,sBAAsB,eAAe;IACvE,WAAW,OAAO,OAAO,cAAc,UAAU,iBAAiB,QAAQ,YAAY;;;;cCgB7E,4BAA0B,EAAA;;;;;GAUpC,EAAA,KAAA;;KAGS,8BAA8B,SAAS,EAAE,cAAc;cAE7D,0BAAwB,EAAA;;;;;;;;;;GAK5B,EAAA,KAAA;;KAEU,6BAA6B,SAAS,EAAE,aAAa;WACtD,YAAY;;;UCJN,+BAA+B;WACrC,SAAS;WACT,aAAa;aACX,SAAS;aACT,OAAO;aACP,QAAQC;QACb,UAAUC;WACP,SAAS;;;KAGR,2BAA2B,YAAY;;UAGlC;;WAEN,SAAS,SAAS,YAAY,QAAQ;;WAEtC,cAAc,SAAS,+BAA+B;;;wBAiCjD,sBAAsB,SAAS,yBAAyB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":["RunReadPermission","VerifiedRunToken","RunAccessGrant"],"sources":["../src/source.ts","../src/tokens.ts","../src/handler.ts"],"mappings":";;;;;UAaiB;WACN,aAAa;;WAEb,OAAO,KAAK;WACZ;IACP,IAAI,OAAO,QAAQ,QAAQ;IAC3B,UAAU,OAAO,OAAO,UAAU,sBAAsB,eAAe;IACvE,WAAW,OAAO,OAAO,cAAc,UAAU,iBAAiB,QAAQ,YAAY;;;;cCgB7E,4BAA0B,EAAA;;;;;GAUpC,EAAA,KAAA;;KAGS,8BAA8B,SAAS,EAAE,cAAc;cAE7D,0BAAwB,EAAA;;;;;;;;;;GAK5B,EAAA,KAAA;;KAEU,6BAA6B,SAAS,EAAE,aAAa;WACtD,YAAY;;;UCLN,+BAA+B;WACrC,SAAS;WACT,aAAa;aACX,SAAS;aACT,OAAO;aACP,QAAQC;QACb,UAAUC;WACP,SAAS;;;KAGR,2BAA2B,YAAY;;UAGlC;;WAEN,SAAS,SAAS,YAAY,QAAQ;;WAEtC,cAAc,SAAS,+BAA+B;;;wBAgCjD,sBAAsB,SAAS,yBAAyB"}
1
+ {"version":3,"file":"index.d.mts","names":["RunReadPermission","VerifiedRunToken","RunAccessGrant"],"sources":["../src/source.ts","../src/tokens.ts","../src/handler.ts"],"mappings":";;;;;UAaiB;WACN,aAAa;;WAEb,OAAO,KAAK;WACZ;IACP,IAAI,OAAO,QAAQ,QAAQ;IAC3B,UAAU,OAAO,OAAO,UAAU,sBAAsB,eAAe;IACvE,WAAW,OAAO,OAAO,cAAc,UAAU,iBAAiB,QAAQ,YAAY;;;;cCgB7E,4BAA0B,EAAA;;;;;GAUpC,EAAA,KAAA;;KAGS,8BAA8B,SAAS,EAAE,cAAc;cAE7D,0BAAwB,EAAA;;;;;;;;;;GAK5B,EAAA,KAAA;;KAEU,6BAA6B,SAAS,EAAE,aAAa;WACtD,YAAY;;;UCJN,+BAA+B;WACrC,SAAS;WACT,aAAa;aACX,SAAS;aACT,OAAO;aACP,QAAQC;QACb,UAAUC;WACP,SAAS;;;KAGR,2BAA2B,YAAY;;UAGlC;;WAEN,SAAS,SAAS,YAAY,QAAQ;;WAEtC,cAAc,SAAS,+BAA+B;;;wBAiCjD,sBAAsB,SAAS,yBAAyB"}