@coji/durably-react 0.6.0 → 0.6.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.
@@ -0,0 +1,136 @@
1
+ // src/shared/create-log-entry.ts
2
+ function createLogEntry(params) {
3
+ return {
4
+ id: crypto.randomUUID(),
5
+ runId: params.runId,
6
+ stepName: params.stepName,
7
+ level: params.level,
8
+ message: params.message,
9
+ data: params.data,
10
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
11
+ };
12
+ }
13
+ function appendLog(logs, newLog, maxLogs) {
14
+ const newLogs = [...logs, newLog];
15
+ if (maxLogs > 0 && newLogs.length > maxLogs) {
16
+ return newLogs.slice(-maxLogs);
17
+ }
18
+ return newLogs;
19
+ }
20
+
21
+ // src/shared/subscription-reducer.ts
22
+ var initialSubscriptionState = {
23
+ status: null,
24
+ output: null,
25
+ error: null,
26
+ logs: [],
27
+ progress: null
28
+ };
29
+ function subscriptionReducer(state, action) {
30
+ switch (action.type) {
31
+ case "run:start":
32
+ return { ...state, status: "running" };
33
+ case "run:complete":
34
+ return { ...state, status: "completed", output: action.output };
35
+ case "run:fail":
36
+ return { ...state, status: "failed", error: action.error };
37
+ case "run:cancel":
38
+ return { ...state, status: "cancelled" };
39
+ case "run:retry":
40
+ return { ...state, status: "pending", error: null };
41
+ case "run:progress":
42
+ return { ...state, progress: action.progress };
43
+ case "log:write": {
44
+ const newLog = createLogEntry({
45
+ runId: action.runId,
46
+ stepName: action.stepName,
47
+ level: action.level,
48
+ message: action.message,
49
+ data: action.data
50
+ });
51
+ return { ...state, logs: appendLog(state.logs, newLog, action.maxLogs) };
52
+ }
53
+ case "reset":
54
+ return initialSubscriptionState;
55
+ case "clear_logs":
56
+ return { ...state, logs: [] };
57
+ case "connection_error":
58
+ return { ...state, error: action.error };
59
+ default:
60
+ return state;
61
+ }
62
+ }
63
+
64
+ // src/shared/use-subscription.ts
65
+ import { useCallback, useEffect, useReducer, useRef } from "react";
66
+ function useSubscription(subscriber, runId, options) {
67
+ const [state, dispatch] = useReducer(
68
+ subscriptionReducer,
69
+ initialSubscriptionState
70
+ );
71
+ const runIdRef = useRef(runId);
72
+ const prevRunIdRef = useRef(null);
73
+ const maxLogs = options?.maxLogs ?? 0;
74
+ if (prevRunIdRef.current !== runId) {
75
+ prevRunIdRef.current = runId;
76
+ if (runIdRef.current !== runId) {
77
+ dispatch({ type: "reset" });
78
+ }
79
+ }
80
+ runIdRef.current = runId;
81
+ useEffect(() => {
82
+ if (!subscriber || !runId) return;
83
+ const unsubscribe = subscriber.subscribe(runId, (event) => {
84
+ if (runIdRef.current !== runId) return;
85
+ switch (event.type) {
86
+ case "run:start":
87
+ case "run:cancel":
88
+ case "run:retry":
89
+ dispatch({ type: event.type });
90
+ break;
91
+ case "run:complete":
92
+ dispatch({ type: "run:complete", output: event.output });
93
+ break;
94
+ case "run:fail":
95
+ dispatch({ type: "run:fail", error: event.error });
96
+ break;
97
+ case "run:progress":
98
+ dispatch({ type: "run:progress", progress: event.progress });
99
+ break;
100
+ case "log:write":
101
+ dispatch({
102
+ type: "log:write",
103
+ runId: event.runId,
104
+ stepName: event.stepName,
105
+ level: event.level,
106
+ message: event.message,
107
+ data: event.data,
108
+ maxLogs
109
+ });
110
+ break;
111
+ case "connection_error":
112
+ dispatch({ type: "connection_error", error: event.error });
113
+ break;
114
+ }
115
+ });
116
+ return unsubscribe;
117
+ }, [subscriber, runId, maxLogs]);
118
+ const clearLogs = useCallback(() => {
119
+ dispatch({ type: "clear_logs" });
120
+ }, []);
121
+ const reset = useCallback(() => {
122
+ dispatch({ type: "reset" });
123
+ }, []);
124
+ return {
125
+ ...state,
126
+ clearLogs,
127
+ reset
128
+ };
129
+ }
130
+
131
+ export {
132
+ initialSubscriptionState,
133
+ subscriptionReducer,
134
+ useSubscription
135
+ };
136
+ //# sourceMappingURL=chunk-M3YA2EA4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/create-log-entry.ts","../src/shared/subscription-reducer.ts","../src/shared/use-subscription.ts"],"sourcesContent":["import type { LogEntry } from '../types'\n\nexport interface CreateLogEntryParams {\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n data: unknown\n}\n\n/**\n * Creates a LogEntry with auto-generated id and timestamp.\n * Extracted to eliminate duplication between subscription hooks.\n */\nexport function createLogEntry(params: CreateLogEntryParams): LogEntry {\n return {\n id: crypto.randomUUID(),\n runId: params.runId,\n stepName: params.stepName,\n level: params.level,\n message: params.message,\n data: params.data,\n timestamp: new Date().toISOString(),\n }\n}\n\n/**\n * Appends a log entry to the array, respecting maxLogs limit.\n */\nexport function appendLog(\n logs: LogEntry[],\n newLog: LogEntry,\n maxLogs: number,\n): LogEntry[] {\n const newLogs = [...logs, newLog]\n if (maxLogs > 0 && newLogs.length > maxLogs) {\n return newLogs.slice(-maxLogs)\n }\n return newLogs\n}\n","import type { Progress, SubscriptionState } from '../types'\nimport { appendLog, createLogEntry } from './create-log-entry'\n\n// Action types for subscription state transitions\nexport type SubscriptionAction<TOutput = unknown> =\n | { type: 'run:start' }\n | { type: 'run:complete'; output: TOutput }\n | { type: 'run:fail'; error: string }\n | { type: 'run:cancel' }\n | { type: 'run:retry' }\n | { type: 'run:progress'; progress: Progress }\n | {\n type: 'log:write'\n runId: string\n stepName: string | null\n level: 'info' | 'warn' | 'error'\n message: string\n data: unknown\n maxLogs: number\n }\n | { type: 'reset' }\n | { type: 'clear_logs' }\n | { type: 'connection_error'; error: string }\n\nexport const initialSubscriptionState: SubscriptionState<unknown> = {\n status: null,\n output: null,\n error: null,\n logs: [],\n progress: null,\n}\n\n/**\n * Pure reducer for subscription state transitions.\n * Extracted to eliminate duplication between useRunSubscription and useSSESubscription.\n */\nexport function subscriptionReducer<TOutput = unknown>(\n state: SubscriptionState<TOutput>,\n action: SubscriptionAction<TOutput>,\n): SubscriptionState<TOutput> {\n switch (action.type) {\n case 'run:start':\n return { ...state, status: 'running' }\n\n case 'run:complete':\n return { ...state, status: 'completed', output: action.output }\n\n case 'run:fail':\n return { ...state, status: 'failed', error: action.error }\n\n case 'run:cancel':\n return { ...state, status: 'cancelled' }\n\n case 'run:retry':\n return { ...state, status: 'pending', error: null }\n\n case 'run:progress':\n return { ...state, progress: action.progress }\n\n case 'log:write': {\n const newLog = createLogEntry({\n runId: action.runId,\n stepName: action.stepName,\n level: action.level,\n message: action.message,\n data: action.data,\n })\n return { ...state, logs: appendLog(state.logs, newLog, action.maxLogs) }\n }\n\n case 'reset':\n return initialSubscriptionState as SubscriptionState<TOutput>\n\n case 'clear_logs':\n return { ...state, logs: [] }\n\n case 'connection_error':\n return { ...state, error: action.error }\n\n default:\n return state\n }\n}\n","import { useCallback, useEffect, useReducer, useRef } from 'react'\nimport type { SubscriptionState } from '../types'\nimport type { EventSubscriber } from './event-subscriber'\nimport {\n initialSubscriptionState,\n subscriptionReducer,\n} from './subscription-reducer'\n\nexport interface UseSubscriptionOptions {\n /**\n * Maximum number of logs to keep (0 = unlimited)\n */\n maxLogs?: number\n}\n\nexport interface UseSubscriptionResult<\n TOutput = unknown,\n> extends SubscriptionState<TOutput> {\n /**\n * Clear all logs\n */\n clearLogs: () => void\n /**\n * Reset all state\n */\n reset: () => void\n}\n\n/**\n * Core subscription hook that works with any EventSubscriber implementation.\n * This unifies the subscription logic between Durably.on and SSE.\n */\nexport function useSubscription<TOutput = unknown>(\n subscriber: EventSubscriber | null,\n runId: string | null,\n options?: UseSubscriptionOptions,\n): UseSubscriptionResult<TOutput> {\n const [state, dispatch] = useReducer(\n subscriptionReducer<TOutput>,\n initialSubscriptionState as SubscriptionState<TOutput>,\n )\n\n const runIdRef = useRef<string | null>(runId)\n const prevRunIdRef = useRef<string | null>(null)\n\n const maxLogs = options?.maxLogs ?? 0\n\n // Reset state when runId changes\n if (prevRunIdRef.current !== runId) {\n prevRunIdRef.current = runId\n if (runIdRef.current !== runId) {\n dispatch({ type: 'reset' })\n }\n }\n runIdRef.current = runId\n\n useEffect(() => {\n if (!subscriber || !runId) return\n\n const unsubscribe = subscriber.subscribe<TOutput>(runId, (event) => {\n // Verify runId hasn't changed during async operation\n if (runIdRef.current !== runId) return\n\n switch (event.type) {\n case 'run:start':\n case 'run:cancel':\n case 'run:retry':\n dispatch({ type: event.type })\n break\n case 'run:complete':\n dispatch({ type: 'run:complete', output: event.output })\n break\n case 'run:fail':\n dispatch({ type: 'run:fail', error: event.error })\n break\n case 'run:progress':\n dispatch({ type: 'run:progress', progress: event.progress })\n break\n case 'log:write':\n dispatch({\n type: 'log:write',\n runId: event.runId,\n stepName: event.stepName,\n level: event.level,\n message: event.message,\n data: event.data,\n maxLogs,\n })\n break\n case 'connection_error':\n dispatch({ type: 'connection_error', error: event.error })\n break\n }\n })\n\n return unsubscribe\n }, [subscriber, runId, maxLogs])\n\n const clearLogs = useCallback(() => {\n dispatch({ type: 'clear_logs' })\n }, [])\n\n const reset = useCallback(() => {\n dispatch({ type: 'reset' })\n }, [])\n\n return {\n ...state,\n clearLogs,\n reset,\n }\n}\n"],"mappings":";AAcO,SAAS,eAAe,QAAwC;AACrE,SAAO;AAAA,IACL,IAAI,OAAO,WAAW;AAAA,IACtB,OAAO,OAAO;AAAA,IACd,UAAU,OAAO;AAAA,IACjB,OAAO,OAAO;AAAA,IACd,SAAS,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACF;AAKO,SAAS,UACd,MACA,QACA,SACY;AACZ,QAAM,UAAU,CAAC,GAAG,MAAM,MAAM;AAChC,MAAI,UAAU,KAAK,QAAQ,SAAS,SAAS;AAC3C,WAAO,QAAQ,MAAM,CAAC,OAAO;AAAA,EAC/B;AACA,SAAO;AACT;;;ACfO,IAAM,2BAAuD;AAAA,EAClE,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,MAAM,CAAC;AAAA,EACP,UAAU;AACZ;AAMO,SAAS,oBACd,OACA,QAC4B;AAC5B,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,UAAU;AAAA,IAEvC,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,aAAa,QAAQ,OAAO,OAAO;AAAA,IAEhE,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,UAAU,OAAO,OAAO,MAAM;AAAA,IAE3D,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,YAAY;AAAA,IAEzC,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,QAAQ,WAAW,OAAO,KAAK;AAAA,IAEpD,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,UAAU,OAAO,SAAS;AAAA,IAE/C,KAAK,aAAa;AAChB,YAAM,SAAS,eAAe;AAAA,QAC5B,OAAO,OAAO;AAAA,QACd,UAAU,OAAO;AAAA,QACjB,OAAO,OAAO;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,MAAM,OAAO;AAAA,MACf,CAAC;AACD,aAAO,EAAE,GAAG,OAAO,MAAM,UAAU,MAAM,MAAM,QAAQ,OAAO,OAAO,EAAE;AAAA,IACzE;AAAA,IAEA,KAAK;AACH,aAAO;AAAA,IAET,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,MAAM,CAAC,EAAE;AAAA,IAE9B,KAAK;AACH,aAAO,EAAE,GAAG,OAAO,OAAO,OAAO,MAAM;AAAA,IAEzC;AACE,aAAO;AAAA,EACX;AACF;;;AClFA,SAAS,aAAa,WAAW,YAAY,cAAc;AAgCpD,SAAS,gBACd,YACA,OACA,SACgC;AAChC,QAAM,CAAC,OAAO,QAAQ,IAAI;AAAA,IACxB;AAAA,IACA;AAAA,EACF;AAEA,QAAM,WAAW,OAAsB,KAAK;AAC5C,QAAM,eAAe,OAAsB,IAAI;AAE/C,QAAM,UAAU,SAAS,WAAW;AAGpC,MAAI,aAAa,YAAY,OAAO;AAClC,iBAAa,UAAU;AACvB,QAAI,SAAS,YAAY,OAAO;AAC9B,eAAS,EAAE,MAAM,QAAQ,CAAC;AAAA,IAC5B;AAAA,EACF;AACA,WAAS,UAAU;AAEnB,YAAU,MAAM;AACd,QAAI,CAAC,cAAc,CAAC,MAAO;AAE3B,UAAM,cAAc,WAAW,UAAmB,OAAO,CAAC,UAAU;AAElE,UAAI,SAAS,YAAY,MAAO;AAEhC,cAAQ,MAAM,MAAM;AAAA,QAClB,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACH,mBAAS,EAAE,MAAM,MAAM,KAAK,CAAC;AAC7B;AAAA,QACF,KAAK;AACH,mBAAS,EAAE,MAAM,gBAAgB,QAAQ,MAAM,OAAO,CAAC;AACvD;AAAA,QACF,KAAK;AACH,mBAAS,EAAE,MAAM,YAAY,OAAO,MAAM,MAAM,CAAC;AACjD;AAAA,QACF,KAAK;AACH,mBAAS,EAAE,MAAM,gBAAgB,UAAU,MAAM,SAAS,CAAC;AAC3D;AAAA,QACF,KAAK;AACH,mBAAS;AAAA,YACP,MAAM;AAAA,YACN,OAAO,MAAM;AAAA,YACb,UAAU,MAAM;AAAA,YAChB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,MAAM,MAAM;AAAA,YACZ;AAAA,UACF,CAAC;AACD;AAAA,QACF,KAAK;AACH,mBAAS,EAAE,MAAM,oBAAoB,OAAO,MAAM,MAAM,CAAC;AACzD;AAAA,MACJ;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,YAAY,OAAO,OAAO,CAAC;AAE/B,QAAM,YAAY,YAAY,MAAM;AAClC,aAAS,EAAE,MAAM,aAAa,CAAC;AAAA,EACjC,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQ,YAAY,MAAM;AAC9B,aAAS,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC5B,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import { R as RunStatus, L as LogEntry, P as Progress, I as InferInput, a as InferOutput } from './types-C7h7nZv3.js';
1
2
  import { JobDefinition } from '@coji/durably';
2
- import { R as RunStatus, L as LogEntry, P as Progress } from './types-BDUvsa8u.js';
3
3
 
4
4
  interface UseJobClientOptions {
5
5
  /**
@@ -190,20 +190,6 @@ interface UseJobRunClientResult<TOutput = unknown> {
190
190
  */
191
191
  declare function useJobRun<TOutput = unknown>(options: UseJobRunClientOptions): UseJobRunClientResult<TOutput>;
192
192
 
193
- /**
194
- * Extract input type from a JobDefinition or JobHandle
195
- */
196
- type InferInput$1<T> = T extends JobDefinition<string, infer TInput, unknown> ? TInput extends Record<string, unknown> ? TInput : Record<string, unknown> : T extends {
197
- trigger: (input: infer TInput) => unknown;
198
- } ? TInput extends Record<string, unknown> ? TInput : Record<string, unknown> : Record<string, unknown>;
199
- /**
200
- * Extract output type from a JobDefinition or JobHandle
201
- */
202
- type InferOutput$1<T> = T extends JobDefinition<string, unknown, infer TOutput> ? TOutput extends Record<string, unknown> ? TOutput : Record<string, unknown> : T extends {
203
- trigger: (input: unknown) => Promise<{
204
- output?: infer TOutput;
205
- }>;
206
- } ? TOutput extends Record<string, unknown> ? TOutput : Record<string, unknown> : Record<string, unknown>;
207
193
  /**
208
194
  * Type-safe hooks for a specific job
209
195
  */
@@ -236,7 +222,7 @@ interface CreateDurablyClientOptions {
236
222
  * A type-safe client with hooks for each registered job
237
223
  */
238
224
  type DurablyClient<TJobs extends Record<string, unknown>> = {
239
- [K in keyof TJobs]: JobClient<InferInput$1<TJobs[K]>, InferOutput$1<TJobs[K]>>;
225
+ [K in keyof TJobs]: JobClient<InferInput<TJobs[K]>, InferOutput<TJobs[K]>>;
240
226
  };
241
227
  /**
242
228
  * Create a type-safe Durably client with hooks for all registered jobs.
@@ -273,14 +259,6 @@ type DurablyClient<TJobs extends Record<string, unknown>> = {
273
259
  */
274
260
  declare function createDurablyClient<TJobs extends Record<string, unknown>>(options: CreateDurablyClientOptions): DurablyClient<TJobs>;
275
261
 
276
- /**
277
- * Extract input type from a JobDefinition
278
- */
279
- type InferInput<T> = T extends JobDefinition<string, infer TInput, unknown> ? TInput extends Record<string, unknown> ? TInput : Record<string, unknown> : Record<string, unknown>;
280
- /**
281
- * Extract output type from a JobDefinition
282
- */
283
- type InferOutput<T> = T extends JobDefinition<string, unknown, infer TOutput> ? TOutput extends Record<string, unknown> ? TOutput : Record<string, unknown> : Record<string, unknown>;
284
262
  /**
285
263
  * Options for createJobHooks
286
264
  */
package/dist/client.js CHANGED
@@ -1,121 +1,88 @@
1
+ import {
2
+ useSubscription
3
+ } from "./chunk-M3YA2EA4.js";
4
+
1
5
  // src/client/use-job.ts
2
- import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
6
+ import { useCallback, useEffect, useState } from "react";
3
7
 
4
8
  // src/client/use-sse-subscription.ts
5
- import { useCallback, useEffect, useRef, useState } from "react";
6
- function useSSESubscription(api, runId, options) {
7
- const [status, setStatus] = useState(null);
8
- const [output, setOutput] = useState(null);
9
- const [error, setError] = useState(null);
10
- const [logs, setLogs] = useState([]);
11
- const [progress, setProgress] = useState(null);
12
- const eventSourceRef = useRef(null);
13
- const runIdRef = useRef(runId);
14
- const prevRunIdRef = useRef(null);
15
- const maxLogs = options?.maxLogs ?? 0;
16
- if (prevRunIdRef.current !== runId) {
17
- prevRunIdRef.current = runId;
18
- if (runIdRef.current !== runId) {
19
- setStatus(null);
20
- setOutput(null);
21
- setError(null);
22
- setLogs([]);
23
- setProgress(null);
24
- }
25
- }
26
- runIdRef.current = runId;
27
- useEffect(() => {
28
- if (!api || !runId) return;
29
- const url = `${api}/subscribe?runId=${encodeURIComponent(runId)}`;
30
- const eventSource = new EventSource(url);
31
- eventSourceRef.current = eventSource;
32
- eventSource.onmessage = (event) => {
33
- try {
34
- const data = JSON.parse(event.data);
35
- if (data.runId !== runIdRef.current) return;
36
- switch (data.type) {
37
- case "run:start":
38
- setStatus("running");
39
- break;
40
- case "run:complete":
41
- setStatus("completed");
42
- setOutput(data.output);
43
- break;
44
- case "run:fail":
45
- setStatus("failed");
46
- setError(data.error);
47
- break;
48
- case "run:cancel":
49
- setStatus("cancelled");
50
- break;
51
- case "run:retry":
52
- setStatus("pending");
53
- setError(null);
54
- break;
55
- case "run:progress":
56
- setProgress(data.progress);
57
- break;
58
- case "log:write":
59
- setLogs((prev) => {
60
- const newLog = {
61
- id: crypto.randomUUID(),
9
+ import { useMemo } from "react";
10
+
11
+ // src/shared/sse-event-subscriber.ts
12
+ function createSSEEventSubscriber(apiBaseUrl) {
13
+ return {
14
+ subscribe(runId, onEvent) {
15
+ const url = `${apiBaseUrl}/subscribe?runId=${encodeURIComponent(runId)}`;
16
+ const eventSource = new EventSource(url);
17
+ eventSource.onmessage = (messageEvent) => {
18
+ try {
19
+ const data = JSON.parse(messageEvent.data);
20
+ if (data.runId !== runId) return;
21
+ switch (data.type) {
22
+ case "run:start":
23
+ onEvent({ type: "run:start" });
24
+ break;
25
+ case "run:complete":
26
+ onEvent({
27
+ type: "run:complete",
28
+ output: data.output
29
+ });
30
+ break;
31
+ case "run:fail":
32
+ onEvent({ type: "run:fail", error: data.error });
33
+ break;
34
+ case "run:cancel":
35
+ onEvent({ type: "run:cancel" });
36
+ break;
37
+ case "run:retry":
38
+ onEvent({ type: "run:retry" });
39
+ break;
40
+ case "run:progress":
41
+ onEvent({ type: "run:progress", progress: data.progress });
42
+ break;
43
+ case "log:write":
44
+ onEvent({
45
+ type: "log:write",
62
46
  runId: data.runId,
63
47
  stepName: null,
64
48
  level: data.level,
65
49
  message: data.message,
66
- data: data.data,
67
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
68
- };
69
- const newLogs = [...prev, newLog];
70
- if (maxLogs > 0 && newLogs.length > maxLogs) {
71
- return newLogs.slice(-maxLogs);
72
- }
73
- return newLogs;
74
- });
75
- break;
50
+ data: data.data
51
+ });
52
+ break;
53
+ }
54
+ } catch {
76
55
  }
77
- } catch {
78
- }
79
- };
80
- eventSource.onerror = () => {
81
- setError("Connection failed");
82
- eventSource.close();
83
- };
84
- return () => {
85
- eventSource.close();
86
- eventSourceRef.current = null;
87
- };
88
- }, [api, runId, maxLogs]);
89
- const clearLogs = useCallback(() => {
90
- setLogs([]);
91
- }, []);
92
- const reset = useCallback(() => {
93
- setStatus(null);
94
- setOutput(null);
95
- setError(null);
96
- setLogs([]);
97
- setProgress(null);
98
- }, []);
99
- return {
100
- status,
101
- output,
102
- error,
103
- logs,
104
- progress,
105
- clearLogs,
106
- reset
56
+ };
57
+ eventSource.onerror = () => {
58
+ onEvent({ type: "connection_error", error: "Connection failed" });
59
+ eventSource.close();
60
+ };
61
+ return () => {
62
+ eventSource.close();
63
+ };
64
+ }
107
65
  };
108
66
  }
109
67
 
68
+ // src/client/use-sse-subscription.ts
69
+ function useSSESubscription(api, runId, options) {
70
+ const subscriber = useMemo(
71
+ () => api ? createSSEEventSubscriber(api) : null,
72
+ [api]
73
+ );
74
+ return useSubscription(subscriber, runId, options);
75
+ }
76
+
110
77
  // src/client/use-job.ts
111
78
  function useJob(options) {
112
79
  const { api, jobName, initialRunId } = options;
113
- const [currentRunId, setCurrentRunId] = useState2(
80
+ const [currentRunId, setCurrentRunId] = useState(
114
81
  initialRunId ?? null
115
82
  );
116
- const [isPending, setIsPending] = useState2(false);
83
+ const [isPending, setIsPending] = useState(false);
117
84
  const subscription = useSSESubscription(api, currentRunId);
118
- const trigger = useCallback2(
85
+ const trigger = useCallback(
119
86
  async (input) => {
120
87
  subscription.reset();
121
88
  setIsPending(true);
@@ -137,7 +104,7 @@ function useJob(options) {
137
104
  },
138
105
  [api, jobName, subscription.reset]
139
106
  );
140
- const triggerAndWait = useCallback2(
107
+ const triggerAndWait = useCallback(
141
108
  async (input) => {
142
109
  const { runId } = await trigger(input);
143
110
  return new Promise((resolve, reject) => {
@@ -157,13 +124,13 @@ function useJob(options) {
157
124
  },
158
125
  [trigger, subscription.status, subscription.output, subscription.error]
159
126
  );
160
- const reset = useCallback2(() => {
127
+ const reset = useCallback(() => {
161
128
  subscription.reset();
162
129
  setCurrentRunId(null);
163
130
  setIsPending(false);
164
131
  }, [subscription.reset]);
165
132
  const effectiveStatus = subscription.status ?? (isPending ? "pending" : null);
166
- useEffect2(() => {
133
+ useEffect(() => {
167
134
  if (subscription.status && isPending) {
168
135
  setIsPending(false);
169
136
  }
@@ -196,7 +163,7 @@ function useJobLogs(options) {
196
163
  }
197
164
 
198
165
  // src/client/use-job-run.ts
199
- import { useEffect as useEffect3, useRef as useRef2 } from "react";
166
+ import { useEffect as useEffect2, useRef } from "react";
200
167
  function useJobRun(options) {
201
168
  const { api, runId, onStart, onComplete, onFail } = options;
202
169
  const subscription = useSSESubscription(api, runId);
@@ -206,8 +173,8 @@ function useJobRun(options) {
206
173
  const isPending = effectiveStatus === "pending";
207
174
  const isRunning = effectiveStatus === "running";
208
175
  const isCancelled = effectiveStatus === "cancelled";
209
- const prevStatusRef = useRef2(null);
210
- useEffect3(() => {
176
+ const prevStatusRef = useRef(null);
177
+ useEffect2(() => {
211
178
  const prevStatus = prevStatusRef.current;
212
179
  prevStatusRef.current = effectiveStatus;
213
180
  if (prevStatus !== effectiveStatus) {
@@ -282,17 +249,17 @@ function createJobHooks(options) {
282
249
  }
283
250
 
284
251
  // src/client/use-runs.ts
285
- import { useCallback as useCallback3, useEffect as useEffect4, useRef as useRef3, useState as useState3 } from "react";
252
+ import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
286
253
  function useRuns(options) {
287
254
  const { api, jobName, status, pageSize = 10 } = options;
288
- const [runs, setRuns] = useState3([]);
289
- const [page, setPage] = useState3(0);
290
- const [hasMore, setHasMore] = useState3(false);
291
- const [isLoading, setIsLoading] = useState3(false);
292
- const [error, setError] = useState3(null);
293
- const isMountedRef = useRef3(true);
294
- const eventSourceRef = useRef3(null);
295
- const refresh = useCallback3(async () => {
255
+ const [runs, setRuns] = useState2([]);
256
+ const [page, setPage] = useState2(0);
257
+ const [hasMore, setHasMore] = useState2(false);
258
+ const [isLoading, setIsLoading] = useState2(false);
259
+ const [error, setError] = useState2(null);
260
+ const isMountedRef = useRef2(true);
261
+ const eventSourceRef = useRef2(null);
262
+ const refresh = useCallback2(async () => {
296
263
  setIsLoading(true);
297
264
  setError(null);
298
265
  try {
@@ -321,14 +288,14 @@ function useRuns(options) {
321
288
  }
322
289
  }
323
290
  }, [api, jobName, status, pageSize, page]);
324
- useEffect4(() => {
291
+ useEffect3(() => {
325
292
  isMountedRef.current = true;
326
293
  refresh();
327
294
  return () => {
328
295
  isMountedRef.current = false;
329
296
  };
330
297
  }, [refresh]);
331
- useEffect4(() => {
298
+ useEffect3(() => {
332
299
  if (page !== 0) {
333
300
  if (eventSourceRef.current) {
334
301
  eventSourceRef.current.close();
@@ -374,15 +341,15 @@ function useRuns(options) {
374
341
  eventSourceRef.current = null;
375
342
  };
376
343
  }, [api, jobName, page, refresh]);
377
- const nextPage = useCallback3(() => {
344
+ const nextPage = useCallback2(() => {
378
345
  if (hasMore) {
379
346
  setPage((p) => p + 1);
380
347
  }
381
348
  }, [hasMore]);
382
- const prevPage = useCallback3(() => {
349
+ const prevPage = useCallback2(() => {
383
350
  setPage((p) => Math.max(0, p - 1));
384
351
  }, []);
385
- const goToPage = useCallback3((newPage) => {
352
+ const goToPage = useCallback2((newPage) => {
386
353
  setPage(Math.max(0, newPage));
387
354
  }, []);
388
355
  return {
@@ -399,12 +366,12 @@ function useRuns(options) {
399
366
  }
400
367
 
401
368
  // src/client/use-run-actions.ts
402
- import { useCallback as useCallback4, useState as useState4 } from "react";
369
+ import { useCallback as useCallback3, useState as useState3 } from "react";
403
370
  function useRunActions(options) {
404
371
  const { api } = options;
405
- const [isLoading, setIsLoading] = useState4(false);
406
- const [error, setError] = useState4(null);
407
- const retry = useCallback4(
372
+ const [isLoading, setIsLoading] = useState3(false);
373
+ const [error, setError] = useState3(null);
374
+ const retry = useCallback3(
408
375
  async (runId) => {
409
376
  setIsLoading(true);
410
377
  setError(null);
@@ -432,7 +399,7 @@ function useRunActions(options) {
432
399
  },
433
400
  [api]
434
401
  );
435
- const cancel = useCallback4(
402
+ const cancel = useCallback3(
436
403
  async (runId) => {
437
404
  setIsLoading(true);
438
405
  setError(null);
@@ -460,7 +427,7 @@ function useRunActions(options) {
460
427
  },
461
428
  [api]
462
429
  );
463
- const deleteRun = useCallback4(
430
+ const deleteRun = useCallback3(
464
431
  async (runId) => {
465
432
  setIsLoading(true);
466
433
  setError(null);
@@ -488,7 +455,7 @@ function useRunActions(options) {
488
455
  },
489
456
  [api]
490
457
  );
491
- const getRun = useCallback4(
458
+ const getRun = useCallback3(
492
459
  async (runId) => {
493
460
  setIsLoading(true);
494
461
  setError(null);
@@ -520,7 +487,7 @@ function useRunActions(options) {
520
487
  },
521
488
  [api]
522
489
  );
523
- const getSteps = useCallback4(
490
+ const getSteps = useCallback3(
524
491
  async (runId) => {
525
492
  setIsLoading(true);
526
493
  setError(null);