@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.
- package/dist/client.cjs +31 -39
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +5 -1
- package/dist/client.d.cts.map +1 -1
- package/dist/client.d.mts +5 -1
- package/dist/client.d.mts.map +1 -1
- package/dist/client.mjs +9 -17
- package/dist/client.mjs.map +1 -1
- package/dist/index.cjs +22 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +6 -3
- package/dist/index.mjs.map +1 -1
- package/dist/{time-sHQYGDPD.mjs → wire-BEevjo-K.mjs} +18 -2
- package/dist/wire-BEevjo-K.mjs.map +1 -0
- package/dist/{time-BhORZuav.cjs → wire-xCr4QuVn.cjs} +29 -1
- package/dist/wire-xCr4QuVn.cjs.map +1 -0
- package/package.json +7 -7
- package/dist/time-BhORZuav.cjs.map +0 -1
- package/dist/time-sHQYGDPD.mjs.map +0 -1
package/dist/client.mjs.map
CHANGED
|
@@ -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
|
|
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 =
|
|
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(
|
|
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 =
|
|
122
|
-
if (ttl < 1e3 || ttl >
|
|
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(
|
|
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 +
|
|
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 >
|
|
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
|
|
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
|
|
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 =
|
|
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 +
|
|
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 -
|
|
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,
|
|
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 ||
|
|
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)(
|
|
395
|
-
if (
|
|
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 ||
|
|
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
|
|
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);
|
package/dist/index.cjs.map
CHANGED
|
@@ -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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"}
|
package/dist/index.d.cts.map
CHANGED
|
@@ -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;;;
|
|
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"}
|
package/dist/index.d.mts.map
CHANGED
|
@@ -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;;;
|
|
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"}
|