@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.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
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 zod = require("zod");
|
|
6
|
-
zod =
|
|
6
|
+
zod = require_wire.__toESM(zod, 1);
|
|
7
7
|
let eventsource_parser = require("eventsource-parser");
|
|
8
8
|
// src/connection-observer.ts
|
|
9
9
|
/** Owns notification ordering and preserves the first callback failure without recursive reporting. */
|
|
@@ -53,7 +53,7 @@ var ConnectionObserver = class {
|
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
55
|
// src/sse.ts
|
|
56
|
-
const encoder
|
|
56
|
+
const encoder = new TextEncoder();
|
|
57
57
|
/** Incremental UTF-8 SSE parser; incomplete trailing events are never dispatched. */
|
|
58
58
|
async function* readSse(body, signal) {
|
|
59
59
|
const reader = body.getReader();
|
|
@@ -74,7 +74,7 @@ async function* readSse(body, signal) {
|
|
|
74
74
|
};
|
|
75
75
|
},
|
|
76
76
|
onError(error) {
|
|
77
|
-
if (error.type === "max-buffer-size-exceeded") throw
|
|
77
|
+
if (error.type === "max-buffer-size-exceeded") throw require_wire.wireInvalid(error);
|
|
78
78
|
}
|
|
79
79
|
});
|
|
80
80
|
const abort = () => {
|
|
@@ -116,8 +116,8 @@ var FrameByteBudget = class {
|
|
|
116
116
|
this.resetAfterCr = false;
|
|
117
117
|
}
|
|
118
118
|
const point = char.codePointAt(0) ?? 0;
|
|
119
|
-
this.retained += point < 128 ? 1 : encoder
|
|
120
|
-
if (this.retained > _runlane_contracts_realtime.realtimeDefaults.maxEventBytes) throw
|
|
119
|
+
this.retained += point < 128 ? 1 : encoder.encode(char).byteLength;
|
|
120
|
+
if (this.retained > _runlane_contracts_realtime.realtimeDefaults.maxEventBytes) throw require_wire.wireInvalid();
|
|
121
121
|
if (this.skipLf && char === "\n") {
|
|
122
122
|
this.skipLf = false;
|
|
123
123
|
if (this.resetAfterCr) {
|
|
@@ -152,18 +152,7 @@ function decodeUtf8(decoder, bytes) {
|
|
|
152
152
|
try {
|
|
153
153
|
return bytes === void 0 ? decoder.decode() : decoder.decode(bytes, { stream: true });
|
|
154
154
|
} catch (cause) {
|
|
155
|
-
throw
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
// src/wire.ts
|
|
159
|
-
const encoder = new TextEncoder();
|
|
160
|
-
/** Parses JSON within the serialized UTF-8 event bound. Storage owns application value validation. */
|
|
161
|
-
function parseWireJson(text) {
|
|
162
|
-
try {
|
|
163
|
-
if (encoder.encode(text).byteLength > _runlane_contracts_realtime.realtimeDefaults.maxEventBytes) throw require_time.wireInvalid();
|
|
164
|
-
return JSON.parse(text);
|
|
165
|
-
} catch (cause) {
|
|
166
|
-
throw require_time.wireInvalid(cause);
|
|
155
|
+
throw require_wire.wireInvalid(cause);
|
|
167
156
|
}
|
|
168
157
|
}
|
|
169
158
|
// src/client.ts
|
|
@@ -182,7 +171,8 @@ const readerFields = {
|
|
|
182
171
|
const readerRequestSchema = zod.discriminatedUnion("kind", [zod.object({
|
|
183
172
|
...readerFields,
|
|
184
173
|
kind: zod.literal(_runlane_contracts_realtime.RunReaderKind.Subscription),
|
|
185
|
-
name: zod.undefined()
|
|
174
|
+
name: zod.undefined(),
|
|
175
|
+
streams: zod.literal(false).optional()
|
|
186
176
|
}), zod.object({
|
|
187
177
|
...readerFields,
|
|
188
178
|
kind: zod.literal(_runlane_contracts_realtime.RunReaderKind.Stream),
|
|
@@ -191,7 +181,7 @@ const readerRequestSchema = zod.discriminatedUnion("kind", [zod.object({
|
|
|
191
181
|
/** Creates a reusable authenticated streaming client with bounded parsing and reconnect delays. */
|
|
192
182
|
function createRunlaneClient(options) {
|
|
193
183
|
const checked = clientOptionsSchema.safeParse(options);
|
|
194
|
-
if (!checked.success) throw
|
|
184
|
+
if (!checked.success) throw require_wire.invalidOptions(checked.error, "client");
|
|
195
185
|
const base = checked.data.baseUrl;
|
|
196
186
|
const fetcher = options.fetch ?? globalThis.fetch;
|
|
197
187
|
async function* subscribe(runId, input) {
|
|
@@ -210,15 +200,17 @@ async function* connect(options, fetcher, base, runId, name, kind, input = {}) {
|
|
|
210
200
|
kind,
|
|
211
201
|
runId,
|
|
212
202
|
name,
|
|
213
|
-
after: input.after
|
|
203
|
+
after: input.after,
|
|
204
|
+
streams: input.streams
|
|
214
205
|
});
|
|
215
206
|
if (!checked.success) throw new _runlane_contracts_realtime.RunlaneError({
|
|
216
207
|
code: checked.error.issues[0]?.path[0] === "after" ? _runlane_contracts_realtime.ErrorCode.RunCursorInvalid : _runlane_contracts_realtime.ErrorCode.ValidationFailed,
|
|
217
|
-
message: "Run id, stream name, or after cursor is invalid.",
|
|
208
|
+
message: "Run id, stream name, stream selection, or after cursor is invalid.",
|
|
218
209
|
cause: checked.error
|
|
219
210
|
});
|
|
220
211
|
const request = checked.data;
|
|
221
212
|
const url = makeUrl(base, request.runId, request.name);
|
|
213
|
+
if (request.kind === _runlane_contracts_realtime.RunReaderKind.Subscription && request.streams === false) url.searchParams.set(require_wire.streamSelectionQuery.name, require_wire.streamSelectionQuery.none);
|
|
222
214
|
const signal = input.signal ?? new AbortController().signal;
|
|
223
215
|
const observer = new ConnectionObserver(input.onConnectionChange);
|
|
224
216
|
let failure;
|
|
@@ -262,7 +254,7 @@ var ReconnectState = class {
|
|
|
262
254
|
refreshAccessToken;
|
|
263
255
|
cursor;
|
|
264
256
|
records = /* @__PURE__ */ new Set();
|
|
265
|
-
delay =
|
|
257
|
+
delay = require_wire.realtimeTiming.clientReconnectDelay;
|
|
266
258
|
attempts = 0;
|
|
267
259
|
failures = 0;
|
|
268
260
|
openedAt;
|
|
@@ -309,8 +301,8 @@ var ReconnectState = class {
|
|
|
309
301
|
open() {
|
|
310
302
|
this.refreshedAfterDenial = false;
|
|
311
303
|
this.attempts = 0;
|
|
312
|
-
this.delay =
|
|
313
|
-
this.openedAt =
|
|
304
|
+
this.delay = require_wire.realtimeTiming.clientReconnectDelay;
|
|
305
|
+
this.openedAt = require_wire.realtimeClock.now();
|
|
314
306
|
}
|
|
315
307
|
deliverRecord(id) {
|
|
316
308
|
if (this.records.has(id)) return false;
|
|
@@ -324,11 +316,11 @@ var ReconnectState = class {
|
|
|
324
316
|
this.failures = 0;
|
|
325
317
|
this.lastFailure = void 0;
|
|
326
318
|
this.attempts = 0;
|
|
327
|
-
this.delay =
|
|
319
|
+
this.delay = require_wire.realtimeTiming.clientReconnectDelay;
|
|
328
320
|
}
|
|
329
321
|
schedule(signal, observer) {
|
|
330
322
|
if (signal.aborted) return void 0;
|
|
331
|
-
if (this.openedAt !== void 0 &&
|
|
323
|
+
if (this.openedAt !== void 0 && require_wire.realtimeClock.now() - this.openedAt >= require_wire.realtimeTiming.heartbeatInterval) {
|
|
332
324
|
this.failures = 0;
|
|
333
325
|
this.lastFailure = void 0;
|
|
334
326
|
}
|
|
@@ -341,7 +333,7 @@ var ReconnectState = class {
|
|
|
341
333
|
...this.cursor === void 0 ? {} : { meta: { cursor: this.cursor } }
|
|
342
334
|
});
|
|
343
335
|
const scheduled = observer.reconnect(signal, ++this.attempts, this.delay);
|
|
344
|
-
this.delay = Math.min(
|
|
336
|
+
this.delay = Math.min(require_wire.realtimeTiming.clientMaxReconnectDelay, this.delay * 2);
|
|
345
337
|
return scheduled;
|
|
346
338
|
}
|
|
347
339
|
};
|
|
@@ -376,7 +368,7 @@ async function requestStream(fetcher, url, retry, signal) {
|
|
|
376
368
|
if (retry.cursor !== void 0) headers.set("last-event-id", retry.cursor);
|
|
377
369
|
const response = await fetchResponse(fetcher, url, headers, signal, retry);
|
|
378
370
|
if (response === void 0) return void 0;
|
|
379
|
-
if (response.body === null || !/^text\/event-stream(?:\s*;|$)/i.test(response.headers.get("content-type") ?? "")) throw
|
|
371
|
+
if (response.body === null || !/^text\/event-stream(?:\s*;|$)/i.test(response.headers.get("content-type") ?? "")) throw require_wire.wireInvalid();
|
|
380
372
|
return response.body;
|
|
381
373
|
}
|
|
382
374
|
async function awaitToken(token, signal) {
|
|
@@ -424,10 +416,10 @@ async function fetchResponse(fetcher, url, headers, signal, retry) {
|
|
|
424
416
|
return response;
|
|
425
417
|
}
|
|
426
418
|
function decodeEvent(event, named) {
|
|
427
|
-
const raw = parseWireJson(event.data);
|
|
419
|
+
const raw = require_wire.parseWireJson(event.data);
|
|
428
420
|
if (event.event === "error") {
|
|
429
421
|
const result = _runlane_contracts_realtime.runRealtimeErrorSchema.safeParse(raw);
|
|
430
|
-
if (!result.success) throw
|
|
422
|
+
if (!result.success) throw require_wire.wireInvalid(result.error);
|
|
431
423
|
const { cursor, ...error } = result.data;
|
|
432
424
|
throw new _runlane_contracts_realtime.RunlaneError({
|
|
433
425
|
...error,
|
|
@@ -435,13 +427,13 @@ function decodeEvent(event, named) {
|
|
|
435
427
|
});
|
|
436
428
|
}
|
|
437
429
|
if (event.event === "end" || event.event === "checkpoint") {
|
|
438
|
-
if (!_runlane_contracts_realtime.runRealtimeControlSchema.safeParse(raw).success) throw
|
|
430
|
+
if (!_runlane_contracts_realtime.runRealtimeControlSchema.safeParse(raw).success) throw require_wire.wireInvalid();
|
|
439
431
|
if (event.event === "end") return {
|
|
440
432
|
kind: "control",
|
|
441
433
|
terminal: true
|
|
442
434
|
};
|
|
443
435
|
const cursor = _runlane_contracts_realtime.runCursorSchema.safeParse(event.id);
|
|
444
|
-
if (!cursor.success) throw
|
|
436
|
+
if (!cursor.success) throw require_wire.wireInvalid(cursor.error);
|
|
445
437
|
return {
|
|
446
438
|
kind: "control",
|
|
447
439
|
cursor: cursor.data
|
|
@@ -451,7 +443,7 @@ function decodeEvent(event, named) {
|
|
|
451
443
|
}
|
|
452
444
|
function decodeNamedEvent(event, raw) {
|
|
453
445
|
const result = _runlane_contracts_realtime.runStreamWireSchema.safeParse(raw);
|
|
454
|
-
if (!result.success || event.event !== "stream" || result.data.cursor !== void 0 && result.data.cursor !== event.id) throw
|
|
446
|
+
if (!result.success || event.event !== "stream" || result.data.cursor !== void 0 && result.data.cursor !== event.id) throw require_wire.wireInvalid();
|
|
455
447
|
return {
|
|
456
448
|
kind: _runlane_contracts_realtime.RunReaderKind.Stream,
|
|
457
449
|
value: result.data.record,
|
|
@@ -460,7 +452,7 @@ function decodeNamedEvent(event, raw) {
|
|
|
460
452
|
}
|
|
461
453
|
function decodeSubscriptionEvent(event, raw) {
|
|
462
454
|
const result = _runlane_contracts_realtime.runSubscriptionWireSchema.safeParse(raw);
|
|
463
|
-
if (!result.success || String(result.data.type) !== event.event || result.data.cursor !== event.id) throw
|
|
455
|
+
if (!result.success || String(result.data.type) !== event.event || result.data.cursor !== event.id) throw require_wire.wireInvalid();
|
|
464
456
|
return {
|
|
465
457
|
kind: _runlane_contracts_realtime.RunReaderKind.Subscription,
|
|
466
458
|
value: result.data,
|
|
@@ -469,7 +461,7 @@ function decodeSubscriptionEvent(event, raw) {
|
|
|
469
461
|
};
|
|
470
462
|
}
|
|
471
463
|
async function responseError(response, signal) {
|
|
472
|
-
if (response.body === null) throw
|
|
464
|
+
if (response.body === null) throw require_wire.wireInvalid();
|
|
473
465
|
const reader = response.body.getReader();
|
|
474
466
|
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
475
467
|
let text = "";
|
|
@@ -483,12 +475,12 @@ async function responseError(response, signal) {
|
|
|
483
475
|
const chunk = await reader.read();
|
|
484
476
|
if (chunk.done) break;
|
|
485
477
|
bytes += chunk.value.byteLength;
|
|
486
|
-
if (bytes > _runlane_contracts_realtime.realtimeDefaults.maxBufferedBytes) throw
|
|
478
|
+
if (bytes > _runlane_contracts_realtime.realtimeDefaults.maxBufferedBytes) throw require_wire.wireInvalid();
|
|
487
479
|
text += decoder.decode(chunk.value, { stream: true });
|
|
488
480
|
}
|
|
489
|
-
const raw = parseWireJson(text + decoder.decode());
|
|
481
|
+
const raw = require_wire.parseWireJson(text + decoder.decode());
|
|
490
482
|
const result = realtimeErrorEnvelopeSchema.safeParse(raw);
|
|
491
|
-
if (!result.success) throw
|
|
483
|
+
if (!result.success) throw require_wire.wireInvalid(result.error);
|
|
492
484
|
const { cursor, ...error } = result.data.error;
|
|
493
485
|
return new _runlane_contracts_realtime.RunlaneError({
|
|
494
486
|
...error,
|
package/dist/client.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.cjs","names":["RunlaneError","ErrorCode","getDownwardJitteredDelayMs","RealtimeConnectionState","RealtimeConnectionCloseReason","encoder","createParser","realtimeDefaults","wireInvalid","RunlaneError","ErrorCode","realtimeDefaults","wireInvalid","z","runRealtimeErrorSchema","createUrlSchema","createFunctionSchema","objectSurfaceSchema","createRunlaneIdSchema","runCursorSchema","RunReaderKind","runStreamNameSchema","invalidOptions","RunlaneError","ErrorCode","RealtimeConnectionState","RunlaneMetricName","RunlaneMetricAttribute","waitForAbortableTimeout","realtimeTiming","realtimeClock","realtimeDefaults","wireInvalid","runRealtimeControlSchema","runStreamWireSchema","runSubscriptionWireSchema","RunSubscriptionEventType","runStatusValues"],"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.cjs","names":["RunlaneError","ErrorCode","getDownwardJitteredDelayMs","RealtimeConnectionState","RealtimeConnectionCloseReason","createParser","realtimeDefaults","wireInvalid","RunlaneError","ErrorCode","z","runRealtimeErrorSchema","createUrlSchema","createFunctionSchema","objectSurfaceSchema","createRunlaneIdSchema","runCursorSchema","RunReaderKind","runStreamNameSchema","invalidOptions","RunlaneError","ErrorCode","streamSelectionQuery","RealtimeConnectionState","RunlaneMetricName","RunlaneMetricAttribute","waitForAbortableTimeout","realtimeTiming","realtimeClock","realtimeDefaults","wireInvalid","parseWireJson","runRealtimeControlSchema","runStreamWireSchema","runSubscriptionWireSchema","RunSubscriptionEventType","runStatusValues"],"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/client.d.cts
CHANGED
|
@@ -17,9 +17,13 @@ export type RunlaneClientReadOptions = Pick<RunSubscribeOptions, 'after' | 'sign
|
|
|
17
17
|
readonly accessToken?: string;
|
|
18
18
|
readonly onConnectionChange?: (change: RealtimeConnectionChange) => void;
|
|
19
19
|
};
|
|
20
|
+
/** Subscription controls; omit streams to read all streams permitted by the grant. */
|
|
21
|
+
export type RunlaneClientSubscribeOptions = RunlaneClientReadOptions & {
|
|
22
|
+
readonly streams?: false;
|
|
23
|
+
};
|
|
20
24
|
/** Pull-based readers validate input on the first next(), before effects. Task generics add types only; abort ends normally. */
|
|
21
25
|
export interface RunlaneClient {
|
|
22
|
-
subscribe<TTask = unknown>(runId: RunlaneIdInput, options?:
|
|
26
|
+
subscribe<TTask = unknown>(runId: RunlaneIdInput, options?: RunlaneClientSubscribeOptions): AsyncGenerator<RunSubscriptionEvent$1<TTask>, void>;
|
|
23
27
|
/** Supply TName explicitly for an exact chunk type when TTask declares several streams. */
|
|
24
28
|
readStream<TTask = unknown, TName extends RunStreamName$1<TTask> = RunStreamName$1<TTask>>(runId: RunlaneIdInput, name: TName, options?: RunlaneClientReadOptions): AsyncGenerator<RunStreamRecord$1<RunStreamPart$1<TTask, TName>>, void>;
|
|
25
29
|
}
|
package/dist/client.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.cts","names":["RunSubscriptionEvent","RunStreamName","RunStreamPart","RunStreamRecord"],"sources":["../src/client.ts"],"mappings":";;;iBA6CiB;WACN,OAAO;WACP,QAAQ;;;iBAIF,6BAA6B;WACnC;;WAEA,sBAAsB,SAAS,oBAAoB;WACnD,eAAe,WAAW;;;YAGzB,2BAA2B,KAAK;WACjC;WACA,sBAAsB,QAAQ;;;
|
|
1
|
+
{"version":3,"file":"client.d.cts","names":["RunSubscriptionEvent","RunStreamName","RunStreamPart","RunStreamRecord"],"sources":["../src/client.ts"],"mappings":";;;iBA6CiB;WACN,OAAO;WACP,QAAQ;;;iBAIF,6BAA6B;WACnC;;WAEA,sBAAsB,SAAS,oBAAoB;WACnD,eAAe,WAAW;;;YAGzB,2BAA2B,KAAK;WACjC;WACA,sBAAsB,QAAQ;;;YAG7B,gCAAgC;WACjC;;;iBAGM;EACf,UAAU,iBACR,OAAO,gBACP,UAAU,gCACT,eAAeA,uBAAqB;;EAEvC,WAAW,iBAAiB,cAAcC,gBAAc,SAASA,gBAAc,QAC7E,OAAO,gBACP,MAAM,OACN,UAAU,2BACT,eAAeE,kBAAgBD,gBAAc,OAAO;;;wBAmDzC,oBAAoB,SAAS,uBAAuB"}
|
package/dist/client.d.mts
CHANGED
|
@@ -17,9 +17,13 @@ export type RunlaneClientReadOptions = Pick<RunSubscribeOptions, 'after' | 'sign
|
|
|
17
17
|
readonly accessToken?: string;
|
|
18
18
|
readonly onConnectionChange?: (change: RealtimeConnectionChange) => void;
|
|
19
19
|
};
|
|
20
|
+
/** Subscription controls; omit streams to read all streams permitted by the grant. */
|
|
21
|
+
export type RunlaneClientSubscribeOptions = RunlaneClientReadOptions & {
|
|
22
|
+
readonly streams?: false;
|
|
23
|
+
};
|
|
20
24
|
/** Pull-based readers validate input on the first next(), before effects. Task generics add types only; abort ends normally. */
|
|
21
25
|
export interface RunlaneClient {
|
|
22
|
-
subscribe<TTask = unknown>(runId: RunlaneIdInput, options?:
|
|
26
|
+
subscribe<TTask = unknown>(runId: RunlaneIdInput, options?: RunlaneClientSubscribeOptions): AsyncGenerator<RunSubscriptionEvent$1<TTask>, void>;
|
|
23
27
|
/** Supply TName explicitly for an exact chunk type when TTask declares several streams. */
|
|
24
28
|
readStream<TTask = unknown, TName extends RunStreamName$1<TTask> = RunStreamName$1<TTask>>(runId: RunlaneIdInput, name: TName, options?: RunlaneClientReadOptions): AsyncGenerator<RunStreamRecord$1<RunStreamPart$1<TTask, TName>>, void>;
|
|
25
29
|
}
|
package/dist/client.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.mts","names":["RunSubscriptionEvent","RunStreamName","RunStreamPart","RunStreamRecord"],"sources":["../src/client.ts"],"mappings":";;;iBA6CiB;WACN,OAAO;WACP,QAAQ;;;iBAIF,6BAA6B;WACnC;;WAEA,sBAAsB,SAAS,oBAAoB;WACnD,eAAe,WAAW;;;YAGzB,2BAA2B,KAAK;WACjC;WACA,sBAAsB,QAAQ;;;
|
|
1
|
+
{"version":3,"file":"client.d.mts","names":["RunSubscriptionEvent","RunStreamName","RunStreamPart","RunStreamRecord"],"sources":["../src/client.ts"],"mappings":";;;iBA6CiB;WACN,OAAO;WACP,QAAQ;;;iBAIF,6BAA6B;WACnC;;WAEA,sBAAsB,SAAS,oBAAoB;WACnD,eAAe,WAAW;;;YAGzB,2BAA2B,KAAK;WACjC;WACA,sBAAsB,QAAQ;;;YAG7B,gCAAgC;WACjC;;;iBAGM;EACf,UAAU,iBACR,OAAO,gBACP,UAAU,gCACT,eAAeA,uBAAqB;;EAEvC,WAAW,iBAAiB,cAAcC,gBAAc,SAASA,gBAAc,QAC7E,OAAO,gBACP,MAAM,OACN,UAAU,2BACT,eAAeE,kBAAgBD,gBAAc,OAAO;;;wBAmDzC,oBAAoB,SAAS,uBAAuB"}
|
package/dist/client.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as
|
|
1
|
+
import { a as invalidOptions, i as realtimeTiming, n as streamSelectionQuery, o as wireInvalid, r as realtimeClock, t as parseWireJson } from "./wire-BEevjo-K.mjs";
|
|
2
2
|
import { ErrorCode, ErrorCode as ErrorCode$1, RealtimeConnectionCloseReason, RealtimeConnectionState, RunReaderKind, RunStreamRecordType, RunSubscriptionEventType, RunSubscriptionEventType as RunSubscriptionEventType$1, RunlaneError, RunlaneError as RunlaneError$1, RunlaneMetricAttribute, RunlaneMetricName, createRunlaneIdSchema, realtimeDefaults, recordRunlaneTelemetryMetric, runCursorSchema, runRealtimeControlSchema, runRealtimeErrorSchema, runStatusValues, runStreamNameSchema, runStreamWireSchema, runSubscriptionWireSchema } from "@runlane/contracts/realtime";
|
|
3
3
|
import { createFunctionSchema, createUrlSchema, getDownwardJitteredDelayMs, objectSurfaceSchema, waitForAbortableTimeout } from "@runlane/utilities/browser";
|
|
4
4
|
import * as z$1 from "zod";
|
|
@@ -51,7 +51,7 @@ var ConnectionObserver = class {
|
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
// src/sse.ts
|
|
54
|
-
const encoder
|
|
54
|
+
const encoder = new TextEncoder();
|
|
55
55
|
/** Incremental UTF-8 SSE parser; incomplete trailing events are never dispatched. */
|
|
56
56
|
async function* readSse(body, signal) {
|
|
57
57
|
const reader = body.getReader();
|
|
@@ -114,7 +114,7 @@ var FrameByteBudget = class {
|
|
|
114
114
|
this.resetAfterCr = false;
|
|
115
115
|
}
|
|
116
116
|
const point = char.codePointAt(0) ?? 0;
|
|
117
|
-
this.retained += point < 128 ? 1 : encoder
|
|
117
|
+
this.retained += point < 128 ? 1 : encoder.encode(char).byteLength;
|
|
118
118
|
if (this.retained > realtimeDefaults.maxEventBytes) throw wireInvalid();
|
|
119
119
|
if (this.skipLf && char === "\n") {
|
|
120
120
|
this.skipLf = false;
|
|
@@ -153,17 +153,6 @@ function decodeUtf8(decoder, bytes) {
|
|
|
153
153
|
throw wireInvalid(cause);
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
// src/wire.ts
|
|
157
|
-
const encoder = new TextEncoder();
|
|
158
|
-
/** Parses JSON within the serialized UTF-8 event bound. Storage owns application value validation. */
|
|
159
|
-
function parseWireJson(text) {
|
|
160
|
-
try {
|
|
161
|
-
if (encoder.encode(text).byteLength > realtimeDefaults.maxEventBytes) throw wireInvalid();
|
|
162
|
-
return JSON.parse(text);
|
|
163
|
-
} catch (cause) {
|
|
164
|
-
throw wireInvalid(cause);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
156
|
// src/client.ts
|
|
168
157
|
const realtimeErrorEnvelopeSchema = z$1.object({ error: runRealtimeErrorSchema });
|
|
169
158
|
const clientOptionsSchema = z$1.strictObject({
|
|
@@ -180,7 +169,8 @@ const readerFields = {
|
|
|
180
169
|
const readerRequestSchema = z$1.discriminatedUnion("kind", [z$1.object({
|
|
181
170
|
...readerFields,
|
|
182
171
|
kind: z$1.literal(RunReaderKind.Subscription),
|
|
183
|
-
name: z$1.undefined()
|
|
172
|
+
name: z$1.undefined(),
|
|
173
|
+
streams: z$1.literal(false).optional()
|
|
184
174
|
}), z$1.object({
|
|
185
175
|
...readerFields,
|
|
186
176
|
kind: z$1.literal(RunReaderKind.Stream),
|
|
@@ -208,15 +198,17 @@ async function* connect(options, fetcher, base, runId, name, kind, input = {}) {
|
|
|
208
198
|
kind,
|
|
209
199
|
runId,
|
|
210
200
|
name,
|
|
211
|
-
after: input.after
|
|
201
|
+
after: input.after,
|
|
202
|
+
streams: input.streams
|
|
212
203
|
});
|
|
213
204
|
if (!checked.success) throw new RunlaneError$1({
|
|
214
205
|
code: checked.error.issues[0]?.path[0] === "after" ? ErrorCode$1.RunCursorInvalid : ErrorCode$1.ValidationFailed,
|
|
215
|
-
message: "Run id, stream name, or after cursor is invalid.",
|
|
206
|
+
message: "Run id, stream name, stream selection, or after cursor is invalid.",
|
|
216
207
|
cause: checked.error
|
|
217
208
|
});
|
|
218
209
|
const request = checked.data;
|
|
219
210
|
const url = makeUrl(base, request.runId, request.name);
|
|
211
|
+
if (request.kind === RunReaderKind.Subscription && request.streams === false) url.searchParams.set(streamSelectionQuery.name, streamSelectionQuery.none);
|
|
220
212
|
const signal = input.signal ?? new AbortController().signal;
|
|
221
213
|
const observer = new ConnectionObserver(input.onConnectionChange);
|
|
222
214
|
let failure;
|