@trigger.dev/react-hooks 0.0.0-langsmith-ai-20250111213529 → 0.0.0-next-20260414123825

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.
Files changed (37) hide show
  1. package/dist/commonjs/hooks/useApiClient.d.ts +2 -0
  2. package/dist/commonjs/hooks/useApiClient.js +2 -1
  3. package/dist/commonjs/hooks/useApiClient.js.map +1 -1
  4. package/dist/commonjs/hooks/useInputStreamSend.d.ts +26 -0
  5. package/dist/commonjs/hooks/useInputStreamSend.js +45 -0
  6. package/dist/commonjs/hooks/useInputStreamSend.js.map +1 -0
  7. package/dist/commonjs/hooks/useRealtime.d.ts +180 -3
  8. package/dist/commonjs/hooks/useRealtime.js +140 -14
  9. package/dist/commonjs/hooks/useRealtime.js.map +1 -1
  10. package/dist/commonjs/hooks/useTaskTrigger.d.ts +7 -1
  11. package/dist/commonjs/hooks/useTaskTrigger.js +2 -1
  12. package/dist/commonjs/hooks/useTaskTrigger.js.map +1 -1
  13. package/dist/commonjs/hooks/useWaitToken.d.ts +35 -0
  14. package/dist/commonjs/hooks/useWaitToken.js +52 -0
  15. package/dist/commonjs/hooks/useWaitToken.js.map +1 -0
  16. package/dist/commonjs/index.d.ts +2 -0
  17. package/dist/commonjs/index.js +2 -0
  18. package/dist/commonjs/index.js.map +1 -1
  19. package/dist/esm/hooks/useApiClient.d.ts +2 -0
  20. package/dist/esm/hooks/useApiClient.js +2 -1
  21. package/dist/esm/hooks/useApiClient.js.map +1 -1
  22. package/dist/esm/hooks/useInputStreamSend.d.ts +26 -0
  23. package/dist/esm/hooks/useInputStreamSend.js +39 -0
  24. package/dist/esm/hooks/useInputStreamSend.js.map +1 -0
  25. package/dist/esm/hooks/useRealtime.d.ts +180 -3
  26. package/dist/esm/hooks/useRealtime.js +139 -14
  27. package/dist/esm/hooks/useRealtime.js.map +1 -1
  28. package/dist/esm/hooks/useTaskTrigger.d.ts +7 -1
  29. package/dist/esm/hooks/useTaskTrigger.js +2 -1
  30. package/dist/esm/hooks/useTaskTrigger.js.map +1 -1
  31. package/dist/esm/hooks/useWaitToken.d.ts +35 -0
  32. package/dist/esm/hooks/useWaitToken.js +46 -0
  33. package/dist/esm/hooks/useWaitToken.js.map +1 -0
  34. package/dist/esm/index.d.ts +2 -0
  35. package/dist/esm/index.js +2 -0
  36. package/dist/esm/index.js.map +1 -1
  37. package/package.json +4 -6
@@ -7,6 +7,8 @@ export type UseApiClientOptions = {
7
7
  accessToken?: string;
8
8
  /** Optional base URL for the API endpoints */
9
9
  baseURL?: string;
10
+ /** Optional preview branch name for preview environments */
11
+ previewBranch?: string;
10
12
  /** Optional additional request configuration */
11
13
  requestOptions?: ApiRequestOptions;
12
14
  /**
@@ -28,6 +28,7 @@ function useApiClient(options) {
28
28
  const auth = (0, contexts_js_1.useTriggerAuthContextOptional)();
29
29
  const baseUrl = options?.baseURL ?? auth?.baseURL ?? "https://api.trigger.dev";
30
30
  const accessToken = options?.accessToken ?? auth?.accessToken;
31
+ const previewBranch = options?.previewBranch ?? auth?.previewBranch;
31
32
  if (!accessToken) {
32
33
  if (options?.enabled === false) {
33
34
  return undefined;
@@ -38,6 +39,6 @@ function useApiClient(options) {
38
39
  ...auth?.requestOptions,
39
40
  ...options?.requestOptions,
40
41
  };
41
- return new v3_1.ApiClient(baseUrl, accessToken, requestOptions);
42
+ return new v3_1.ApiClient(baseUrl, accessToken, previewBranch, requestOptions);
42
43
  }
43
44
  //# sourceMappingURL=useApiClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useApiClient.js","sourceRoot":"","sources":["../../../src/hooks/useApiClient.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;AA4Cb,oCAoBC;AA9DD,6CAAoE;AACpE,gDAA+D;AAqB/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,YAAY,CAAC,OAA6B;IACxD,MAAM,IAAI,GAAG,IAAA,2CAA6B,GAAE,CAAC;IAE7C,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,IAAI,yBAAyB,CAAC;IAC/E,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,IAAI,EAAE,WAAW,CAAC;IAE9D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,IAAI,OAAO,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,cAAc,GAAsB;QACxC,GAAG,IAAI,EAAE,cAAc;QACvB,GAAG,OAAO,EAAE,cAAc;KAC3B,CAAC;IAEF,OAAO,IAAI,cAAS,CAAC,OAAO,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"useApiClient.js","sourceRoot":"","sources":["../../../src/hooks/useApiClient.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;AA8Cb,oCAoBC;AAhED,6CAAoE;AACpE,gDAA+D;AAuB/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAgB,YAAY,CAAC,OAA6B;IACxD,MAAM,IAAI,GAAG,IAAA,2CAA6B,GAAE,CAAC;IAE7C,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,OAAO,IAAI,yBAAyB,CAAC;IAC/E,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,IAAI,EAAE,WAAW,CAAC;IAC9D,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,IAAI,EAAE,aAAa,CAAC;IACpE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,IAAI,OAAO,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;YAC/B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,MAAM,IAAI,KAAK,CAAC,mEAAmE,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,cAAc,GAAsB;QACxC,GAAG,IAAI,EAAE,cAAc;QACvB,GAAG,OAAO,EAAE,cAAc;KAC3B,CAAC;IAEF,OAAO,IAAI,cAAS,CAAC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { UseApiClientOptions } from "./useApiClient.js";
2
+ export interface InputStreamSendInstance<TData> {
3
+ /** Send data to the input stream */
4
+ send: (data: TData) => void;
5
+ /** Whether a send is currently in progress */
6
+ isLoading: boolean;
7
+ /** Any error that occurred during the last send */
8
+ error?: Error;
9
+ /** Whether the hook is ready to send (has runId and access token) */
10
+ isReady: boolean;
11
+ }
12
+ /**
13
+ * Hook to send data to an input stream on a running task.
14
+ *
15
+ * @template TData - The type of data to send
16
+ * @param streamId - The input stream identifier
17
+ * @param runId - The run to send input stream data to
18
+ * @param options - API client options (e.g. accessToken)
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * const { send, isLoading } = useInputStreamSend("my-stream", runId, { accessToken });
23
+ * send({ message: "hello" });
24
+ * ```
25
+ */
26
+ export declare function useInputStreamSend<TData>(streamId: string, runId?: string, options?: UseApiClientOptions): InputStreamSendInstance<TData>;
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.useInputStreamSend = useInputStreamSend;
8
+ const mutation_1 = __importDefault(require("swr/mutation"));
9
+ const useApiClient_js_1 = require("./useApiClient.js");
10
+ /**
11
+ * Hook to send data to an input stream on a running task.
12
+ *
13
+ * @template TData - The type of data to send
14
+ * @param streamId - The input stream identifier
15
+ * @param runId - The run to send input stream data to
16
+ * @param options - API client options (e.g. accessToken)
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * const { send, isLoading } = useInputStreamSend("my-stream", runId, { accessToken });
21
+ * send({ message: "hello" });
22
+ * ```
23
+ */
24
+ function useInputStreamSend(streamId, runId, options) {
25
+ const apiClient = (0, useApiClient_js_1.useApiClient)(options);
26
+ async function sendToStream(key, { arg }) {
27
+ if (!apiClient) {
28
+ throw new Error("Could not send to input stream: Missing access token");
29
+ }
30
+ if (!runId) {
31
+ throw new Error("Could not send to input stream: Missing run ID");
32
+ }
33
+ return await apiClient.sendInputStream(runId, streamId, arg.data);
34
+ }
35
+ const mutation = (0, mutation_1.default)(runId ? `input-stream:${runId}:${streamId}` : null, sendToStream);
36
+ return {
37
+ send: (data) => {
38
+ mutation.trigger({ data });
39
+ },
40
+ isLoading: mutation.isMutating,
41
+ isReady: !!runId && !!apiClient,
42
+ error: mutation.error,
43
+ };
44
+ }
45
+ //# sourceMappingURL=useInputStreamSend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useInputStreamSend.js","sourceRoot":"","sources":["../../../src/hooks/useInputStreamSend.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;;;;;;AA8Bb,gDA6BC;AAzDD,4DAA0C;AAC1C,uDAAsE;AAatE;;;;;;;;;;;;;GAaG;AACH,SAAgB,kBAAkB,CAChC,QAAgB,EAChB,KAAc,EACd,OAA6B;IAE7B,MAAM,SAAS,GAAG,IAAA,8BAAY,EAAC,OAAO,CAAC,CAAC;IAExC,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,EAAE,GAAG,EAA4B;QACxE,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,MAAM,SAAS,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,QAAQ,GAAG,IAAA,kBAAc,EAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;IAElG,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YACb,QAAQ,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7B,CAAC;QACD,SAAS,EAAE,QAAQ,CAAC,UAAU;QAC9B,OAAO,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,SAAS;QAC/B,KAAK,EAAE,QAAQ,CAAC,KAAK;KACtB,CAAC;AACJ,CAAC"}
@@ -1,9 +1,14 @@
1
- import { AnyTask, RealtimeRun } from "@trigger.dev/core/v3";
1
+ import { AnyTask, InferStreamType, RealtimeDefinedStream, RealtimeRun, RealtimeRunSkipColumns } from "@trigger.dev/core/v3";
2
2
  import { UseApiClientOptions } from "./useApiClient.js";
3
3
  export type UseRealtimeRunOptions = UseApiClientOptions & {
4
4
  id?: string;
5
5
  enabled?: boolean;
6
- experimental_throttleInMs?: number;
6
+ /**
7
+ * The number of milliseconds to throttle the stream updates.
8
+ *
9
+ * @default 16
10
+ */
11
+ throttleInMs?: number;
7
12
  };
8
13
  export type UseRealtimeSingleRunOptions<TTask extends AnyTask = AnyTask> = UseRealtimeRunOptions & {
9
14
  /**
@@ -21,6 +26,12 @@ export type UseRealtimeSingleRunOptions<TTask extends AnyTask = AnyTask> = UseRe
21
26
  * Set this to false if you are making updates to the run metadata after completion through child runs
22
27
  */
23
28
  stopOnCompletion?: boolean;
29
+ /**
30
+ * Skip columns from the subscription.
31
+ *
32
+ * @default []
33
+ */
34
+ skipColumns?: RealtimeRunSkipColumns;
24
35
  };
25
36
  export type UseRealtimeRunInstance<TTask extends AnyTask = AnyTask> = {
26
37
  run: RealtimeRun<TTask> | undefined;
@@ -83,6 +94,30 @@ export type UseRealtimeRunsInstance<TTask extends AnyTask = AnyTask> = {
83
94
  */
84
95
  stop: () => void;
85
96
  };
97
+ export type UseRealtimeRunsWithTagOptions = UseRealtimeRunOptions & {
98
+ /**
99
+ * Filter runs by the time they were created. You must specify the duration string like "1h", "10s", "30m", etc.
100
+ *
101
+ * @example
102
+ * "1h" - 1 hour ago
103
+ * "10s" - 10 seconds ago
104
+ * "30m" - 30 minutes ago
105
+ * "1d" - 1 day ago
106
+ * "1w" - 1 week ago
107
+ *
108
+ * The maximum duration is 1 week
109
+ *
110
+ * @note The timestamp will be calculated on the server side when you first subscribe to the runs.
111
+ *
112
+ */
113
+ createdAt?: string;
114
+ /**
115
+ * Skip columns from the subscription.
116
+ *
117
+ * @default []
118
+ */
119
+ skipColumns?: RealtimeRunSkipColumns;
120
+ };
86
121
  /**
87
122
  * Hook to subscribe to realtime updates of task runs filtered by tag(s).
88
123
  *
@@ -97,9 +132,11 @@ export type UseRealtimeRunsInstance<TTask extends AnyTask = AnyTask> = {
97
132
  * const { runs, error } = useRealtimeRunsWithTag<typeof myTask>('my-tag');
98
133
  * // Or with multiple tags
99
134
  * const { runs, error } = useRealtimeRunsWithTag<typeof myTask>(['tag1', 'tag2']);
135
+ * // Or with a createdAt filter
136
+ * const { runs, error } = useRealtimeRunsWithTag<typeof myTask>('my-tag', { createdAt: '1h' });
100
137
  * ```
101
138
  */
102
- export declare function useRealtimeRunsWithTag<TTask extends AnyTask>(tag: string | string[], options?: UseRealtimeRunOptions): UseRealtimeRunsInstance<TTask>;
139
+ export declare function useRealtimeRunsWithTag<TTask extends AnyTask>(tag: string | string[], options?: UseRealtimeRunsWithTagOptions): UseRealtimeRunsInstance<TTask>;
103
140
  /**
104
141
  * Hook to subscribe to realtime updates of a batch of task runs.
105
142
  *
@@ -115,3 +152,143 @@ export declare function useRealtimeRunsWithTag<TTask extends AnyTask>(tag: strin
115
152
  * ```
116
153
  */
117
154
  export declare function useRealtimeBatch<TTask extends AnyTask>(batchId: string, options?: UseRealtimeRunOptions): UseRealtimeRunsInstance<TTask>;
155
+ export type UseRealtimeStreamInstance<TPart> = {
156
+ parts: Array<TPart>;
157
+ error: Error | undefined;
158
+ /**
159
+ * Abort the current request immediately, keep the generated tokens if any.
160
+ */
161
+ stop: () => void;
162
+ };
163
+ export type UseRealtimeStreamOptions<TPart> = UseApiClientOptions & {
164
+ id?: string;
165
+ enabled?: boolean;
166
+ /**
167
+ * The number of milliseconds to throttle the stream updates.
168
+ *
169
+ * @default 16
170
+ */
171
+ throttleInMs?: number;
172
+ /**
173
+ * The number of seconds to wait for new data to be available,
174
+ * If no data arrives within the timeout, the stream will be closed.
175
+ *
176
+ * @default 60 seconds
177
+ */
178
+ timeoutInSeconds?: number;
179
+ /**
180
+ * The index to start reading from.
181
+ * If not provided, the stream will start from the beginning.
182
+ * @default 0
183
+ */
184
+ startIndex?: number;
185
+ /**
186
+ * Callback this is called when new data is received.
187
+ */
188
+ onData?: (data: TPart) => void;
189
+ };
190
+ export declare function useRealtimeStream<TDefinedStream extends RealtimeDefinedStream<any>>(stream: TDefinedStream, runId: string, options?: UseRealtimeStreamOptions<InferStreamType<TDefinedStream>>): UseRealtimeStreamInstance<InferStreamType<TDefinedStream>>;
191
+ /**
192
+ * Hook to subscribe to realtime updates of a stream with a specific stream key.
193
+ *
194
+ * This hook automatically subscribes to a stream and updates the `parts` array as new data arrives.
195
+ * The stream subscription is automatically managed: it starts when the component mounts (or when
196
+ * `enabled` becomes `true`) and stops when the component unmounts or when `stop()` is called.
197
+ *
198
+ * @template TPart - The type of each chunk/part in the stream
199
+ * @param runId - The unique identifier of the run to subscribe to
200
+ * @param streamKey - The unique identifier of the stream to subscribe to. Use this overload
201
+ * when you want to read from a specific stream key.
202
+ * @param options - Optional configuration for the stream subscription
203
+ * @returns An object containing:
204
+ * - `parts`: An array of all stream chunks received so far (accumulates over time)
205
+ * - `error`: Any error that occurred during subscription
206
+ * - `stop`: A function to manually stop the subscription
207
+ *
208
+ * @example
209
+ * ```tsx
210
+ * "use client";
211
+ * import { useRealtimeStream } from "@trigger.dev/react-hooks";
212
+ *
213
+ * function StreamViewer({ runId }: { runId: string }) {
214
+ * const { parts, error } = useRealtimeStream<string>(
215
+ * runId,
216
+ * "my-stream",
217
+ * {
218
+ * accessToken: process.env.NEXT_PUBLIC_TRIGGER_PUBLIC_KEY,
219
+ * }
220
+ * );
221
+ *
222
+ * if (error) return <div>Error: {error.message}</div>;
223
+ *
224
+ * // Parts array accumulates all chunks
225
+ * const fullText = parts.join("");
226
+ *
227
+ * return <div>{fullText}</div>;
228
+ * }
229
+ * ```
230
+ *
231
+ * @example
232
+ * ```tsx
233
+ * // With custom options
234
+ * const { parts, error, stop } = useRealtimeStream<ChatChunk>(
235
+ * runId,
236
+ * "chat-stream",
237
+ * {
238
+ * accessToken: publicKey,
239
+ * timeoutInSeconds: 120,
240
+ * startIndex: 10, // Start from the 10th chunk
241
+ * throttleInMs: 50, // Throttle updates to every 50ms
242
+ * onData: (chunk) => {
243
+ * console.log("New chunk received:", chunk);
244
+ * },
245
+ * }
246
+ * );
247
+ *
248
+ * // Manually stop the subscription
249
+ * <button onClick={stop}>Stop Stream</button>
250
+ * ```
251
+ */
252
+ export declare function useRealtimeStream<TPart>(runId: string, streamKey: string, options?: UseRealtimeStreamOptions<TPart>): UseRealtimeStreamInstance<TPart>;
253
+ /**
254
+ * Hook to subscribe to realtime updates of a stream using the default stream key (`"default"`).
255
+ *
256
+ * This is a convenience overload that allows you to subscribe to the default stream without
257
+ * specifying a stream key. The stream will be accessed with the key `"default"`.
258
+ *
259
+ * @template TPart - The type of each chunk/part in the stream
260
+ * @param runId - The unique identifier of the run to subscribe to
261
+ * @param options - Optional configuration for the stream subscription
262
+ * @returns An object containing:
263
+ * - `parts`: An array of all stream chunks received so far (accumulates over time)
264
+ * - `error`: Any error that occurred during subscription
265
+ * - `stop`: A function to manually stop the subscription
266
+ *
267
+ * @example
268
+ * ```tsx
269
+ * "use client";
270
+ * import { useRealtimeStream } from "@trigger.dev/react-hooks";
271
+ *
272
+ * function DefaultStreamViewer({ runId }: { runId: string }) {
273
+ * // Subscribe to the default stream
274
+ * const { parts, error } = useRealtimeStream<string>(runId, {
275
+ * accessToken: process.env.NEXT_PUBLIC_TRIGGER_PUBLIC_KEY,
276
+ * });
277
+ *
278
+ * if (error) return <div>Error: {error.message}</div>;
279
+ *
280
+ * const fullText = parts.join("");
281
+ * return <div>{fullText}</div>;
282
+ * }
283
+ * ```
284
+ *
285
+ * @example
286
+ * ```tsx
287
+ * // Conditionally enable the stream
288
+ * const { parts } = useRealtimeStream<string>(runId, {
289
+ * accessToken: publicKey,
290
+ * enabled: !!runId && isStreaming, // Only subscribe when runId exists and isStreaming is true
291
+ * });
292
+ * ```
293
+ */
294
+ export declare function useRealtimeStream<TPart>(runId: string, options?: UseRealtimeStreamOptions<TPart>): UseRealtimeStreamInstance<TPart>;
@@ -5,6 +5,7 @@ exports.useRealtimeRun = useRealtimeRun;
5
5
  exports.useRealtimeRunWithStreams = useRealtimeRunWithStreams;
6
6
  exports.useRealtimeRunsWithTag = useRealtimeRunsWithTag;
7
7
  exports.useRealtimeBatch = useRealtimeBatch;
8
+ exports.useRealtimeStream = useRealtimeStream;
8
9
  const react_1 = require("react");
9
10
  const trigger_swr_js_1 = require("../utils/trigger-swr.js");
10
11
  const useApiClient_js_1 = require("./useApiClient.js");
@@ -47,7 +48,7 @@ function useRealtimeRun(runId, options) {
47
48
  }
48
49
  const abortController = new AbortController();
49
50
  abortControllerRef.current = abortController;
50
- await processRealtimeRun(runId, apiClient, mutateRun, setError, abortControllerRef, typeof options?.stopOnCompletion === "boolean" ? options.stopOnCompletion : true);
51
+ await processRealtimeRun(runId, { skipColumns: options?.skipColumns }, apiClient, mutateRun, setError, abortControllerRef, typeof options?.stopOnCompletion === "boolean" ? options.stopOnCompletion : true);
51
52
  }
52
53
  catch (err) {
53
54
  // Ignore abort errors as they are expected.
@@ -67,8 +68,10 @@ function useRealtimeRun(runId, options) {
67
68
  }, [runId, mutateRun, abortControllerRef, apiClient, setError]);
68
69
  const hasCalledOnCompleteRef = (0, react_1.useRef)(false);
69
70
  // Effect to handle onComplete callback
71
+ // Only call onComplete when the run has actually finished (has finishedAt),
72
+ // not just when the subscription stream ends (which can happen due to network issues)
70
73
  (0, react_1.useEffect)(() => {
71
- if (isComplete && run && options?.onComplete && !hasCalledOnCompleteRef.current) {
74
+ if (isComplete && run?.finishedAt && options?.onComplete && !hasCalledOnCompleteRef.current) {
72
75
  options.onComplete(run, error);
73
76
  hasCalledOnCompleteRef.current = true;
74
77
  }
@@ -143,7 +146,7 @@ function useRealtimeRunWithStreams(runId, options) {
143
146
  }
144
147
  const abortController = new AbortController();
145
148
  abortControllerRef.current = abortController;
146
- await processRealtimeRunWithStreams(runId, apiClient, mutateRun, mutateStreams, streamsRef, setError, abortControllerRef, typeof options?.stopOnCompletion === "boolean" ? options.stopOnCompletion : true, options?.experimental_throttleInMs);
149
+ await processRealtimeRunWithStreams(runId, { skipColumns: options?.skipColumns }, apiClient, mutateRun, mutateStreams, streamsRef, setError, abortControllerRef, typeof options?.stopOnCompletion === "boolean" ? options.stopOnCompletion : true, options?.throttleInMs ?? 16);
147
150
  }
148
151
  catch (err) {
149
152
  // Ignore abort errors as they are expected.
@@ -163,8 +166,10 @@ function useRealtimeRunWithStreams(runId, options) {
163
166
  }, [runId, mutateRun, mutateStreams, streamsRef, abortControllerRef, apiClient, setError]);
164
167
  const hasCalledOnCompleteRef = (0, react_1.useRef)(false);
165
168
  // Effect to handle onComplete callback
169
+ // Only call onComplete when the run has actually finished (has finishedAt),
170
+ // not just when the subscription stream ends (which can happen due to network issues)
166
171
  (0, react_1.useEffect)(() => {
167
- if (isComplete && run && options?.onComplete && !hasCalledOnCompleteRef.current) {
172
+ if (isComplete && run?.finishedAt && options?.onComplete && !hasCalledOnCompleteRef.current) {
168
173
  options.onComplete(run, error);
169
174
  hasCalledOnCompleteRef.current = true;
170
175
  }
@@ -202,9 +207,12 @@ function useRealtimeRunWithStreams(runId, options) {
202
207
  * const { runs, error } = useRealtimeRunsWithTag<typeof myTask>('my-tag');
203
208
  * // Or with multiple tags
204
209
  * const { runs, error } = useRealtimeRunsWithTag<typeof myTask>(['tag1', 'tag2']);
210
+ * // Or with a createdAt filter
211
+ * const { runs, error } = useRealtimeRunsWithTag<typeof myTask>('my-tag', { createdAt: '1h' });
205
212
  * ```
206
213
  */
207
214
  function useRealtimeRunsWithTag(tag, options) {
215
+ const normalizedTag = (Array.isArray(tag) ? tag : [tag]).join("-");
208
216
  const hookId = (0, react_1.useId)();
209
217
  const idKey = options?.id ?? hookId;
210
218
  // Store the streams state in SWR, using the idKey as the key to share states.
@@ -233,7 +241,7 @@ function useRealtimeRunsWithTag(tag, options) {
233
241
  }
234
242
  const abortController = new AbortController();
235
243
  abortControllerRef.current = abortController;
236
- await processRealtimeRunsWithTag(tag, apiClient, mutateRuns, runsRef, setError, abortControllerRef);
244
+ await processRealtimeRunsWithTag(tag, { createdAt: options?.createdAt, skipColumns: options?.skipColumns }, apiClient, mutateRuns, runsRef, setError, abortControllerRef);
237
245
  }
238
246
  catch (err) {
239
247
  // Ignore abort errors as they are expected.
@@ -248,7 +256,7 @@ function useRealtimeRunsWithTag(tag, options) {
248
256
  abortControllerRef.current = null;
249
257
  }
250
258
  }
251
- }, [tag, mutateRuns, runsRef, abortControllerRef, apiClient, setError]);
259
+ }, [normalizedTag, mutateRuns, runsRef, abortControllerRef, apiClient, setError]);
252
260
  (0, react_1.useEffect)(() => {
253
261
  if (typeof options?.enabled === "boolean" && !options.enabled) {
254
262
  return;
@@ -257,7 +265,7 @@ function useRealtimeRunsWithTag(tag, options) {
257
265
  return () => {
258
266
  stop();
259
267
  };
260
- }, [tag, stop, options?.enabled]);
268
+ }, [normalizedTag, stop, options?.enabled]);
261
269
  return { runs: runs ?? [], error, stop };
262
270
  }
263
271
  /**
@@ -330,6 +338,93 @@ function useRealtimeBatch(batchId, options) {
330
338
  }, [batchId, stop, options?.enabled]);
331
339
  return { runs: runs ?? [], error, stop };
332
340
  }
341
+ function useRealtimeStream(runIdOrDefinedStream, streamKeyOrOptionsOrRunId, options) {
342
+ if (typeof runIdOrDefinedStream === "string") {
343
+ if (typeof streamKeyOrOptionsOrRunId === "string") {
344
+ return useRealtimeStreamImplementation(runIdOrDefinedStream, streamKeyOrOptionsOrRunId, options);
345
+ }
346
+ else {
347
+ return useRealtimeStreamImplementation(runIdOrDefinedStream, "default", streamKeyOrOptionsOrRunId);
348
+ }
349
+ }
350
+ else {
351
+ if (typeof streamKeyOrOptionsOrRunId === "string") {
352
+ return useRealtimeStreamImplementation(streamKeyOrOptionsOrRunId, runIdOrDefinedStream.id, options);
353
+ }
354
+ else {
355
+ throw new Error("Invalid second argument to useRealtimeStream. When using a defined stream instance, the second argument to useRealtimeStream must be a run ID.");
356
+ }
357
+ }
358
+ }
359
+ function useRealtimeStreamImplementation(runId, streamKey, options) {
360
+ const hookId = (0, react_1.useId)();
361
+ const idKey = options?.id ?? hookId;
362
+ const [initialPartsFallback] = (0, react_1.useState)([]);
363
+ // Store the streams state in SWR, using the idKey as the key to share states.
364
+ const { data: parts, mutate: mutateParts } = (0, trigger_swr_js_1.useSWR)([idKey, runId, streamKey, "parts"], null, {
365
+ fallbackData: initialPartsFallback,
366
+ });
367
+ // Keep the latest streams in a ref.
368
+ const partsRef = (0, react_1.useRef)(parts ?? []);
369
+ (0, react_1.useEffect)(() => {
370
+ partsRef.current = parts || [];
371
+ }, [parts]);
372
+ // Add state to track when the subscription is complete
373
+ const { data: isComplete = false, mutate: setIsComplete } = (0, trigger_swr_js_1.useSWR)([idKey, runId, streamKey, "complete"], null);
374
+ const { data: error = undefined, mutate: setError } = (0, trigger_swr_js_1.useSWR)([idKey, runId, streamKey, "error"], null);
375
+ // Abort controller to cancel the current API call.
376
+ const abortControllerRef = (0, react_1.useRef)(null);
377
+ const stop = (0, react_1.useCallback)(() => {
378
+ if (abortControllerRef.current) {
379
+ abortControllerRef.current.abort();
380
+ abortControllerRef.current = null;
381
+ }
382
+ }, []);
383
+ const onData = (0, react_1.useCallback)((data) => {
384
+ if (options?.onData) {
385
+ options.onData(data);
386
+ }
387
+ }, [options?.onData]);
388
+ const apiClient = (0, useApiClient_js_1.useApiClient)(options);
389
+ const triggerRequest = (0, react_1.useCallback)(async () => {
390
+ try {
391
+ if (!runId || !apiClient) {
392
+ return;
393
+ }
394
+ const abortController = new AbortController();
395
+ abortControllerRef.current = abortController;
396
+ await processRealtimeStream(runId, streamKey, apiClient, mutateParts, partsRef, setError, onData, abortControllerRef, options?.timeoutInSeconds, options?.startIndex, options?.throttleInMs ?? 16);
397
+ }
398
+ catch (err) {
399
+ // Ignore abort errors as they are expected.
400
+ if (err.name === "AbortError") {
401
+ abortControllerRef.current = null;
402
+ return;
403
+ }
404
+ setError(err);
405
+ }
406
+ finally {
407
+ if (abortControllerRef.current) {
408
+ abortControllerRef.current = null;
409
+ }
410
+ // Mark the subscription as complete
411
+ setIsComplete(true);
412
+ }
413
+ }, [runId, streamKey, mutateParts, partsRef, abortControllerRef, apiClient, setError]);
414
+ (0, react_1.useEffect)(() => {
415
+ if (typeof options?.enabled === "boolean" && !options.enabled) {
416
+ return;
417
+ }
418
+ if (!runId) {
419
+ return;
420
+ }
421
+ triggerRequest().finally(() => { });
422
+ return () => {
423
+ stop();
424
+ };
425
+ }, [runId, stop, options?.enabled]);
426
+ return { parts: parts ?? initialPartsFallback, error, stop };
427
+ }
333
428
  async function processRealtimeBatch(batchId, apiClient, mutateRunsData, existingRunsRef, onError, abortControllerRef) {
334
429
  const subscription = apiClient.subscribeToBatch(batchId, {
335
430
  signal: abortControllerRef.current?.signal,
@@ -339,21 +434,21 @@ async function processRealtimeBatch(batchId, apiClient, mutateRunsData, existing
339
434
  mutateRunsData(insertRunShapeInOrder(existingRunsRef.current, part));
340
435
  }
341
436
  }
342
- // Inserts and then orders by the run number, and ensures that the run is not duplicated
437
+ // Inserts and then orders by the run createdAt timestamp, and ensures that the run is not duplicated
343
438
  function insertRunShapeInOrder(previousRuns, run) {
344
439
  const existingRun = previousRuns.find((r) => r.id === run.id);
345
440
  if (existingRun) {
346
441
  return previousRuns.map((r) => (r.id === run.id ? run : r));
347
442
  }
348
- const runNumber = run.number;
349
- const index = previousRuns.findIndex((r) => r.number > runNumber);
443
+ const runCreatedAt = run.createdAt;
444
+ const index = previousRuns.findIndex((r) => r.createdAt > runCreatedAt);
350
445
  if (index === -1) {
351
446
  return [...previousRuns, run];
352
447
  }
353
448
  return [...previousRuns.slice(0, index), run, ...previousRuns.slice(index)];
354
449
  }
355
- async function processRealtimeRunsWithTag(tag, apiClient, mutateRunsData, existingRunsRef, onError, abortControllerRef) {
356
- const subscription = apiClient.subscribeToRunsWithTag(tag, {
450
+ async function processRealtimeRunsWithTag(tag, filters, apiClient, mutateRunsData, existingRunsRef, onError, abortControllerRef) {
451
+ const subscription = apiClient.subscribeToRunsWithTag(tag, filters, {
357
452
  signal: abortControllerRef.current?.signal,
358
453
  onFetchError: onError,
359
454
  });
@@ -374,11 +469,12 @@ function insertRunShape(previousRuns, run) {
374
469
  }
375
470
  return [...previousRuns.slice(0, index), run, ...previousRuns.slice(index)];
376
471
  }
377
- async function processRealtimeRunWithStreams(runId, apiClient, mutateRunData, mutateStreamData, existingDataRef, onError, abortControllerRef, stopOnCompletion = true, throttleInMs) {
472
+ async function processRealtimeRunWithStreams(runId, filters, apiClient, mutateRunData, mutateStreamData, existingDataRef, onError, abortControllerRef, stopOnCompletion = true, throttleInMs) {
378
473
  const subscription = apiClient.subscribeToRun(runId, {
379
474
  signal: abortControllerRef.current?.signal,
380
475
  closeOnComplete: stopOnCompletion,
381
476
  onFetchError: onError,
477
+ skipColumns: filters.skipColumns,
382
478
  });
383
479
  const streamQueue = (0, throttle_js_1.createThrottledQueue)(async (updates) => {
384
480
  const nextStreamData = { ...existingDataRef.current };
@@ -410,14 +506,44 @@ async function processRealtimeRunWithStreams(runId, apiClient, mutateRunData, mu
410
506
  }
411
507
  }
412
508
  }
413
- async function processRealtimeRun(runId, apiClient, mutateRunData, onError, abortControllerRef, stopOnCompletion = true) {
509
+ async function processRealtimeRun(runId, filters, apiClient, mutateRunData, onError, abortControllerRef, stopOnCompletion = true) {
414
510
  const subscription = apiClient.subscribeToRun(runId, {
415
511
  signal: abortControllerRef.current?.signal,
416
512
  closeOnComplete: stopOnCompletion,
417
513
  onFetchError: onError,
514
+ skipColumns: filters.skipColumns,
418
515
  });
419
516
  for await (const part of subscription) {
420
517
  mutateRunData(part);
421
518
  }
422
519
  }
520
+ async function processRealtimeStream(runId, streamKey, apiClient, mutatePartsData, existingPartsRef, onError, onData, abortControllerRef, timeoutInSeconds, startIndex, throttleInMs) {
521
+ try {
522
+ const stream = await apiClient.fetchStream(runId, streamKey, {
523
+ signal: abortControllerRef.current?.signal,
524
+ timeoutInSeconds,
525
+ lastEventId: startIndex ? (startIndex - 1).toString() : undefined,
526
+ });
527
+ // Throttle the stream
528
+ const streamQueue = (0, throttle_js_1.createThrottledQueue)(async (parts) => {
529
+ mutatePartsData([...existingPartsRef.current, ...parts]);
530
+ }, throttleInMs);
531
+ for await (const part of stream) {
532
+ onData(part);
533
+ streamQueue.add(part);
534
+ }
535
+ }
536
+ catch (err) {
537
+ if (err.name === "AbortError") {
538
+ return;
539
+ }
540
+ if (err instanceof Error) {
541
+ onError(err);
542
+ }
543
+ else {
544
+ onError(new Error(String(err)));
545
+ }
546
+ throw err;
547
+ }
548
+ }
423
549
  //# sourceMappingURL=useRealtime.js.map