@runlane/react 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/index.cjs CHANGED
@@ -130,11 +130,14 @@ function createReaderStore(initial, read, options) {
130
130
  complete,
131
131
  update: (reduce) => {
132
132
  if (!signal.aborted) {
133
- pending = {
134
- ...pending,
135
- value: reduce(pending.value)
136
- };
137
- schedule();
133
+ const value = reduce(pending.value);
134
+ if (!Object.is(value, pending.value)) {
135
+ pending = {
136
+ ...pending,
137
+ value
138
+ };
139
+ schedule();
140
+ }
138
141
  }
139
142
  return pending.value;
140
143
  },
@@ -207,8 +210,7 @@ function reduceRun(state, event) {
207
210
  version: run.version,
208
211
  output: run.output,
209
212
  failure: run.failure,
210
- meta: run.meta,
211
- streams: {}
213
+ meta: run.meta
212
214
  };
213
215
  }
214
216
  if (state === void 0) return state;
@@ -230,13 +232,7 @@ function reduceRun(state, event) {
230
232
  ...event.patch
231
233
  }
232
234
  };
233
- case _runlane_contracts_realtime.RunSubscriptionEventType.Stream: return {
234
- ...state,
235
- streams: {
236
- ...state.streams,
237
- [event.record.name]: event.record
238
- }
239
- };
235
+ case _runlane_contracts_realtime.RunSubscriptionEventType.Stream: return state;
240
236
  }
241
237
  }
242
238
  // src/stream-state.ts
@@ -309,6 +305,7 @@ function useRun(runId, { client: supplied, enabled = true, accessToken, onComple
309
305
  const { value: run, error, isConnected, stop } = useReader((0, react.useMemo)(() => createReaderStore(void 0, async (options) => {
310
306
  if (!enabled || runId === void 0) return;
311
307
  for await (const event of client.subscribe(runId, {
308
+ streams: false,
312
309
  accessToken: latest.current.accessToken,
313
310
  signal: options.signal,
314
311
  onConnectionChange: options.onConnectionChange
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["useRef","createContext","useContext","RunlaneError","ErrorCode","useMemo","createRunlaneClient","createElement","RealtimeConnectionState","useCallback","useSyncExternalStore","RunlaneError","ErrorCode","RunSubscriptionEventType","RunStreamRecordType","contractDefaults","useMemo","runStatusValues","RunStreamRecordType","createDurationSchema","RunlaneError","ErrorCode"],"sources":["../src/latest.ts","../src/provider.ts","../src/reader-store.ts","../src/run-state.ts","../src/stream-state.ts","../src/index.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\n\n// Readers observe committed options without making callbacks part of their identity.\nexport function useLatest<T>(value: T) {\n const ref = useRef(value)\n useLayoutEffect(() => {\n ref.current = value\n })\n return ref\n}\n","import { createContext, createElement, useContext, useMemo, type ReactNode } from 'react'\n\nimport { ErrorCode, RunlaneError, type RunlaneIdInput, type RunStreamName } from '@runlane/contracts/realtime'\nimport {\n createRunlaneClient,\n type RunlaneClient,\n type RunlaneClientOptions,\n type RunlaneClientReadOptions,\n} from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\n\nconst ClientContext = createContext<RunlaneClient | undefined>(undefined)\n\nexport function useClient(client: RunlaneClient | undefined): RunlaneClient {\n const provided = useContext(ClientContext)\n const resolved = client ?? provided\n if (resolved === undefined)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Wrap the view in RunlaneProvider or pass a client option to the hook.',\n })\n return resolved\n}\n\n/** Shared client configuration. Each hook still owns an independent reader. */\nexport interface RunlaneProviderProps extends RunlaneClientOptions {\n readonly children?: ReactNode\n}\n\n/** Owns one client per base URL and fetch function; callback changes keep readers open. */\nexport function RunlaneProvider({ children, ...options }: RunlaneProviderProps) {\n const latest = useLatest(options)\n const { baseUrl, fetch } = options\n const client = useMemo(() => {\n let owned: RunlaneClient | undefined\n const refresh: NonNullable<RunlaneClientOptions['refreshAccessToken']> = (request) => {\n const callback = latest.current.refreshAccessToken\n if (callback === undefined)\n throw new RunlaneError({ code: ErrorCode.AccessDenied, message: 'No access token refresher is configured.' })\n return callback(request)\n }\n // Delay construction until a committed reader starts: relative URLs need the browser origin,\n // which is absent during server rendering. All readers share this same client.\n function getClient(): RunlaneClient {\n if (owned !== undefined) return owned\n let url: URL\n try {\n url = new URL(baseUrl, typeof location === 'undefined' ? undefined : location.href)\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'baseUrl must resolve to an HTTP or HTTPS URL.',\n cause,\n })\n }\n owned = createRunlaneClient({\n baseUrl: url.href,\n fetch,\n get refreshAccessToken() {\n return latest.current.refreshAccessToken === undefined ? undefined : refresh\n },\n get telemetry() {\n return latest.current.telemetry\n },\n get onTelemetryError() {\n return latest.current.onTelemetryError\n },\n })\n return owned\n }\n return {\n subscribe<TTask = undefined>(runId: RunlaneIdInput, input?: RunlaneClientReadOptions) {\n return getClient().subscribe<TTask>(runId, input)\n },\n readStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n input?: RunlaneClientReadOptions,\n ) {\n return getClient().readStream<TTask, TName>(runId, name, input)\n },\n } satisfies RunlaneClient\n }, [baseUrl, fetch, latest])\n return createElement(ClientContext.Provider, { value: client }, children)\n}\n","import { useCallback, useSyncExternalStore } from 'react'\n\nimport {\n ErrorCode,\n RealtimeConnectionState,\n RunlaneError,\n type RealtimeConnectionChange,\n} from '@runlane/contracts/realtime'\n\nimport { useLatest } from './latest.js'\n\n/** Cached immutable snapshot for one hook-owned external store. */\ninterface ReaderState<T> {\n readonly value: T\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n}\n\n// Each store belongs to one hook identity. Each subscription cycle has its own abort fence,\n// including Strict Mode's setup/cleanup/setup and Activity hide/show on the same store.\nexport function createReaderStore<T>(\n initial: T,\n read: (options: {\n signal: AbortSignal\n update: (reduce: (value: T) => T) => T\n complete: () => void\n flush: () => void\n onConnectionChange: (change: RealtimeConnectionChange) => void\n }) => Promise<void>,\n options: {\n throttle: () => number\n onComplete?: (value: T, error?: RunlaneError) => void\n onConnectionChange?: (change: RealtimeConnectionChange) => void\n },\n) {\n const serverState: ReaderState<T> = { value: initial, error: undefined, isConnected: false }\n let state = serverState\n let stopReader: (() => void) | undefined\n\n return {\n getSnapshot: () => state,\n getServerSnapshot: () => serverState,\n stop: () => stopReader?.(),\n subscribe: (notify: () => void) => {\n const controller = new AbortController()\n const { signal } = controller\n let pending = serverState\n let timer: ReturnType<typeof setTimeout> | undefined\n let completed = false\n state = serverState\n const flush = () => {\n clearTimeout(timer)\n timer = undefined\n if (signal.aborted || state === pending) return\n state = pending\n notify()\n }\n const schedule = () => {\n timer ??= setTimeout(flush, options.throttle())\n }\n const complete = (error?: RunlaneError) => {\n if (signal.aborted || completed) return\n completed = true\n flush()\n options.onComplete?.(pending.value, error)\n }\n const stop = () => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: false }\n flush()\n controller.abort()\n }\n stopReader = stop\n void read({\n signal,\n flush,\n complete,\n update: (reduce) => {\n if (!signal.aborted) {\n pending = { ...pending, value: reduce(pending.value) }\n schedule()\n }\n return pending.value\n },\n onConnectionChange: (change) => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: change.state === RealtimeConnectionState.Open }\n if (change.state === RealtimeConnectionState.Closed) flush()\n else schedule()\n options.onConnectionChange?.(change)\n },\n })\n .then(() => {\n if (signal.aborted) return\n if (pending.isConnected) pending = { ...pending, isConnected: false }\n flush()\n })\n .catch((cause: unknown) => {\n if (signal.aborted) return\n pending = {\n ...pending,\n isConnected: false,\n error: readerError(cause),\n }\n flush()\n try {\n complete(pending.error)\n } catch (cause) {\n pending = { ...pending, error: readerError(cause) }\n flush()\n }\n controller.abort()\n })\n return stop\n },\n }\n}\n\nexport function useReader<T>(store: ReturnType<typeof createReaderStore<T>>) {\n const latest = useLatest(store)\n const stop = useCallback(() => latest.current.stop(), [latest])\n return { ...useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot), stop }\n}\n\nfunction readerError(cause: unknown): RunlaneError {\n return cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Run reader failed.',\n cause,\n })\n}\n","import {\n RunSubscriptionEventType,\n type RunMeta,\n type RunOutput,\n type RunRecord,\n type RunStreamRecord,\n type RunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\n\n/** Read-only live projection. Only fields maintained by snapshot/deltas are exposed. */\nexport interface RunRealtimeState<TTask = undefined> {\n /** Public run identifier. */\n readonly id: RunRecord['id']\n /** Environment that owns the run. */\n readonly environment: RunRecord['environment']\n /** Registered task identifier. */\n readonly taskId: RunRecord['taskId']\n /** Latest delivered lifecycle status; terminal task failure is data. */\n readonly status: RunRecord['status']\n /** Number of started attempts, from the snapshot counters or status delta. */\n readonly attempts: RunRecord['counters']['attempts']\n /** Last delivered status or metadata event sequence, independent of stream positions. */\n readonly eventSequence: RunRecord['eventSequence']\n /** Starting code identity when permitted and available. */\n readonly version: RunRecord['version']\n /** Stored handler output (schema input) when permitted and available; independent of streamed chunks. */\n readonly output: RunOutput<TTask> | undefined\n /** Latest permitted task failure summary, cleared by a status delta without failure. */\n readonly failure: RunRecord['failure']\n /** Permitted metadata after applying top-level patches, including explicit null values. */\n readonly meta: RunMeta<TTask> | undefined\n /** Most recent envelope for each delivered stream name; use useRunStream for retained chunks. */\n readonly streams: Readonly<Record<string, RunStreamRecord>>\n}\n\nexport function reduceRun<TTask>(\n state: RunRealtimeState<TTask> | undefined,\n event: RunSubscriptionEvent<TTask>,\n): RunRealtimeState<TTask> | undefined\nexport function reduceRun(\n state: RunRealtimeState | undefined,\n event: RunSubscriptionEvent,\n): RunRealtimeState | undefined {\n if (event.type === RunSubscriptionEventType.Snapshot) {\n const { run } = event\n return {\n id: run.id,\n environment: run.environment,\n taskId: run.taskId,\n status: run.status,\n attempts: run.counters.attempts,\n eventSequence: run.eventSequence,\n version: run.version,\n output: run.output,\n failure: run.failure,\n meta: run.meta,\n streams: {},\n }\n }\n if (state === undefined) return state\n switch (event.type) {\n case RunSubscriptionEventType.Status:\n return {\n ...state,\n status: event.status,\n attempts: event.attempts,\n eventSequence: event.eventSequence,\n version: event.version,\n output: event.output,\n failure: event.failure,\n }\n case RunSubscriptionEventType.Meta:\n return { ...state, eventSequence: event.eventSequence, meta: { ...state.meta, ...event.patch } }\n case RunSubscriptionEventType.Stream:\n return { ...state, streams: { ...state.streams, [event.record.name]: event.record } }\n }\n}\n","import { RunStreamRecordType, type RunStreamCloseReason, type RunStreamRecord } from '@runlane/contracts/realtime'\n\n/** Whether a named reader retains the latest attempt or all attempts. */\nexport enum RunStreamMode {\n Latest = 'latest',\n All = 'all',\n}\n\n/** One retained attempt. Parts are already validated by the task on the server. */\nexport interface RunStreamAttempt<TPart> {\n readonly attempt: number\n readonly parts: readonly TPart[]\n readonly closeReason?: RunStreamCloseReason\n}\n\n/** Retained parts and sticky eviction state for one reader. */\nexport interface StreamState<TPart> {\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly truncated: boolean\n}\n\nexport function reduceStream<TPart>(\n state: StreamState<TPart>,\n record: RunStreamRecord<TPart>,\n mode: RunStreamMode,\n maxParts: number,\n): StreamState<TPart> {\n const attempts =\n mode === RunStreamMode.Latest && record.type === RunStreamRecordType.AttemptStarted ? [] : [...state.attempts]\n const previous = attempts.at(-1)\n const current = previous?.attempt === record.attempt ? previous : { attempt: record.attempt, parts: [] }\n if (current === previous) attempts.pop()\n switch (record.type) {\n case RunStreamRecordType.AttemptStarted:\n attempts.push(current)\n break\n case RunStreamRecordType.Chunk:\n attempts.push({ ...current, parts: [...current.parts, record.value] })\n break\n case RunStreamRecordType.AttemptClosed:\n attempts.push({ ...current, closeReason: record.reason })\n break\n }\n const evicted = evictParts(attempts, maxParts)\n return { attempts, truncated: state.truncated || evicted }\n}\n\nfunction evictParts<TPart>(attempts: RunStreamAttempt<TPart>[], maxParts: number): boolean {\n const excessAttempts = Math.max(0, attempts.length - maxParts)\n attempts.splice(0, excessAttempts)\n let excess = attempts.reduce((total, attempt) => total + attempt.parts.length, 0) - maxParts\n const evicted = excessAttempts > 0 || excess > 0\n while (excess > 0) {\n const oldest = attempts[0]\n if (oldest === undefined) break\n if (oldest.parts.length <= excess) {\n attempts.shift()\n excess -= oldest.parts.length\n } else {\n attempts[0] = { ...oldest, parts: oldest.parts.slice(excess) }\n excess = 0\n }\n }\n return evicted\n}\n","'use client'\n\nimport { useMemo } from 'react'\n\nimport {\n contractDefaults,\n createDurationSchema,\n ErrorCode,\n RunlaneError,\n runStatusValues,\n RunStreamRecordType,\n type DurationString,\n type RealtimeConnectionChange,\n type RunlaneIdInput,\n type RunStreamName,\n type RunStreamPart,\n} from '@runlane/contracts/realtime'\nimport { type RunlaneClient } from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\nimport { useClient } from './provider.js'\nimport { createReaderStore, useReader } from './reader-store.js'\nimport { reduceRun, type RunRealtimeState } from './run-state.js'\nimport { reduceStream, RunStreamMode, type RunStreamAttempt, type StreamState } from './stream-state.js'\n\nexport { RunStreamMode, type RunStreamAttempt } from './stream-state.js'\n\nexport { RunlaneProvider, type RunlaneProviderProps } from './provider.js'\n\nexport type { RunRealtimeState } from './run-state.js'\n\n/** One caller-owned client; disabling a hook opens no reader and clears its state. */\ninterface ReaderOptions {\n readonly client?: RunlaneClient\n readonly accessToken?: string\n readonly enabled?: boolean\n readonly throttle?: DurationString\n readonly onConnectionChange?: (change: RealtimeConnectionChange) => void\n}\n\n/** Run reader options. Callback, token, and throttle changes keep the reader open. */\nexport interface UseRunOptions<TTask = undefined> extends ReaderOptions {\n readonly onComplete?: (run: RunRealtimeState<TTask> | undefined, error?: RunlaneError) => void\n}\n\n/** Live run projection and transport state for this hook instance. */\nexport interface UseRunResult<TTask = undefined> {\n readonly stop: () => void\n readonly run: RunRealtimeState<TTask> | undefined\n readonly error: RunlaneError | undefined\n /** True only after the client's Open notification, until another connection state. */\n readonly isConnected: boolean\n}\n\n/** Subscribe to a snapshot and apply status, metadata, and stream deltas in delivery order. */\nexport function useRun<TTask = undefined>(\n runId: RunlaneIdInput | undefined,\n {\n client: supplied,\n enabled = true,\n accessToken,\n onComplete,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n }: UseRunOptions<TTask> = {},\n): UseRunResult<TTask> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, throttle, onComplete, onConnectionChange })\n const store = useMemo(\n () =>\n createReaderStore<RunRealtimeState<TTask> | undefined>(\n undefined,\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const event of client.subscribe<TTask>(runId, {\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n const run = options.update((state) => reduceRun(state, event))\n if (run !== undefined && runStatusValues.isTerminal(run.status)) options.complete()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onComplete: (run, error) => latest.current.onComplete?.(run, error),\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, client, enabled],\n )\n const { value: run, error, isConnected, stop } = useReader(store)\n return { run, error, isConnected, stop }\n}\n\n/** Named reader policy. Identity changes clear retained parts and attempt history. */\nexport interface UseRunStreamOptions<TPart> extends ReaderOptions {\n readonly onData?: (part: TPart) => void\n readonly mode?: RunStreamMode\n /** Total retained parts across attempts; positive and at most clientMaxPartsLimit. */\n readonly maxParts?: number\n}\n\n/** Bounded attempt history and reader state for this hook instance. */\nexport interface UseRunStreamResult<TPart> {\n readonly stop: () => void\n readonly parts: readonly TPart[]\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n /** Sticky after any part eviction in this reader. */\n readonly truncated: boolean\n}\n\n/** Read a task-declared stream. The server validates parts when writing them. */\nexport function useRunStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput | undefined,\n name: TName,\n {\n client: supplied,\n accessToken,\n onData,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n enabled = true,\n mode = RunStreamMode.Latest,\n maxParts = contractDefaults.realtime.clientMaxParts,\n }: UseRunStreamOptions<RunStreamPart<TTask, TName>> = {},\n): UseRunStreamResult<RunStreamPart<TTask, TName>> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, onData, onConnectionChange, throttle })\n validatePartLimit(maxParts)\n const store = useMemo(\n () =>\n createReaderStore<StreamState<RunStreamPart<TTask, TName>>>(\n { attempts: [], truncated: false },\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const record of client.readStream<TTask, TName>(runId, name, {\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n if (options.signal.aborted) return\n options.update((state) => reduceStream(state, record, mode, maxParts))\n if (record.type === RunStreamRecordType.Chunk) latest.current.onData?.(record.value)\n if (record.type === RunStreamRecordType.AttemptClosed) options.flush()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, name, client, enabled, mode, maxParts],\n )\n const { value, error, isConnected, stop } = useReader(store)\n return { ...value, parts: value.attempts.at(-1)?.parts ?? [], error, isConnected, stop }\n}\n\nexport type {\n RunTypes,\n RunOutput,\n RunMeta,\n RunStreams,\n RunStreamName,\n RunStreamPart,\n} from '@runlane/contracts/realtime'\n\nconst throttleSchema = createDurationSchema(\n 'throttle must be a positive DurationString that resolves to whole milliseconds.',\n)\n\nfunction useReaderOptions<T extends { readonly throttle: DurationString }>(options: T) {\n const result = throttleSchema.safeParse(options.throttle)\n if (!result.success)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Invalid render throttle.',\n cause: result.error,\n })\n return useLatest({ ...options, throttleMs: result.data.milliseconds })\n}\n\nfunction validatePartLimit(maxParts: number): void {\n if (!Number.isSafeInteger(maxParts) || maxParts < 1 || maxParts > contractDefaults.realtime.clientMaxPartsLimit)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'maxParts must be a positive safe integer no greater than realtime.clientMaxPartsLimit.',\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["useRef","createContext","useContext","RunlaneError","ErrorCode","useMemo","createRunlaneClient","createElement","RealtimeConnectionState","useCallback","useSyncExternalStore","RunlaneError","ErrorCode","RunSubscriptionEventType","RunStreamRecordType","contractDefaults","useMemo","runStatusValues","RunStreamRecordType","createDurationSchema","RunlaneError","ErrorCode"],"sources":["../src/latest.ts","../src/provider.ts","../src/reader-store.ts","../src/run-state.ts","../src/stream-state.ts","../src/index.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\n\n// Readers observe committed options without making callbacks part of their identity.\nexport function useLatest<T>(value: T) {\n const ref = useRef(value)\n useLayoutEffect(() => {\n ref.current = value\n })\n return ref\n}\n","import { createContext, createElement, useContext, useMemo, type ReactNode } from 'react'\n\nimport { ErrorCode, RunlaneError, type RunlaneIdInput, type RunStreamName } from '@runlane/contracts/realtime'\nimport {\n createRunlaneClient,\n type RunlaneClient,\n type RunlaneClientOptions,\n type RunlaneClientReadOptions,\n} from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\n\nconst ClientContext = createContext<RunlaneClient | undefined>(undefined)\n\nexport function useClient(client: RunlaneClient | undefined): RunlaneClient {\n const provided = useContext(ClientContext)\n const resolved = client ?? provided\n if (resolved === undefined)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Wrap the view in RunlaneProvider or pass a client option to the hook.',\n })\n return resolved\n}\n\n/** Shared client configuration. Each hook still owns an independent reader. */\nexport interface RunlaneProviderProps extends RunlaneClientOptions {\n readonly children?: ReactNode\n}\n\n/** Owns one client per base URL and fetch function; callback changes keep readers open. */\nexport function RunlaneProvider({ children, ...options }: RunlaneProviderProps) {\n const latest = useLatest(options)\n const { baseUrl, fetch } = options\n const client = useMemo(() => {\n let owned: RunlaneClient | undefined\n const refresh: NonNullable<RunlaneClientOptions['refreshAccessToken']> = (request) => {\n const callback = latest.current.refreshAccessToken\n if (callback === undefined)\n throw new RunlaneError({ code: ErrorCode.AccessDenied, message: 'No access token refresher is configured.' })\n return callback(request)\n }\n // Delay construction until a committed reader starts: relative URLs need the browser origin,\n // which is absent during server rendering. All readers share this same client.\n function getClient(): RunlaneClient {\n if (owned !== undefined) return owned\n let url: URL\n try {\n url = new URL(baseUrl, typeof location === 'undefined' ? undefined : location.href)\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'baseUrl must resolve to an HTTP or HTTPS URL.',\n cause,\n })\n }\n owned = createRunlaneClient({\n baseUrl: url.href,\n fetch,\n get refreshAccessToken() {\n return latest.current.refreshAccessToken === undefined ? undefined : refresh\n },\n get telemetry() {\n return latest.current.telemetry\n },\n get onTelemetryError() {\n return latest.current.onTelemetryError\n },\n })\n return owned\n }\n return {\n subscribe<TTask = undefined>(runId: RunlaneIdInput, input?: RunlaneClientReadOptions) {\n return getClient().subscribe<TTask>(runId, input)\n },\n readStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n input?: RunlaneClientReadOptions,\n ) {\n return getClient().readStream<TTask, TName>(runId, name, input)\n },\n } satisfies RunlaneClient\n }, [baseUrl, fetch, latest])\n return createElement(ClientContext.Provider, { value: client }, children)\n}\n","import { useCallback, useSyncExternalStore } from 'react'\n\nimport {\n ErrorCode,\n RealtimeConnectionState,\n RunlaneError,\n type RealtimeConnectionChange,\n} from '@runlane/contracts/realtime'\n\nimport { useLatest } from './latest.js'\n\n/** Cached immutable snapshot for one hook-owned external store. */\ninterface ReaderState<T> {\n readonly value: T\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n}\n\n// Each store belongs to one hook identity. Each subscription cycle has its own abort fence,\n// including Strict Mode's setup/cleanup/setup and Activity hide/show on the same store.\nexport function createReaderStore<T>(\n initial: T,\n read: (options: {\n signal: AbortSignal\n update: (reduce: (value: T) => T) => T\n complete: () => void\n flush: () => void\n onConnectionChange: (change: RealtimeConnectionChange) => void\n }) => Promise<void>,\n options: {\n throttle: () => number\n onComplete?: (value: T, error?: RunlaneError) => void\n onConnectionChange?: (change: RealtimeConnectionChange) => void\n },\n) {\n const serverState: ReaderState<T> = { value: initial, error: undefined, isConnected: false }\n let state = serverState\n let stopReader: (() => void) | undefined\n\n return {\n getSnapshot: () => state,\n getServerSnapshot: () => serverState,\n stop: () => stopReader?.(),\n subscribe: (notify: () => void) => {\n const controller = new AbortController()\n const { signal } = controller\n let pending = serverState\n let timer: ReturnType<typeof setTimeout> | undefined\n let completed = false\n state = serverState\n const flush = () => {\n clearTimeout(timer)\n timer = undefined\n if (signal.aborted || state === pending) return\n state = pending\n notify()\n }\n const schedule = () => {\n timer ??= setTimeout(flush, options.throttle())\n }\n const complete = (error?: RunlaneError) => {\n if (signal.aborted || completed) return\n completed = true\n flush()\n options.onComplete?.(pending.value, error)\n }\n const stop = () => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: false }\n flush()\n controller.abort()\n }\n stopReader = stop\n void read({\n signal,\n flush,\n complete,\n update: (reduce) => {\n if (!signal.aborted) {\n const value = reduce(pending.value)\n if (!Object.is(value, pending.value)) {\n pending = { ...pending, value }\n schedule()\n }\n }\n return pending.value\n },\n onConnectionChange: (change) => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: change.state === RealtimeConnectionState.Open }\n if (change.state === RealtimeConnectionState.Closed) flush()\n else schedule()\n options.onConnectionChange?.(change)\n },\n })\n .then(() => {\n if (signal.aborted) return\n if (pending.isConnected) pending = { ...pending, isConnected: false }\n flush()\n })\n .catch((cause: unknown) => {\n if (signal.aborted) return\n pending = {\n ...pending,\n isConnected: false,\n error: readerError(cause),\n }\n flush()\n try {\n complete(pending.error)\n } catch (cause) {\n pending = { ...pending, error: readerError(cause) }\n flush()\n }\n controller.abort()\n })\n return stop\n },\n }\n}\n\nexport function useReader<T>(store: ReturnType<typeof createReaderStore<T>>) {\n const latest = useLatest(store)\n const stop = useCallback(() => latest.current.stop(), [latest])\n return { ...useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot), stop }\n}\n\nfunction readerError(cause: unknown): RunlaneError {\n return cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Run reader failed.',\n cause,\n })\n}\n","import {\n RunSubscriptionEventType,\n type RunMeta,\n type RunOutput,\n type RunRecord,\n type RunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\n\n/** Read-only live projection. Only fields maintained by snapshot/deltas are exposed. */\nexport interface RunRealtimeState<TTask = undefined> {\n /** Public run identifier. */\n readonly id: RunRecord['id']\n /** Environment that owns the run. */\n readonly environment: RunRecord['environment']\n /** Registered task identifier. */\n readonly taskId: RunRecord['taskId']\n /** Latest delivered lifecycle status; terminal task failure is data. */\n readonly status: RunRecord['status']\n /** Number of started attempts, from the snapshot counters or status delta. */\n readonly attempts: RunRecord['counters']['attempts']\n /** Last delivered status or metadata event sequence, independent of stream positions. */\n readonly eventSequence: RunRecord['eventSequence']\n /** Starting code identity when permitted and available. */\n readonly version: RunRecord['version']\n /** Stored handler output (schema input) when permitted and available; independent of streamed chunks. */\n readonly output: RunOutput<TTask> | undefined\n /** Latest permitted task failure summary, cleared by a status delta without failure. */\n readonly failure: RunRecord['failure']\n /** Permitted metadata after applying top-level patches, including explicit null values. */\n readonly meta: RunMeta<TTask> | undefined\n}\n\nexport function reduceRun<TTask>(\n state: RunRealtimeState<TTask> | undefined,\n event: RunSubscriptionEvent<TTask>,\n): RunRealtimeState<TTask> | undefined\nexport function reduceRun(\n state: RunRealtimeState | undefined,\n event: RunSubscriptionEvent,\n): RunRealtimeState | undefined {\n if (event.type === RunSubscriptionEventType.Snapshot) {\n const { run } = event\n return {\n id: run.id,\n environment: run.environment,\n taskId: run.taskId,\n status: run.status,\n attempts: run.counters.attempts,\n eventSequence: run.eventSequence,\n version: run.version,\n output: run.output,\n failure: run.failure,\n meta: run.meta,\n }\n }\n if (state === undefined) return state\n switch (event.type) {\n case RunSubscriptionEventType.Status:\n return {\n ...state,\n status: event.status,\n attempts: event.attempts,\n eventSequence: event.eventSequence,\n version: event.version,\n output: event.output,\n failure: event.failure,\n }\n case RunSubscriptionEventType.Meta:\n return { ...state, eventSequence: event.eventSequence, meta: { ...state.meta, ...event.patch } }\n case RunSubscriptionEventType.Stream:\n return state\n }\n}\n","import { RunStreamRecordType, type RunStreamCloseReason, type RunStreamRecord } from '@runlane/contracts/realtime'\n\n/** Whether a named reader retains the latest attempt or all attempts. */\nexport enum RunStreamMode {\n Latest = 'latest',\n All = 'all',\n}\n\n/** One retained attempt. Parts are already validated by the task on the server. */\nexport interface RunStreamAttempt<TPart> {\n readonly attempt: number\n readonly parts: readonly TPart[]\n readonly closeReason?: RunStreamCloseReason\n}\n\n/** Retained parts and sticky eviction state for one reader. */\nexport interface StreamState<TPart> {\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly truncated: boolean\n}\n\nexport function reduceStream<TPart>(\n state: StreamState<TPart>,\n record: RunStreamRecord<TPart>,\n mode: RunStreamMode,\n maxParts: number,\n): StreamState<TPart> {\n const attempts =\n mode === RunStreamMode.Latest && record.type === RunStreamRecordType.AttemptStarted ? [] : [...state.attempts]\n const previous = attempts.at(-1)\n const current = previous?.attempt === record.attempt ? previous : { attempt: record.attempt, parts: [] }\n if (current === previous) attempts.pop()\n switch (record.type) {\n case RunStreamRecordType.AttemptStarted:\n attempts.push(current)\n break\n case RunStreamRecordType.Chunk:\n attempts.push({ ...current, parts: [...current.parts, record.value] })\n break\n case RunStreamRecordType.AttemptClosed:\n attempts.push({ ...current, closeReason: record.reason })\n break\n }\n const evicted = evictParts(attempts, maxParts)\n return { attempts, truncated: state.truncated || evicted }\n}\n\nfunction evictParts<TPart>(attempts: RunStreamAttempt<TPart>[], maxParts: number): boolean {\n const excessAttempts = Math.max(0, attempts.length - maxParts)\n attempts.splice(0, excessAttempts)\n let excess = attempts.reduce((total, attempt) => total + attempt.parts.length, 0) - maxParts\n const evicted = excessAttempts > 0 || excess > 0\n while (excess > 0) {\n const oldest = attempts[0]\n if (oldest === undefined) break\n if (oldest.parts.length <= excess) {\n attempts.shift()\n excess -= oldest.parts.length\n } else {\n attempts[0] = { ...oldest, parts: oldest.parts.slice(excess) }\n excess = 0\n }\n }\n return evicted\n}\n","'use client'\n\nimport { useMemo } from 'react'\n\nimport {\n contractDefaults,\n createDurationSchema,\n ErrorCode,\n RunlaneError,\n runStatusValues,\n RunStreamRecordType,\n type DurationString,\n type RealtimeConnectionChange,\n type RunlaneIdInput,\n type RunStreamName,\n type RunStreamPart,\n} from '@runlane/contracts/realtime'\nimport { type RunlaneClient } from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\nimport { useClient } from './provider.js'\nimport { createReaderStore, useReader } from './reader-store.js'\nimport { reduceRun, type RunRealtimeState } from './run-state.js'\nimport { reduceStream, RunStreamMode, type RunStreamAttempt, type StreamState } from './stream-state.js'\n\nexport { RunStreamMode, type RunStreamAttempt } from './stream-state.js'\n\nexport { RunlaneProvider, type RunlaneProviderProps } from './provider.js'\n\nexport type { RunRealtimeState } from './run-state.js'\n\n/** One caller-owned client; disabling a hook opens no reader and clears its state. */\ninterface ReaderOptions {\n readonly client?: RunlaneClient\n readonly accessToken?: string\n readonly enabled?: boolean\n readonly throttle?: DurationString\n readonly onConnectionChange?: (change: RealtimeConnectionChange) => void\n}\n\n/** Run reader options. Callback, token, and throttle changes keep the reader open. */\nexport interface UseRunOptions<TTask = undefined> extends ReaderOptions {\n readonly onComplete?: (run: RunRealtimeState<TTask> | undefined, error?: RunlaneError) => void\n}\n\n/** Live run projection and transport state for this hook instance. */\nexport interface UseRunResult<TTask = undefined> {\n readonly stop: () => void\n readonly run: RunRealtimeState<TTask> | undefined\n readonly error: RunlaneError | undefined\n /** True only after the client's Open notification, until another connection state. */\n readonly isConnected: boolean\n}\n\n/** Subscribe to a snapshot and apply status, metadata, and stream deltas in delivery order. */\nexport function useRun<TTask = undefined>(\n runId: RunlaneIdInput | undefined,\n {\n client: supplied,\n enabled = true,\n accessToken,\n onComplete,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n }: UseRunOptions<TTask> = {},\n): UseRunResult<TTask> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, throttle, onComplete, onConnectionChange })\n const store = useMemo(\n () =>\n createReaderStore<RunRealtimeState<TTask> | undefined>(\n undefined,\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const event of client.subscribe<TTask>(runId, {\n streams: false,\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n const run = options.update((state) => reduceRun(state, event))\n if (run !== undefined && runStatusValues.isTerminal(run.status)) options.complete()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onComplete: (run, error) => latest.current.onComplete?.(run, error),\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, client, enabled],\n )\n const { value: run, error, isConnected, stop } = useReader(store)\n return { run, error, isConnected, stop }\n}\n\n/** Named reader policy. Identity changes clear retained parts and attempt history. */\nexport interface UseRunStreamOptions<TPart> extends ReaderOptions {\n readonly onData?: (part: TPart) => void\n readonly mode?: RunStreamMode\n /** Total retained parts across attempts; positive and at most clientMaxPartsLimit. */\n readonly maxParts?: number\n}\n\n/** Bounded attempt history and reader state for this hook instance. */\nexport interface UseRunStreamResult<TPart> {\n readonly stop: () => void\n readonly parts: readonly TPart[]\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n /** Sticky after any part eviction in this reader. */\n readonly truncated: boolean\n}\n\n/** Read a task-declared stream. The server validates parts when writing them. */\nexport function useRunStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput | undefined,\n name: TName,\n {\n client: supplied,\n accessToken,\n onData,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n enabled = true,\n mode = RunStreamMode.Latest,\n maxParts = contractDefaults.realtime.clientMaxParts,\n }: UseRunStreamOptions<RunStreamPart<TTask, TName>> = {},\n): UseRunStreamResult<RunStreamPart<TTask, TName>> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, onData, onConnectionChange, throttle })\n validatePartLimit(maxParts)\n const store = useMemo(\n () =>\n createReaderStore<StreamState<RunStreamPart<TTask, TName>>>(\n { attempts: [], truncated: false },\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const record of client.readStream<TTask, TName>(runId, name, {\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n if (options.signal.aborted) return\n options.update((state) => reduceStream(state, record, mode, maxParts))\n if (record.type === RunStreamRecordType.Chunk) latest.current.onData?.(record.value)\n if (record.type === RunStreamRecordType.AttemptClosed) options.flush()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, name, client, enabled, mode, maxParts],\n )\n const { value, error, isConnected, stop } = useReader(store)\n return { ...value, parts: value.attempts.at(-1)?.parts ?? [], error, isConnected, stop }\n}\n\nexport type {\n RunTypes,\n RunOutput,\n RunMeta,\n RunStreams,\n RunStreamName,\n RunStreamPart,\n} from '@runlane/contracts/realtime'\n\nconst throttleSchema = createDurationSchema(\n 'throttle must be a positive DurationString that resolves to whole milliseconds.',\n)\n\nfunction useReaderOptions<T extends { readonly throttle: DurationString }>(options: T) {\n const result = throttleSchema.safeParse(options.throttle)\n if (!result.success)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Invalid render throttle.',\n cause: result.error,\n })\n return useLatest({ ...options, throttleMs: result.data.milliseconds })\n}\n\nfunction validatePartLimit(maxParts: number): void {\n if (!Number.isSafeInteger(maxParts) || maxParts < 1 || maxParts > contractDefaults.realtime.clientMaxPartsLimit)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'maxParts must be a positive safe integer no greater than realtime.clientMaxPartsLimit.',\n })\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { DurationString, RealtimeConnectionChange, RunMeta, RunMeta as RunMeta$1, RunOutput, RunOutput as RunOutput$1, RunRecord, RunStreamCloseReason, RunStreamName, RunStreamName as RunStreamName$1, RunStreamPart, RunStreamPart as RunStreamPart$1, RunStreamRecord, RunStreams, RunTypes, RunlaneError, RunlaneIdInput } from "@runlane/contracts/realtime";
1
+ import { DurationString, RealtimeConnectionChange, RunMeta, RunMeta as RunMeta$1, RunOutput, RunOutput as RunOutput$1, RunRecord, RunStreamCloseReason, RunStreamName, RunStreamName as RunStreamName$1, RunStreamPart, RunStreamPart as RunStreamPart$1, RunStreams, RunTypes, RunlaneError, RunlaneIdInput } from "@runlane/contracts/realtime";
2
2
  import { RunlaneClient, RunlaneClientOptions } from "@runlane/realtime/client";
3
3
  import { ReactNode } from "react";
4
4
  /** Read-only live projection. Only fields maintained by snapshot/deltas are exposed. */
@@ -23,8 +23,6 @@ interface RunRealtimeState<TTask = undefined> {
23
23
  readonly failure: RunRecord['failure'];
24
24
  /** Permitted metadata after applying top-level patches, including explicit null values. */
25
25
  readonly meta: RunMeta$1<TTask> | undefined;
26
- /** Most recent envelope for each delivered stream name; use useRunStream for retained chunks. */
27
- readonly streams: Readonly<Record<string, RunStreamRecord>>;
28
26
  }
29
27
  /** Whether a named reader retains the latest attempt or all attempts. */
30
28
  export declare enum RunStreamMode {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":["RunOutput","RunMeta","RunStreamName","RunStreamPart"],"sources":["../src/run-state.ts","../src/stream-state.ts","../src/provider.ts","../src/index.ts"],"mappings":";;;;UAUiB,iBAAiB;;WAEvB,IAAI;;WAEJ,aAAa;;WAEb,QAAQ;;WAER,QAAQ;;WAER,UAAU;;WAEV,eAAe;;WAEf,SAAS;;WAET,QAAQA,YAAU;;WAElB,SAAS;;WAET,MAAMC,UAAQ;;WAEd,SAAS,SAAS,eAAe;;;oBC7BhC;EACV;EACA;;;UAIe,iBAAiB;WACvB;WACA,gBAAgB;WAChB,cAAc;;;UCcR,6BAA6B;WACnC,WAAW;;;wBAIN,kBAAkB,aAAa,WAAW,uCAAoB,yCAAA,cAAA;;UCCpE;WACC,SAAS;WACT;WACA;WACA,WAAW;WACX,sBAAsB,QAAQ;;;iBAIxB,cAAc,2BAA2B;WAC/C,cAAc,KAAK,iBAAiB,oBAAoB,QAAQ;;;iBAI1D,aAAa;WACnB;WACA,KAAK,iBAAiB;WACtB,OAAO;;WAEP;;;wBAIK,OAAO,mBACrB,OAAO,8BAEL,QAAQ,UACR,SACA,aACA,YACA,oBACA,aACC,cAAc,SAChB,aAAa;;iBA+BC,oBAAoB,eAAe;WACzC,UAAU,MAAM;WAChB,OAAO;;WAEP;;;iBAIM,mBAAmB;WACzB;WACA,gBAAgB;WAChB,mBAAmB,iBAAiB;WACpC,OAAO;WACP;;WAEA;;;wBAIK,aAAa,mBAAmB,cAAcC,gBAAc,SAASA,gBAAc,QACjG,OAAO,4BACP,MAAM,SAEJ,QAAQ,UACR,aACA,QACA,oBACA,UACA,SACA,MACA,aACC,oBAAoBC,gBAAc,OAAO,UAC3C,mBAAmBA,gBAAc,OAAO"}
1
+ {"version":3,"file":"index.d.cts","names":["RunOutput","RunMeta","RunStreamName","RunStreamPart"],"sources":["../src/run-state.ts","../src/stream-state.ts","../src/provider.ts","../src/index.ts"],"mappings":";;;;UASiB,iBAAiB;;WAEvB,IAAI;;WAEJ,aAAa;;WAEb,QAAQ;;WAER,QAAQ;;WAER,UAAU;;WAEV,eAAe;;WAEf,SAAS;;WAET,QAAQA,YAAU;;WAElB,SAAS;;WAET,MAAMC,UAAQ;;;oBC1Bb;EACV;EACA;;;UAIe,iBAAiB;WACvB;WACA,gBAAgB;WAChB,cAAc;;;UCcR,6BAA6B;WACnC,WAAW;;;wBAIN,kBAAkB,aAAa,WAAW,uCAAoB,yCAAA,cAAA;;UCCpE;WACC,SAAS;WACT;WACA;WACA,WAAW;WACX,sBAAsB,QAAQ;;;iBAIxB,cAAc,2BAA2B;WAC/C,cAAc,KAAK,iBAAiB,oBAAoB,QAAQ;;;iBAI1D,aAAa;WACnB;WACA,KAAK,iBAAiB;WACtB,OAAO;;WAEP;;;wBAIK,OAAO,mBACrB,OAAO,8BAEL,QAAQ,UACR,SACA,aACA,YACA,oBACA,aACC,cAAc,SAChB,aAAa;;iBAgCC,oBAAoB,eAAe;WACzC,UAAU,MAAM;WAChB,OAAO;;WAEP;;;iBAIM,mBAAmB;WACzB;WACA,gBAAgB;WAChB,mBAAmB,iBAAiB;WACpC,OAAO;WACP;;WAEA;;;wBAIK,aAAa,mBAAmB,cAAcC,gBAAc,SAASA,gBAAc,QACjG,OAAO,4BACP,MAAM,SAEJ,QAAQ,UACR,aACA,QACA,oBACA,UACA,SACA,MACA,aACC,oBAAoBC,gBAAc,OAAO,UAC3C,mBAAmBA,gBAAc,OAAO"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from "react";
2
- import { DurationString, RealtimeConnectionChange, RunMeta, RunMeta as RunMeta$1, RunOutput, RunOutput as RunOutput$1, RunRecord, RunStreamCloseReason, RunStreamName, RunStreamName as RunStreamName$1, RunStreamPart, RunStreamPart as RunStreamPart$1, RunStreamRecord, RunStreams, RunTypes, RunlaneError, RunlaneIdInput } from "@runlane/contracts/realtime";
2
+ import { DurationString, RealtimeConnectionChange, RunMeta, RunMeta as RunMeta$1, RunOutput, RunOutput as RunOutput$1, RunRecord, RunStreamCloseReason, RunStreamName, RunStreamName as RunStreamName$1, RunStreamPart, RunStreamPart as RunStreamPart$1, RunStreams, RunTypes, RunlaneError, RunlaneIdInput } from "@runlane/contracts/realtime";
3
3
  import { RunlaneClient, RunlaneClientOptions } from "@runlane/realtime/client";
4
4
  /** Read-only live projection. Only fields maintained by snapshot/deltas are exposed. */
5
5
  interface RunRealtimeState<TTask = undefined> {
@@ -23,8 +23,6 @@ interface RunRealtimeState<TTask = undefined> {
23
23
  readonly failure: RunRecord['failure'];
24
24
  /** Permitted metadata after applying top-level patches, including explicit null values. */
25
25
  readonly meta: RunMeta$1<TTask> | undefined;
26
- /** Most recent envelope for each delivered stream name; use useRunStream for retained chunks. */
27
- readonly streams: Readonly<Record<string, RunStreamRecord>>;
28
26
  }
29
27
  /** Whether a named reader retains the latest attempt or all attempts. */
30
28
  export declare enum RunStreamMode {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":["RunOutput","RunMeta","RunStreamName","RunStreamPart"],"sources":["../src/run-state.ts","../src/stream-state.ts","../src/provider.ts","../src/index.ts"],"mappings":";;;;UAUiB,iBAAiB;;WAEvB,IAAI;;WAEJ,aAAa;;WAEb,QAAQ;;WAER,QAAQ;;WAER,UAAU;;WAEV,eAAe;;WAEf,SAAS;;WAET,QAAQA,YAAU;;WAElB,SAAS;;WAET,MAAMC,UAAQ;;WAEd,SAAS,SAAS,eAAe;;;oBC7BhC;EACV;EACA;;;UAIe,iBAAiB;WACvB;WACA,gBAAgB;WAChB,cAAc;;;UCcR,6BAA6B;WACnC,WAAW;;;wBAIN,kBAAkB,aAAa,WAAW,uCAAoB,yCAAA,cAAA;;UCCpE;WACC,SAAS;WACT;WACA;WACA,WAAW;WACX,sBAAsB,QAAQ;;;iBAIxB,cAAc,2BAA2B;WAC/C,cAAc,KAAK,iBAAiB,oBAAoB,QAAQ;;;iBAI1D,aAAa;WACnB;WACA,KAAK,iBAAiB;WACtB,OAAO;;WAEP;;;wBAIK,OAAO,mBACrB,OAAO,8BAEL,QAAQ,UACR,SACA,aACA,YACA,oBACA,aACC,cAAc,SAChB,aAAa;;iBA+BC,oBAAoB,eAAe;WACzC,UAAU,MAAM;WAChB,OAAO;;WAEP;;;iBAIM,mBAAmB;WACzB;WACA,gBAAgB;WAChB,mBAAmB,iBAAiB;WACpC,OAAO;WACP;;WAEA;;;wBAIK,aAAa,mBAAmB,cAAcC,gBAAc,SAASA,gBAAc,QACjG,OAAO,4BACP,MAAM,SAEJ,QAAQ,UACR,aACA,QACA,oBACA,UACA,SACA,MACA,aACC,oBAAoBC,gBAAc,OAAO,UAC3C,mBAAmBA,gBAAc,OAAO"}
1
+ {"version":3,"file":"index.d.mts","names":["RunOutput","RunMeta","RunStreamName","RunStreamPart"],"sources":["../src/run-state.ts","../src/stream-state.ts","../src/provider.ts","../src/index.ts"],"mappings":";;;;UASiB,iBAAiB;;WAEvB,IAAI;;WAEJ,aAAa;;WAEb,QAAQ;;WAER,QAAQ;;WAER,UAAU;;WAEV,eAAe;;WAEf,SAAS;;WAET,QAAQA,YAAU;;WAElB,SAAS;;WAET,MAAMC,UAAQ;;;oBC1Bb;EACV;EACA;;;UAIe,iBAAiB;WACvB;WACA,gBAAgB;WAChB,cAAc;;;UCcR,6BAA6B;WACnC,WAAW;;;wBAIN,kBAAkB,aAAa,WAAW,uCAAoB,yCAAA,cAAA;;UCCpE;WACC,SAAS;WACT;WACA;WACA,WAAW;WACX,sBAAsB,QAAQ;;;iBAIxB,cAAc,2BAA2B;WAC/C,cAAc,KAAK,iBAAiB,oBAAoB,QAAQ;;;iBAI1D,aAAa;WACnB;WACA,KAAK,iBAAiB;WACtB,OAAO;;WAEP;;;wBAIK,OAAO,mBACrB,OAAO,8BAEL,QAAQ,UACR,SACA,aACA,YACA,oBACA,aACC,cAAc,SAChB,aAAa;;iBAgCC,oBAAoB,eAAe;WACzC,UAAU,MAAM;WAChB,OAAO;;WAEP;;;iBAIM,mBAAmB;WACzB;WACA,gBAAgB;WAChB,mBAAmB,iBAAiB;WACpC,OAAO;WACP;;WAEA;;;wBAIK,aAAa,mBAAmB,cAAcC,gBAAc,SAASA,gBAAc,QACjG,OAAO,4BACP,MAAM,SAEJ,QAAQ,UACR,aACA,QACA,oBACA,UACA,SACA,MACA,aACC,oBAAoBC,gBAAc,OAAO,UAC3C,mBAAmBA,gBAAc,OAAO"}
package/dist/index.mjs CHANGED
@@ -129,11 +129,14 @@ function createReaderStore(initial, read, options) {
129
129
  complete,
130
130
  update: (reduce) => {
131
131
  if (!signal.aborted) {
132
- pending = {
133
- ...pending,
134
- value: reduce(pending.value)
135
- };
136
- schedule();
132
+ const value = reduce(pending.value);
133
+ if (!Object.is(value, pending.value)) {
134
+ pending = {
135
+ ...pending,
136
+ value
137
+ };
138
+ schedule();
139
+ }
137
140
  }
138
141
  return pending.value;
139
142
  },
@@ -206,8 +209,7 @@ function reduceRun(state, event) {
206
209
  version: run.version,
207
210
  output: run.output,
208
211
  failure: run.failure,
209
- meta: run.meta,
210
- streams: {}
212
+ meta: run.meta
211
213
  };
212
214
  }
213
215
  if (state === void 0) return state;
@@ -229,13 +231,7 @@ function reduceRun(state, event) {
229
231
  ...event.patch
230
232
  }
231
233
  };
232
- case RunSubscriptionEventType.Stream: return {
233
- ...state,
234
- streams: {
235
- ...state.streams,
236
- [event.record.name]: event.record
237
- }
238
- };
234
+ case RunSubscriptionEventType.Stream: return state;
239
235
  }
240
236
  }
241
237
  // src/stream-state.ts
@@ -308,6 +304,7 @@ function useRun(runId, { client: supplied, enabled = true, accessToken, onComple
308
304
  const { value: run, error, isConnected, stop } = useReader(useMemo(() => createReaderStore(void 0, async (options) => {
309
305
  if (!enabled || runId === void 0) return;
310
306
  for await (const event of client.subscribe(runId, {
307
+ streams: false,
311
308
  accessToken: latest.current.accessToken,
312
309
  signal: options.signal,
313
310
  onConnectionChange: options.onConnectionChange
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/latest.ts","../src/provider.ts","../src/reader-store.ts","../src/run-state.ts","../src/stream-state.ts","../src/index.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\n\n// Readers observe committed options without making callbacks part of their identity.\nexport function useLatest<T>(value: T) {\n const ref = useRef(value)\n useLayoutEffect(() => {\n ref.current = value\n })\n return ref\n}\n","import { createContext, createElement, useContext, useMemo, type ReactNode } from 'react'\n\nimport { ErrorCode, RunlaneError, type RunlaneIdInput, type RunStreamName } from '@runlane/contracts/realtime'\nimport {\n createRunlaneClient,\n type RunlaneClient,\n type RunlaneClientOptions,\n type RunlaneClientReadOptions,\n} from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\n\nconst ClientContext = createContext<RunlaneClient | undefined>(undefined)\n\nexport function useClient(client: RunlaneClient | undefined): RunlaneClient {\n const provided = useContext(ClientContext)\n const resolved = client ?? provided\n if (resolved === undefined)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Wrap the view in RunlaneProvider or pass a client option to the hook.',\n })\n return resolved\n}\n\n/** Shared client configuration. Each hook still owns an independent reader. */\nexport interface RunlaneProviderProps extends RunlaneClientOptions {\n readonly children?: ReactNode\n}\n\n/** Owns one client per base URL and fetch function; callback changes keep readers open. */\nexport function RunlaneProvider({ children, ...options }: RunlaneProviderProps) {\n const latest = useLatest(options)\n const { baseUrl, fetch } = options\n const client = useMemo(() => {\n let owned: RunlaneClient | undefined\n const refresh: NonNullable<RunlaneClientOptions['refreshAccessToken']> = (request) => {\n const callback = latest.current.refreshAccessToken\n if (callback === undefined)\n throw new RunlaneError({ code: ErrorCode.AccessDenied, message: 'No access token refresher is configured.' })\n return callback(request)\n }\n // Delay construction until a committed reader starts: relative URLs need the browser origin,\n // which is absent during server rendering. All readers share this same client.\n function getClient(): RunlaneClient {\n if (owned !== undefined) return owned\n let url: URL\n try {\n url = new URL(baseUrl, typeof location === 'undefined' ? undefined : location.href)\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'baseUrl must resolve to an HTTP or HTTPS URL.',\n cause,\n })\n }\n owned = createRunlaneClient({\n baseUrl: url.href,\n fetch,\n get refreshAccessToken() {\n return latest.current.refreshAccessToken === undefined ? undefined : refresh\n },\n get telemetry() {\n return latest.current.telemetry\n },\n get onTelemetryError() {\n return latest.current.onTelemetryError\n },\n })\n return owned\n }\n return {\n subscribe<TTask = undefined>(runId: RunlaneIdInput, input?: RunlaneClientReadOptions) {\n return getClient().subscribe<TTask>(runId, input)\n },\n readStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n input?: RunlaneClientReadOptions,\n ) {\n return getClient().readStream<TTask, TName>(runId, name, input)\n },\n } satisfies RunlaneClient\n }, [baseUrl, fetch, latest])\n return createElement(ClientContext.Provider, { value: client }, children)\n}\n","import { useCallback, useSyncExternalStore } from 'react'\n\nimport {\n ErrorCode,\n RealtimeConnectionState,\n RunlaneError,\n type RealtimeConnectionChange,\n} from '@runlane/contracts/realtime'\n\nimport { useLatest } from './latest.js'\n\n/** Cached immutable snapshot for one hook-owned external store. */\ninterface ReaderState<T> {\n readonly value: T\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n}\n\n// Each store belongs to one hook identity. Each subscription cycle has its own abort fence,\n// including Strict Mode's setup/cleanup/setup and Activity hide/show on the same store.\nexport function createReaderStore<T>(\n initial: T,\n read: (options: {\n signal: AbortSignal\n update: (reduce: (value: T) => T) => T\n complete: () => void\n flush: () => void\n onConnectionChange: (change: RealtimeConnectionChange) => void\n }) => Promise<void>,\n options: {\n throttle: () => number\n onComplete?: (value: T, error?: RunlaneError) => void\n onConnectionChange?: (change: RealtimeConnectionChange) => void\n },\n) {\n const serverState: ReaderState<T> = { value: initial, error: undefined, isConnected: false }\n let state = serverState\n let stopReader: (() => void) | undefined\n\n return {\n getSnapshot: () => state,\n getServerSnapshot: () => serverState,\n stop: () => stopReader?.(),\n subscribe: (notify: () => void) => {\n const controller = new AbortController()\n const { signal } = controller\n let pending = serverState\n let timer: ReturnType<typeof setTimeout> | undefined\n let completed = false\n state = serverState\n const flush = () => {\n clearTimeout(timer)\n timer = undefined\n if (signal.aborted || state === pending) return\n state = pending\n notify()\n }\n const schedule = () => {\n timer ??= setTimeout(flush, options.throttle())\n }\n const complete = (error?: RunlaneError) => {\n if (signal.aborted || completed) return\n completed = true\n flush()\n options.onComplete?.(pending.value, error)\n }\n const stop = () => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: false }\n flush()\n controller.abort()\n }\n stopReader = stop\n void read({\n signal,\n flush,\n complete,\n update: (reduce) => {\n if (!signal.aborted) {\n pending = { ...pending, value: reduce(pending.value) }\n schedule()\n }\n return pending.value\n },\n onConnectionChange: (change) => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: change.state === RealtimeConnectionState.Open }\n if (change.state === RealtimeConnectionState.Closed) flush()\n else schedule()\n options.onConnectionChange?.(change)\n },\n })\n .then(() => {\n if (signal.aborted) return\n if (pending.isConnected) pending = { ...pending, isConnected: false }\n flush()\n })\n .catch((cause: unknown) => {\n if (signal.aborted) return\n pending = {\n ...pending,\n isConnected: false,\n error: readerError(cause),\n }\n flush()\n try {\n complete(pending.error)\n } catch (cause) {\n pending = { ...pending, error: readerError(cause) }\n flush()\n }\n controller.abort()\n })\n return stop\n },\n }\n}\n\nexport function useReader<T>(store: ReturnType<typeof createReaderStore<T>>) {\n const latest = useLatest(store)\n const stop = useCallback(() => latest.current.stop(), [latest])\n return { ...useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot), stop }\n}\n\nfunction readerError(cause: unknown): RunlaneError {\n return cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Run reader failed.',\n cause,\n })\n}\n","import {\n RunSubscriptionEventType,\n type RunMeta,\n type RunOutput,\n type RunRecord,\n type RunStreamRecord,\n type RunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\n\n/** Read-only live projection. Only fields maintained by snapshot/deltas are exposed. */\nexport interface RunRealtimeState<TTask = undefined> {\n /** Public run identifier. */\n readonly id: RunRecord['id']\n /** Environment that owns the run. */\n readonly environment: RunRecord['environment']\n /** Registered task identifier. */\n readonly taskId: RunRecord['taskId']\n /** Latest delivered lifecycle status; terminal task failure is data. */\n readonly status: RunRecord['status']\n /** Number of started attempts, from the snapshot counters or status delta. */\n readonly attempts: RunRecord['counters']['attempts']\n /** Last delivered status or metadata event sequence, independent of stream positions. */\n readonly eventSequence: RunRecord['eventSequence']\n /** Starting code identity when permitted and available. */\n readonly version: RunRecord['version']\n /** Stored handler output (schema input) when permitted and available; independent of streamed chunks. */\n readonly output: RunOutput<TTask> | undefined\n /** Latest permitted task failure summary, cleared by a status delta without failure. */\n readonly failure: RunRecord['failure']\n /** Permitted metadata after applying top-level patches, including explicit null values. */\n readonly meta: RunMeta<TTask> | undefined\n /** Most recent envelope for each delivered stream name; use useRunStream for retained chunks. */\n readonly streams: Readonly<Record<string, RunStreamRecord>>\n}\n\nexport function reduceRun<TTask>(\n state: RunRealtimeState<TTask> | undefined,\n event: RunSubscriptionEvent<TTask>,\n): RunRealtimeState<TTask> | undefined\nexport function reduceRun(\n state: RunRealtimeState | undefined,\n event: RunSubscriptionEvent,\n): RunRealtimeState | undefined {\n if (event.type === RunSubscriptionEventType.Snapshot) {\n const { run } = event\n return {\n id: run.id,\n environment: run.environment,\n taskId: run.taskId,\n status: run.status,\n attempts: run.counters.attempts,\n eventSequence: run.eventSequence,\n version: run.version,\n output: run.output,\n failure: run.failure,\n meta: run.meta,\n streams: {},\n }\n }\n if (state === undefined) return state\n switch (event.type) {\n case RunSubscriptionEventType.Status:\n return {\n ...state,\n status: event.status,\n attempts: event.attempts,\n eventSequence: event.eventSequence,\n version: event.version,\n output: event.output,\n failure: event.failure,\n }\n case RunSubscriptionEventType.Meta:\n return { ...state, eventSequence: event.eventSequence, meta: { ...state.meta, ...event.patch } }\n case RunSubscriptionEventType.Stream:\n return { ...state, streams: { ...state.streams, [event.record.name]: event.record } }\n }\n}\n","import { RunStreamRecordType, type RunStreamCloseReason, type RunStreamRecord } from '@runlane/contracts/realtime'\n\n/** Whether a named reader retains the latest attempt or all attempts. */\nexport enum RunStreamMode {\n Latest = 'latest',\n All = 'all',\n}\n\n/** One retained attempt. Parts are already validated by the task on the server. */\nexport interface RunStreamAttempt<TPart> {\n readonly attempt: number\n readonly parts: readonly TPart[]\n readonly closeReason?: RunStreamCloseReason\n}\n\n/** Retained parts and sticky eviction state for one reader. */\nexport interface StreamState<TPart> {\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly truncated: boolean\n}\n\nexport function reduceStream<TPart>(\n state: StreamState<TPart>,\n record: RunStreamRecord<TPart>,\n mode: RunStreamMode,\n maxParts: number,\n): StreamState<TPart> {\n const attempts =\n mode === RunStreamMode.Latest && record.type === RunStreamRecordType.AttemptStarted ? [] : [...state.attempts]\n const previous = attempts.at(-1)\n const current = previous?.attempt === record.attempt ? previous : { attempt: record.attempt, parts: [] }\n if (current === previous) attempts.pop()\n switch (record.type) {\n case RunStreamRecordType.AttemptStarted:\n attempts.push(current)\n break\n case RunStreamRecordType.Chunk:\n attempts.push({ ...current, parts: [...current.parts, record.value] })\n break\n case RunStreamRecordType.AttemptClosed:\n attempts.push({ ...current, closeReason: record.reason })\n break\n }\n const evicted = evictParts(attempts, maxParts)\n return { attempts, truncated: state.truncated || evicted }\n}\n\nfunction evictParts<TPart>(attempts: RunStreamAttempt<TPart>[], maxParts: number): boolean {\n const excessAttempts = Math.max(0, attempts.length - maxParts)\n attempts.splice(0, excessAttempts)\n let excess = attempts.reduce((total, attempt) => total + attempt.parts.length, 0) - maxParts\n const evicted = excessAttempts > 0 || excess > 0\n while (excess > 0) {\n const oldest = attempts[0]\n if (oldest === undefined) break\n if (oldest.parts.length <= excess) {\n attempts.shift()\n excess -= oldest.parts.length\n } else {\n attempts[0] = { ...oldest, parts: oldest.parts.slice(excess) }\n excess = 0\n }\n }\n return evicted\n}\n","'use client'\n\nimport { useMemo } from 'react'\n\nimport {\n contractDefaults,\n createDurationSchema,\n ErrorCode,\n RunlaneError,\n runStatusValues,\n RunStreamRecordType,\n type DurationString,\n type RealtimeConnectionChange,\n type RunlaneIdInput,\n type RunStreamName,\n type RunStreamPart,\n} from '@runlane/contracts/realtime'\nimport { type RunlaneClient } from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\nimport { useClient } from './provider.js'\nimport { createReaderStore, useReader } from './reader-store.js'\nimport { reduceRun, type RunRealtimeState } from './run-state.js'\nimport { reduceStream, RunStreamMode, type RunStreamAttempt, type StreamState } from './stream-state.js'\n\nexport { RunStreamMode, type RunStreamAttempt } from './stream-state.js'\n\nexport { RunlaneProvider, type RunlaneProviderProps } from './provider.js'\n\nexport type { RunRealtimeState } from './run-state.js'\n\n/** One caller-owned client; disabling a hook opens no reader and clears its state. */\ninterface ReaderOptions {\n readonly client?: RunlaneClient\n readonly accessToken?: string\n readonly enabled?: boolean\n readonly throttle?: DurationString\n readonly onConnectionChange?: (change: RealtimeConnectionChange) => void\n}\n\n/** Run reader options. Callback, token, and throttle changes keep the reader open. */\nexport interface UseRunOptions<TTask = undefined> extends ReaderOptions {\n readonly onComplete?: (run: RunRealtimeState<TTask> | undefined, error?: RunlaneError) => void\n}\n\n/** Live run projection and transport state for this hook instance. */\nexport interface UseRunResult<TTask = undefined> {\n readonly stop: () => void\n readonly run: RunRealtimeState<TTask> | undefined\n readonly error: RunlaneError | undefined\n /** True only after the client's Open notification, until another connection state. */\n readonly isConnected: boolean\n}\n\n/** Subscribe to a snapshot and apply status, metadata, and stream deltas in delivery order. */\nexport function useRun<TTask = undefined>(\n runId: RunlaneIdInput | undefined,\n {\n client: supplied,\n enabled = true,\n accessToken,\n onComplete,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n }: UseRunOptions<TTask> = {},\n): UseRunResult<TTask> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, throttle, onComplete, onConnectionChange })\n const store = useMemo(\n () =>\n createReaderStore<RunRealtimeState<TTask> | undefined>(\n undefined,\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const event of client.subscribe<TTask>(runId, {\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n const run = options.update((state) => reduceRun(state, event))\n if (run !== undefined && runStatusValues.isTerminal(run.status)) options.complete()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onComplete: (run, error) => latest.current.onComplete?.(run, error),\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, client, enabled],\n )\n const { value: run, error, isConnected, stop } = useReader(store)\n return { run, error, isConnected, stop }\n}\n\n/** Named reader policy. Identity changes clear retained parts and attempt history. */\nexport interface UseRunStreamOptions<TPart> extends ReaderOptions {\n readonly onData?: (part: TPart) => void\n readonly mode?: RunStreamMode\n /** Total retained parts across attempts; positive and at most clientMaxPartsLimit. */\n readonly maxParts?: number\n}\n\n/** Bounded attempt history and reader state for this hook instance. */\nexport interface UseRunStreamResult<TPart> {\n readonly stop: () => void\n readonly parts: readonly TPart[]\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n /** Sticky after any part eviction in this reader. */\n readonly truncated: boolean\n}\n\n/** Read a task-declared stream. The server validates parts when writing them. */\nexport function useRunStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput | undefined,\n name: TName,\n {\n client: supplied,\n accessToken,\n onData,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n enabled = true,\n mode = RunStreamMode.Latest,\n maxParts = contractDefaults.realtime.clientMaxParts,\n }: UseRunStreamOptions<RunStreamPart<TTask, TName>> = {},\n): UseRunStreamResult<RunStreamPart<TTask, TName>> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, onData, onConnectionChange, throttle })\n validatePartLimit(maxParts)\n const store = useMemo(\n () =>\n createReaderStore<StreamState<RunStreamPart<TTask, TName>>>(\n { attempts: [], truncated: false },\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const record of client.readStream<TTask, TName>(runId, name, {\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n if (options.signal.aborted) return\n options.update((state) => reduceStream(state, record, mode, maxParts))\n if (record.type === RunStreamRecordType.Chunk) latest.current.onData?.(record.value)\n if (record.type === RunStreamRecordType.AttemptClosed) options.flush()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, name, client, enabled, mode, maxParts],\n )\n const { value, error, isConnected, stop } = useReader(store)\n return { ...value, parts: value.attempts.at(-1)?.parts ?? [], error, isConnected, stop }\n}\n\nexport type {\n RunTypes,\n RunOutput,\n RunMeta,\n RunStreams,\n RunStreamName,\n RunStreamPart,\n} from '@runlane/contracts/realtime'\n\nconst throttleSchema = createDurationSchema(\n 'throttle must be a positive DurationString that resolves to whole milliseconds.',\n)\n\nfunction useReaderOptions<T extends { readonly throttle: DurationString }>(options: T) {\n const result = throttleSchema.safeParse(options.throttle)\n if (!result.success)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Invalid render throttle.',\n cause: result.error,\n })\n return useLatest({ ...options, throttleMs: result.data.milliseconds })\n}\n\nfunction validatePartLimit(maxParts: number): void {\n if (!Number.isSafeInteger(maxParts) || maxParts < 1 || maxParts > contractDefaults.realtime.clientMaxPartsLimit)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'maxParts must be a positive safe integer no greater than realtime.clientMaxPartsLimit.',\n })\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/latest.ts","../src/provider.ts","../src/reader-store.ts","../src/run-state.ts","../src/stream-state.ts","../src/index.ts"],"sourcesContent":["import { useLayoutEffect, useRef } from 'react'\n\n// Readers observe committed options without making callbacks part of their identity.\nexport function useLatest<T>(value: T) {\n const ref = useRef(value)\n useLayoutEffect(() => {\n ref.current = value\n })\n return ref\n}\n","import { createContext, createElement, useContext, useMemo, type ReactNode } from 'react'\n\nimport { ErrorCode, RunlaneError, type RunlaneIdInput, type RunStreamName } from '@runlane/contracts/realtime'\nimport {\n createRunlaneClient,\n type RunlaneClient,\n type RunlaneClientOptions,\n type RunlaneClientReadOptions,\n} from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\n\nconst ClientContext = createContext<RunlaneClient | undefined>(undefined)\n\nexport function useClient(client: RunlaneClient | undefined): RunlaneClient {\n const provided = useContext(ClientContext)\n const resolved = client ?? provided\n if (resolved === undefined)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Wrap the view in RunlaneProvider or pass a client option to the hook.',\n })\n return resolved\n}\n\n/** Shared client configuration. Each hook still owns an independent reader. */\nexport interface RunlaneProviderProps extends RunlaneClientOptions {\n readonly children?: ReactNode\n}\n\n/** Owns one client per base URL and fetch function; callback changes keep readers open. */\nexport function RunlaneProvider({ children, ...options }: RunlaneProviderProps) {\n const latest = useLatest(options)\n const { baseUrl, fetch } = options\n const client = useMemo(() => {\n let owned: RunlaneClient | undefined\n const refresh: NonNullable<RunlaneClientOptions['refreshAccessToken']> = (request) => {\n const callback = latest.current.refreshAccessToken\n if (callback === undefined)\n throw new RunlaneError({ code: ErrorCode.AccessDenied, message: 'No access token refresher is configured.' })\n return callback(request)\n }\n // Delay construction until a committed reader starts: relative URLs need the browser origin,\n // which is absent during server rendering. All readers share this same client.\n function getClient(): RunlaneClient {\n if (owned !== undefined) return owned\n let url: URL\n try {\n url = new URL(baseUrl, typeof location === 'undefined' ? undefined : location.href)\n } catch (cause) {\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'baseUrl must resolve to an HTTP or HTTPS URL.',\n cause,\n })\n }\n owned = createRunlaneClient({\n baseUrl: url.href,\n fetch,\n get refreshAccessToken() {\n return latest.current.refreshAccessToken === undefined ? undefined : refresh\n },\n get telemetry() {\n return latest.current.telemetry\n },\n get onTelemetryError() {\n return latest.current.onTelemetryError\n },\n })\n return owned\n }\n return {\n subscribe<TTask = undefined>(runId: RunlaneIdInput, input?: RunlaneClientReadOptions) {\n return getClient().subscribe<TTask>(runId, input)\n },\n readStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput,\n name: TName,\n input?: RunlaneClientReadOptions,\n ) {\n return getClient().readStream<TTask, TName>(runId, name, input)\n },\n } satisfies RunlaneClient\n }, [baseUrl, fetch, latest])\n return createElement(ClientContext.Provider, { value: client }, children)\n}\n","import { useCallback, useSyncExternalStore } from 'react'\n\nimport {\n ErrorCode,\n RealtimeConnectionState,\n RunlaneError,\n type RealtimeConnectionChange,\n} from '@runlane/contracts/realtime'\n\nimport { useLatest } from './latest.js'\n\n/** Cached immutable snapshot for one hook-owned external store. */\ninterface ReaderState<T> {\n readonly value: T\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n}\n\n// Each store belongs to one hook identity. Each subscription cycle has its own abort fence,\n// including Strict Mode's setup/cleanup/setup and Activity hide/show on the same store.\nexport function createReaderStore<T>(\n initial: T,\n read: (options: {\n signal: AbortSignal\n update: (reduce: (value: T) => T) => T\n complete: () => void\n flush: () => void\n onConnectionChange: (change: RealtimeConnectionChange) => void\n }) => Promise<void>,\n options: {\n throttle: () => number\n onComplete?: (value: T, error?: RunlaneError) => void\n onConnectionChange?: (change: RealtimeConnectionChange) => void\n },\n) {\n const serverState: ReaderState<T> = { value: initial, error: undefined, isConnected: false }\n let state = serverState\n let stopReader: (() => void) | undefined\n\n return {\n getSnapshot: () => state,\n getServerSnapshot: () => serverState,\n stop: () => stopReader?.(),\n subscribe: (notify: () => void) => {\n const controller = new AbortController()\n const { signal } = controller\n let pending = serverState\n let timer: ReturnType<typeof setTimeout> | undefined\n let completed = false\n state = serverState\n const flush = () => {\n clearTimeout(timer)\n timer = undefined\n if (signal.aborted || state === pending) return\n state = pending\n notify()\n }\n const schedule = () => {\n timer ??= setTimeout(flush, options.throttle())\n }\n const complete = (error?: RunlaneError) => {\n if (signal.aborted || completed) return\n completed = true\n flush()\n options.onComplete?.(pending.value, error)\n }\n const stop = () => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: false }\n flush()\n controller.abort()\n }\n stopReader = stop\n void read({\n signal,\n flush,\n complete,\n update: (reduce) => {\n if (!signal.aborted) {\n const value = reduce(pending.value)\n if (!Object.is(value, pending.value)) {\n pending = { ...pending, value }\n schedule()\n }\n }\n return pending.value\n },\n onConnectionChange: (change) => {\n if (signal.aborted) return\n pending = { ...pending, isConnected: change.state === RealtimeConnectionState.Open }\n if (change.state === RealtimeConnectionState.Closed) flush()\n else schedule()\n options.onConnectionChange?.(change)\n },\n })\n .then(() => {\n if (signal.aborted) return\n if (pending.isConnected) pending = { ...pending, isConnected: false }\n flush()\n })\n .catch((cause: unknown) => {\n if (signal.aborted) return\n pending = {\n ...pending,\n isConnected: false,\n error: readerError(cause),\n }\n flush()\n try {\n complete(pending.error)\n } catch (cause) {\n pending = { ...pending, error: readerError(cause) }\n flush()\n }\n controller.abort()\n })\n return stop\n },\n }\n}\n\nexport function useReader<T>(store: ReturnType<typeof createReaderStore<T>>) {\n const latest = useLatest(store)\n const stop = useCallback(() => latest.current.stop(), [latest])\n return { ...useSyncExternalStore(store.subscribe, store.getSnapshot, store.getServerSnapshot), stop }\n}\n\nfunction readerError(cause: unknown): RunlaneError {\n return cause instanceof RunlaneError\n ? cause\n : new RunlaneError({\n code: ErrorCode.InternalError,\n message: 'Run reader failed.',\n cause,\n })\n}\n","import {\n RunSubscriptionEventType,\n type RunMeta,\n type RunOutput,\n type RunRecord,\n type RunSubscriptionEvent,\n} from '@runlane/contracts/realtime'\n\n/** Read-only live projection. Only fields maintained by snapshot/deltas are exposed. */\nexport interface RunRealtimeState<TTask = undefined> {\n /** Public run identifier. */\n readonly id: RunRecord['id']\n /** Environment that owns the run. */\n readonly environment: RunRecord['environment']\n /** Registered task identifier. */\n readonly taskId: RunRecord['taskId']\n /** Latest delivered lifecycle status; terminal task failure is data. */\n readonly status: RunRecord['status']\n /** Number of started attempts, from the snapshot counters or status delta. */\n readonly attempts: RunRecord['counters']['attempts']\n /** Last delivered status or metadata event sequence, independent of stream positions. */\n readonly eventSequence: RunRecord['eventSequence']\n /** Starting code identity when permitted and available. */\n readonly version: RunRecord['version']\n /** Stored handler output (schema input) when permitted and available; independent of streamed chunks. */\n readonly output: RunOutput<TTask> | undefined\n /** Latest permitted task failure summary, cleared by a status delta without failure. */\n readonly failure: RunRecord['failure']\n /** Permitted metadata after applying top-level patches, including explicit null values. */\n readonly meta: RunMeta<TTask> | undefined\n}\n\nexport function reduceRun<TTask>(\n state: RunRealtimeState<TTask> | undefined,\n event: RunSubscriptionEvent<TTask>,\n): RunRealtimeState<TTask> | undefined\nexport function reduceRun(\n state: RunRealtimeState | undefined,\n event: RunSubscriptionEvent,\n): RunRealtimeState | undefined {\n if (event.type === RunSubscriptionEventType.Snapshot) {\n const { run } = event\n return {\n id: run.id,\n environment: run.environment,\n taskId: run.taskId,\n status: run.status,\n attempts: run.counters.attempts,\n eventSequence: run.eventSequence,\n version: run.version,\n output: run.output,\n failure: run.failure,\n meta: run.meta,\n }\n }\n if (state === undefined) return state\n switch (event.type) {\n case RunSubscriptionEventType.Status:\n return {\n ...state,\n status: event.status,\n attempts: event.attempts,\n eventSequence: event.eventSequence,\n version: event.version,\n output: event.output,\n failure: event.failure,\n }\n case RunSubscriptionEventType.Meta:\n return { ...state, eventSequence: event.eventSequence, meta: { ...state.meta, ...event.patch } }\n case RunSubscriptionEventType.Stream:\n return state\n }\n}\n","import { RunStreamRecordType, type RunStreamCloseReason, type RunStreamRecord } from '@runlane/contracts/realtime'\n\n/** Whether a named reader retains the latest attempt or all attempts. */\nexport enum RunStreamMode {\n Latest = 'latest',\n All = 'all',\n}\n\n/** One retained attempt. Parts are already validated by the task on the server. */\nexport interface RunStreamAttempt<TPart> {\n readonly attempt: number\n readonly parts: readonly TPart[]\n readonly closeReason?: RunStreamCloseReason\n}\n\n/** Retained parts and sticky eviction state for one reader. */\nexport interface StreamState<TPart> {\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly truncated: boolean\n}\n\nexport function reduceStream<TPart>(\n state: StreamState<TPart>,\n record: RunStreamRecord<TPart>,\n mode: RunStreamMode,\n maxParts: number,\n): StreamState<TPart> {\n const attempts =\n mode === RunStreamMode.Latest && record.type === RunStreamRecordType.AttemptStarted ? [] : [...state.attempts]\n const previous = attempts.at(-1)\n const current = previous?.attempt === record.attempt ? previous : { attempt: record.attempt, parts: [] }\n if (current === previous) attempts.pop()\n switch (record.type) {\n case RunStreamRecordType.AttemptStarted:\n attempts.push(current)\n break\n case RunStreamRecordType.Chunk:\n attempts.push({ ...current, parts: [...current.parts, record.value] })\n break\n case RunStreamRecordType.AttemptClosed:\n attempts.push({ ...current, closeReason: record.reason })\n break\n }\n const evicted = evictParts(attempts, maxParts)\n return { attempts, truncated: state.truncated || evicted }\n}\n\nfunction evictParts<TPart>(attempts: RunStreamAttempt<TPart>[], maxParts: number): boolean {\n const excessAttempts = Math.max(0, attempts.length - maxParts)\n attempts.splice(0, excessAttempts)\n let excess = attempts.reduce((total, attempt) => total + attempt.parts.length, 0) - maxParts\n const evicted = excessAttempts > 0 || excess > 0\n while (excess > 0) {\n const oldest = attempts[0]\n if (oldest === undefined) break\n if (oldest.parts.length <= excess) {\n attempts.shift()\n excess -= oldest.parts.length\n } else {\n attempts[0] = { ...oldest, parts: oldest.parts.slice(excess) }\n excess = 0\n }\n }\n return evicted\n}\n","'use client'\n\nimport { useMemo } from 'react'\n\nimport {\n contractDefaults,\n createDurationSchema,\n ErrorCode,\n RunlaneError,\n runStatusValues,\n RunStreamRecordType,\n type DurationString,\n type RealtimeConnectionChange,\n type RunlaneIdInput,\n type RunStreamName,\n type RunStreamPart,\n} from '@runlane/contracts/realtime'\nimport { type RunlaneClient } from '@runlane/realtime/client'\n\nimport { useLatest } from './latest.js'\nimport { useClient } from './provider.js'\nimport { createReaderStore, useReader } from './reader-store.js'\nimport { reduceRun, type RunRealtimeState } from './run-state.js'\nimport { reduceStream, RunStreamMode, type RunStreamAttempt, type StreamState } from './stream-state.js'\n\nexport { RunStreamMode, type RunStreamAttempt } from './stream-state.js'\n\nexport { RunlaneProvider, type RunlaneProviderProps } from './provider.js'\n\nexport type { RunRealtimeState } from './run-state.js'\n\n/** One caller-owned client; disabling a hook opens no reader and clears its state. */\ninterface ReaderOptions {\n readonly client?: RunlaneClient\n readonly accessToken?: string\n readonly enabled?: boolean\n readonly throttle?: DurationString\n readonly onConnectionChange?: (change: RealtimeConnectionChange) => void\n}\n\n/** Run reader options. Callback, token, and throttle changes keep the reader open. */\nexport interface UseRunOptions<TTask = undefined> extends ReaderOptions {\n readonly onComplete?: (run: RunRealtimeState<TTask> | undefined, error?: RunlaneError) => void\n}\n\n/** Live run projection and transport state for this hook instance. */\nexport interface UseRunResult<TTask = undefined> {\n readonly stop: () => void\n readonly run: RunRealtimeState<TTask> | undefined\n readonly error: RunlaneError | undefined\n /** True only after the client's Open notification, until another connection state. */\n readonly isConnected: boolean\n}\n\n/** Subscribe to a snapshot and apply status, metadata, and stream deltas in delivery order. */\nexport function useRun<TTask = undefined>(\n runId: RunlaneIdInput | undefined,\n {\n client: supplied,\n enabled = true,\n accessToken,\n onComplete,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n }: UseRunOptions<TTask> = {},\n): UseRunResult<TTask> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, throttle, onComplete, onConnectionChange })\n const store = useMemo(\n () =>\n createReaderStore<RunRealtimeState<TTask> | undefined>(\n undefined,\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const event of client.subscribe<TTask>(runId, {\n streams: false,\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n const run = options.update((state) => reduceRun(state, event))\n if (run !== undefined && runStatusValues.isTerminal(run.status)) options.complete()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onComplete: (run, error) => latest.current.onComplete?.(run, error),\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, client, enabled],\n )\n const { value: run, error, isConnected, stop } = useReader(store)\n return { run, error, isConnected, stop }\n}\n\n/** Named reader policy. Identity changes clear retained parts and attempt history. */\nexport interface UseRunStreamOptions<TPart> extends ReaderOptions {\n readonly onData?: (part: TPart) => void\n readonly mode?: RunStreamMode\n /** Total retained parts across attempts; positive and at most clientMaxPartsLimit. */\n readonly maxParts?: number\n}\n\n/** Bounded attempt history and reader state for this hook instance. */\nexport interface UseRunStreamResult<TPart> {\n readonly stop: () => void\n readonly parts: readonly TPart[]\n readonly attempts: readonly RunStreamAttempt<TPart>[]\n readonly error: RunlaneError | undefined\n readonly isConnected: boolean\n /** Sticky after any part eviction in this reader. */\n readonly truncated: boolean\n}\n\n/** Read a task-declared stream. The server validates parts when writing them. */\nexport function useRunStream<TTask = undefined, TName extends RunStreamName<TTask> = RunStreamName<TTask>>(\n runId: RunlaneIdInput | undefined,\n name: TName,\n {\n client: supplied,\n accessToken,\n onData,\n onConnectionChange,\n throttle = contractDefaults.realtime.renderThrottle,\n enabled = true,\n mode = RunStreamMode.Latest,\n maxParts = contractDefaults.realtime.clientMaxParts,\n }: UseRunStreamOptions<RunStreamPart<TTask, TName>> = {},\n): UseRunStreamResult<RunStreamPart<TTask, TName>> {\n const client = useClient(supplied)\n const latest = useReaderOptions({ accessToken, onData, onConnectionChange, throttle })\n validatePartLimit(maxParts)\n const store = useMemo(\n () =>\n createReaderStore<StreamState<RunStreamPart<TTask, TName>>>(\n { attempts: [], truncated: false },\n async (options) => {\n if (!enabled || runId === undefined) return\n for await (const record of client.readStream<TTask, TName>(runId, name, {\n accessToken: latest.current.accessToken,\n signal: options.signal,\n onConnectionChange: options.onConnectionChange,\n })) {\n if (options.signal.aborted) return\n options.update((state) => reduceStream(state, record, mode, maxParts))\n if (record.type === RunStreamRecordType.Chunk) latest.current.onData?.(record.value)\n if (record.type === RunStreamRecordType.AttemptClosed) options.flush()\n }\n },\n {\n throttle: () => latest.current.throttleMs,\n onConnectionChange: (change) => latest.current.onConnectionChange?.(change),\n },\n ),\n [runId, name, client, enabled, mode, maxParts],\n )\n const { value, error, isConnected, stop } = useReader(store)\n return { ...value, parts: value.attempts.at(-1)?.parts ?? [], error, isConnected, stop }\n}\n\nexport type {\n RunTypes,\n RunOutput,\n RunMeta,\n RunStreams,\n RunStreamName,\n RunStreamPart,\n} from '@runlane/contracts/realtime'\n\nconst throttleSchema = createDurationSchema(\n 'throttle must be a positive DurationString that resolves to whole milliseconds.',\n)\n\nfunction useReaderOptions<T extends { readonly throttle: DurationString }>(options: T) {\n const result = throttleSchema.safeParse(options.throttle)\n if (!result.success)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'Invalid render throttle.',\n cause: result.error,\n })\n return useLatest({ ...options, throttleMs: result.data.milliseconds })\n}\n\nfunction validatePartLimit(maxParts: number): void {\n if (!Number.isSafeInteger(maxParts) || maxParts < 1 || maxParts > contractDefaults.realtime.clientMaxPartsLimit)\n throw new RunlaneError({\n code: ErrorCode.ConfigurationInvalid,\n message: 'maxParts must be a positive safe integer no greater than realtime.clientMaxPartsLimit.',\n })\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runlane/react",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "React hooks for live run state and validated streams.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -44,8 +44,8 @@
44
44
  "access": "public"
45
45
  },
46
46
  "dependencies": {
47
- "@runlane/contracts": "0.5.0",
48
- "@runlane/realtime": "0.5.0"
47
+ "@runlane/contracts": "0.5.1",
48
+ "@runlane/realtime": "0.5.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "react": "^19.3.0",
@@ -60,8 +60,8 @@
60
60
  "@repo/tsdown": "0.0.0",
61
61
  "@repo/typescript": "0.0.0",
62
62
  "@repo/vitest": "0.0.0",
63
- "@runlane/core": "0.5.0",
64
- "@runlane/lane-local": "0.5.0"
63
+ "@runlane/core": "0.5.1",
64
+ "@runlane/lane-local": "0.5.1"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "react": "^19.0.0"