@trigger.dev/react-hooks 0.0.0-bun-runtimes-20260909131057

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 (89) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +1 -0
  3. package/dist/commonjs/contexts.d.ts +4 -0
  4. package/dist/commonjs/contexts.js +10 -0
  5. package/dist/commonjs/contexts.js.map +1 -0
  6. package/dist/commonjs/hooks/useApiClient.d.ts +47 -0
  7. package/dist/commonjs/hooks/useApiClient.js +57 -0
  8. package/dist/commonjs/hooks/useApiClient.js.map +1 -0
  9. package/dist/commonjs/hooks/useInputStreamSend.d.ts +26 -0
  10. package/dist/commonjs/hooks/useInputStreamSend.js +45 -0
  11. package/dist/commonjs/hooks/useInputStreamSend.js.map +1 -0
  12. package/dist/commonjs/hooks/useRealtime.d.ts +336 -0
  13. package/dist/commonjs/hooks/useRealtime.js +642 -0
  14. package/dist/commonjs/hooks/useRealtime.js.map +1 -0
  15. package/dist/commonjs/hooks/useRun.d.ts +22 -0
  16. package/dist/commonjs/hooks/useRun.js +40 -0
  17. package/dist/commonjs/hooks/useRun.js.map +1 -0
  18. package/dist/commonjs/hooks/useSessionStream.d.ts +131 -0
  19. package/dist/commonjs/hooks/useSessionStream.js +236 -0
  20. package/dist/commonjs/hooks/useSessionStream.js.map +1 -0
  21. package/dist/commonjs/hooks/useSessionStreamChannel.d.ts +86 -0
  22. package/dist/commonjs/hooks/useSessionStreamChannel.js +209 -0
  23. package/dist/commonjs/hooks/useSessionStreamChannel.js.map +1 -0
  24. package/dist/commonjs/hooks/useTaskTrigger.d.ts +107 -0
  25. package/dist/commonjs/hooks/useTaskTrigger.js +139 -0
  26. package/dist/commonjs/hooks/useTaskTrigger.js.map +1 -0
  27. package/dist/commonjs/hooks/useWaitToken.d.ts +35 -0
  28. package/dist/commonjs/hooks/useWaitToken.js +52 -0
  29. package/dist/commonjs/hooks/useWaitToken.js.map +1 -0
  30. package/dist/commonjs/index.d.ts +9 -0
  31. package/dist/commonjs/index.js +26 -0
  32. package/dist/commonjs/index.js.map +1 -0
  33. package/dist/commonjs/package.json +3 -0
  34. package/dist/commonjs/utils/createContextAndHook.d.ts +14 -0
  35. package/dist/commonjs/utils/createContextAndHook.js +38 -0
  36. package/dist/commonjs/utils/createContextAndHook.js.map +1 -0
  37. package/dist/commonjs/utils/throttle.d.ts +6 -0
  38. package/dist/commonjs/utils/throttle.js +50 -0
  39. package/dist/commonjs/utils/throttle.js.map +1 -0
  40. package/dist/commonjs/utils/trigger-swr.d.ts +19 -0
  41. package/dist/commonjs/utils/trigger-swr.js +27 -0
  42. package/dist/commonjs/utils/trigger-swr.js.map +1 -0
  43. package/dist/commonjs/utils/useStableRequestCallback.d.ts +5 -0
  44. package/dist/commonjs/utils/useStableRequestCallback.js +17 -0
  45. package/dist/commonjs/utils/useStableRequestCallback.js.map +1 -0
  46. package/dist/esm/contexts.d.ts +4 -0
  47. package/dist/esm/contexts.js +5 -0
  48. package/dist/esm/contexts.js.map +1 -0
  49. package/dist/esm/hooks/useApiClient.d.ts +47 -0
  50. package/dist/esm/hooks/useApiClient.js +54 -0
  51. package/dist/esm/hooks/useApiClient.js.map +1 -0
  52. package/dist/esm/hooks/useInputStreamSend.d.ts +26 -0
  53. package/dist/esm/hooks/useInputStreamSend.js +39 -0
  54. package/dist/esm/hooks/useInputStreamSend.js.map +1 -0
  55. package/dist/esm/hooks/useRealtime.d.ts +336 -0
  56. package/dist/esm/hooks/useRealtime.js +635 -0
  57. package/dist/esm/hooks/useRealtime.js.map +1 -0
  58. package/dist/esm/hooks/useRun.d.ts +22 -0
  59. package/dist/esm/hooks/useRun.js +37 -0
  60. package/dist/esm/hooks/useRun.js.map +1 -0
  61. package/dist/esm/hooks/useSessionStream.d.ts +131 -0
  62. package/dist/esm/hooks/useSessionStream.js +233 -0
  63. package/dist/esm/hooks/useSessionStream.js.map +1 -0
  64. package/dist/esm/hooks/useSessionStreamChannel.d.ts +86 -0
  65. package/dist/esm/hooks/useSessionStreamChannel.js +206 -0
  66. package/dist/esm/hooks/useSessionStreamChannel.js.map +1 -0
  67. package/dist/esm/hooks/useTaskTrigger.d.ts +107 -0
  68. package/dist/esm/hooks/useTaskTrigger.js +131 -0
  69. package/dist/esm/hooks/useTaskTrigger.js.map +1 -0
  70. package/dist/esm/hooks/useWaitToken.d.ts +35 -0
  71. package/dist/esm/hooks/useWaitToken.js +46 -0
  72. package/dist/esm/hooks/useWaitToken.js.map +1 -0
  73. package/dist/esm/index.d.ts +9 -0
  74. package/dist/esm/index.js +10 -0
  75. package/dist/esm/index.js.map +1 -0
  76. package/dist/esm/package.json +3 -0
  77. package/dist/esm/utils/createContextAndHook.d.ts +14 -0
  78. package/dist/esm/utils/createContextAndHook.js +31 -0
  79. package/dist/esm/utils/createContextAndHook.js.map +1 -0
  80. package/dist/esm/utils/throttle.d.ts +6 -0
  81. package/dist/esm/utils/throttle.js +47 -0
  82. package/dist/esm/utils/throttle.js.map +1 -0
  83. package/dist/esm/utils/trigger-swr.d.ts +19 -0
  84. package/dist/esm/utils/trigger-swr.js +6 -0
  85. package/dist/esm/utils/trigger-swr.js.map +1 -0
  86. package/dist/esm/utils/useStableRequestCallback.d.ts +5 -0
  87. package/dist/esm/utils/useStableRequestCallback.js +14 -0
  88. package/dist/esm/utils/useStableRequestCallback.js.map +1 -0
  89. package/package.json +75 -0
@@ -0,0 +1,22 @@
1
+ import type { AnyTask, RetrieveRunResult } from "@trigger.dev/core/v3";
2
+ import type { CommonTriggerHookOptions } from "../utils/trigger-swr.js";
3
+ /**
4
+ * Custom hook to retrieve and manage the state of a run by its ID.
5
+ *
6
+ * @template TTask - The type of the task associated with the run.
7
+ * @param {string} runId - The unique identifier of the run to retrieve.
8
+ * @param {CommonTriggerHookOptions} [options] - Optional configuration for the hook's behavior.
9
+ * @returns {Object} An object containing the run data, error, loading state, validation state, and error state.
10
+ * @returns {RetrieveRunResult<TTask> | undefined} run - The retrieved run data.
11
+ * @returns {Error | undefined} error - The error object if an error occurred.
12
+ * @returns {boolean} isLoading - Indicates if the run data is currently being loaded.
13
+ * @returns {boolean} isValidating - Indicates if the run data is currently being validated.
14
+ * @returns {boolean} isError - Indicates if an error occurred during the retrieval of the run data.
15
+ */
16
+ export declare function useRun<TTask extends AnyTask>(runId: string, options?: CommonTriggerHookOptions): {
17
+ run: RetrieveRunResult<TTask> | undefined;
18
+ error: Error | undefined;
19
+ isLoading: boolean;
20
+ isValidating: boolean;
21
+ isError: boolean;
22
+ };
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { useSWR } from "../utils/trigger-swr.js";
3
+ import { useApiClient } from "./useApiClient.js";
4
+ /**
5
+ * Custom hook to retrieve and manage the state of a run by its ID.
6
+ *
7
+ * @template TTask - The type of the task associated with the run.
8
+ * @param {string} runId - The unique identifier of the run to retrieve.
9
+ * @param {CommonTriggerHookOptions} [options] - Optional configuration for the hook's behavior.
10
+ * @returns {Object} An object containing the run data, error, loading state, validation state, and error state.
11
+ * @returns {RetrieveRunResult<TTask> | undefined} run - The retrieved run data.
12
+ * @returns {Error | undefined} error - The error object if an error occurred.
13
+ * @returns {boolean} isLoading - Indicates if the run data is currently being loaded.
14
+ * @returns {boolean} isValidating - Indicates if the run data is currently being validated.
15
+ * @returns {boolean} isError - Indicates if an error occurred during the retrieval of the run data.
16
+ */
17
+ export function useRun(runId, options) {
18
+ const apiClient = useApiClient(options);
19
+ const { data: run, error, isLoading, isValidating, } = useSWR(runId, () => {
20
+ if (!apiClient) {
21
+ throw new Error("Could not call useRun: Missing access token");
22
+ }
23
+ return apiClient.retrieveRun(runId);
24
+ }, {
25
+ revalidateOnReconnect: options?.revalidateOnReconnect,
26
+ refreshInterval: (run) => {
27
+ if (!run)
28
+ return options?.refreshInterval ?? 0;
29
+ if (run.isCompleted)
30
+ return 0;
31
+ return options?.refreshInterval ?? 0;
32
+ },
33
+ revalidateOnFocus: options?.revalidateOnFocus,
34
+ });
35
+ return { run, error, isLoading, isValidating, isError: !!error };
36
+ }
37
+ //# sourceMappingURL=useRun.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useRun.js","sourceRoot":"","sources":["../../../src/hooks/useRun.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAIb,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,MAAM,CACpB,KAAa,EACb,OAAkC;IAQlC,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACxC,MAAM,EACJ,IAAI,EAAE,GAAG,EACT,KAAK,EACL,SAAS,EACT,YAAY,GACb,GAAG,MAAM,CACR,KAAK,EACL,GAAG,EAAE;QACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,EACD;QACE,qBAAqB,EAAE,OAAO,EAAE,qBAAqB;QACrD,eAAe,EAAE,CAAC,GAAG,EAAE,EAAE;YACvB,IAAI,CAAC,GAAG;gBAAE,OAAO,OAAO,EAAE,eAAe,IAAI,CAAC,CAAC;YAE/C,IAAI,GAAG,CAAC,WAAW;gBAAE,OAAO,CAAC,CAAC;YAE9B,OAAO,OAAO,EAAE,eAAe,IAAI,CAAC,CAAC;QACvC,CAAC;QACD,iBAAiB,EAAE,OAAO,EAAE,iBAAiB;KAC9C,CACF,CAAC;IAEF,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;AACnE,CAAC"}
@@ -0,0 +1,131 @@
1
+ import type { ControlEvent, SSEStreamPart } from "@trigger.dev/core/v3";
2
+ import type { UseApiClientOptions } from "./useApiClient.js";
3
+ export type UseSessionStreamInstance<TRecord> = {
4
+ /**
5
+ * The records received so far on the channel, in arrival order. Control records are
6
+ * never included here, they are delivered to `onControl` instead.
7
+ */
8
+ records: Array<TRecord>;
9
+ /**
10
+ * The cursor of the last record seen. Persist this and pass it back as the `lastEventId`
11
+ * option to resume the channel where you left off.
12
+ */
13
+ lastEventId: string | undefined;
14
+ /**
15
+ * The last control record seen on the channel (e.g. `turn-complete`).
16
+ */
17
+ lastControl: ControlEvent | undefined;
18
+ error: Error | undefined;
19
+ /**
20
+ * Abort the current request immediately, keep the records received so far.
21
+ */
22
+ stop: () => void;
23
+ };
24
+ export type UseSessionStreamOptions<TRecord> = UseApiClientOptions & {
25
+ id?: string;
26
+ enabled?: boolean;
27
+ /**
28
+ * Which channel of the session to read.
29
+ *
30
+ * @default "out"
31
+ */
32
+ io?: "out" | "in";
33
+ /**
34
+ * The number of milliseconds to throttle the record updates.
35
+ *
36
+ * @default 16
37
+ */
38
+ throttleInMs?: number;
39
+ /**
40
+ * The number of seconds to wait for new data to be available,
41
+ * If no data arrives within the timeout, the stream will be closed.
42
+ *
43
+ * @default 60 seconds
44
+ */
45
+ timeoutInSeconds?: number;
46
+ /**
47
+ * The cursor to resume from. If not provided, the channel is read from the beginning.
48
+ */
49
+ lastEventId?: string | number;
50
+ /**
51
+ * Where a fresh subscription (no `lastEventId`) starts reading.
52
+ *
53
+ * - `"beginning"` (default): replay the full channel history, then live-tail.
54
+ * - `"latest"`: start at the current tail (the latest record, then live
55
+ * updates) instead of replaying history, for a last-value / live view.
56
+ *
57
+ * Ignored when `lastEventId` is set.
58
+ */
59
+ from?: "beginning" | "latest";
60
+ /**
61
+ * Cap the number of records kept in the accumulated `records` array. When more
62
+ * than `maxRecords` have been received, only the most recent `maxRecords` are
63
+ * retained. Use `maxRecords: 1` with `from: "latest"` for a last-value view
64
+ * with bounded memory. When unset, `records` accumulates without bound.
65
+ */
66
+ maxRecords?: number;
67
+ /**
68
+ * Callback invoked once per throttled flush with the batch of records in that
69
+ * flush, each carrying its event `id`, `chunk` and `timestamp`. Fires at the
70
+ * `throttleInMs` cadence (not per record) and includes control records, so it
71
+ * can track the resume cursor for everything on the channel.
72
+ */
73
+ onRecords?: (records: Array<SSEStreamPart<TRecord>>) => void;
74
+ /**
75
+ * Callback this is called when a control record is received (e.g. `turn-complete`).
76
+ */
77
+ onControl?: (event: ControlEvent) => void;
78
+ };
79
+ /**
80
+ * Hook to read one channel of a Session's realtime stream.
81
+ *
82
+ * This hook subscribes to one of the session's channels (`out` by default, or `in`) and
83
+ * updates the `records` array as new records arrive. It is read-only: use `useSession` for
84
+ * two-way (read and write) communication. The subscription is automatically managed: it
85
+ * starts when the component mounts (or when `enabled` becomes `true`) and stops when the
86
+ * component unmounts or when `stop()` is called.
87
+ *
88
+ * Requires a Public Access Token with the `read:sessions:{id}` scope.
89
+ *
90
+ * @template TRecord - The type of each record on the channel
91
+ * @param sessionIdOrExternalId - The id or external id of the session to subscribe to
92
+ * @param options - Optional configuration for the subscription
93
+ * @returns An object containing:
94
+ * - `records`: An array of all the records received so far (accumulates over time)
95
+ * - `lastEventId`: The cursor of the last record seen, for resuming later
96
+ * - `lastControl`: The last control record seen
97
+ * - `error`: Any error that occurred during subscription
98
+ * - `stop`: A function to manually stop the subscription
99
+ *
100
+ * @example
101
+ * ```tsx
102
+ * "use client";
103
+ * import { useSessionStream } from "@trigger.dev/react-hooks";
104
+ *
105
+ * function SessionViewer({ sessionId }: { sessionId: string }) {
106
+ * const { records, error } = useSessionStream<string>(sessionId, {
107
+ * accessToken: publicAccessToken,
108
+ * });
109
+ *
110
+ * if (error) return <div>Error: {error.message}</div>;
111
+ *
112
+ * return <div>{records.join("")}</div>;
113
+ * }
114
+ * ```
115
+ *
116
+ * @example
117
+ * ```tsx
118
+ * // Read the input channel, resuming from a persisted cursor
119
+ * const { records, lastEventId, stop } = useSessionStream<MyRecord>(sessionId, {
120
+ * accessToken: publicAccessToken,
121
+ * io: "in",
122
+ * lastEventId: persistedCursor,
123
+ * onControl: (event) => {
124
+ * if (event.subtype === "turn-complete") {
125
+ * console.log("The turn is complete");
126
+ * }
127
+ * },
128
+ * });
129
+ * ```
130
+ */
131
+ export declare function useSessionStream<TRecord = unknown>(sessionIdOrExternalId?: string, options?: UseSessionStreamOptions<TRecord>): UseSessionStreamInstance<TRecord>;
@@ -0,0 +1,233 @@
1
+ "use client";
2
+ import { useCallback, useEffect, useId, useRef, useState } from "react";
3
+ import { createThrottledQueue } from "../utils/throttle.js";
4
+ import { useSWR } from "../utils/trigger-swr.js";
5
+ import { useStableRequestCallback } from "../utils/useStableRequestCallback.js";
6
+ import { useApiClient } from "./useApiClient.js";
7
+ /**
8
+ * Hook to read one channel of a Session's realtime stream.
9
+ *
10
+ * This hook subscribes to one of the session's channels (`out` by default, or `in`) and
11
+ * updates the `records` array as new records arrive. It is read-only: use `useSession` for
12
+ * two-way (read and write) communication. The subscription is automatically managed: it
13
+ * starts when the component mounts (or when `enabled` becomes `true`) and stops when the
14
+ * component unmounts or when `stop()` is called.
15
+ *
16
+ * Requires a Public Access Token with the `read:sessions:{id}` scope.
17
+ *
18
+ * @template TRecord - The type of each record on the channel
19
+ * @param sessionIdOrExternalId - The id or external id of the session to subscribe to
20
+ * @param options - Optional configuration for the subscription
21
+ * @returns An object containing:
22
+ * - `records`: An array of all the records received so far (accumulates over time)
23
+ * - `lastEventId`: The cursor of the last record seen, for resuming later
24
+ * - `lastControl`: The last control record seen
25
+ * - `error`: Any error that occurred during subscription
26
+ * - `stop`: A function to manually stop the subscription
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * "use client";
31
+ * import { useSessionStream } from "@trigger.dev/react-hooks";
32
+ *
33
+ * function SessionViewer({ sessionId }: { sessionId: string }) {
34
+ * const { records, error } = useSessionStream<string>(sessionId, {
35
+ * accessToken: publicAccessToken,
36
+ * });
37
+ *
38
+ * if (error) return <div>Error: {error.message}</div>;
39
+ *
40
+ * return <div>{records.join("")}</div>;
41
+ * }
42
+ * ```
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // Read the input channel, resuming from a persisted cursor
47
+ * const { records, lastEventId, stop } = useSessionStream<MyRecord>(sessionId, {
48
+ * accessToken: publicAccessToken,
49
+ * io: "in",
50
+ * lastEventId: persistedCursor,
51
+ * onControl: (event) => {
52
+ * if (event.subtype === "turn-complete") {
53
+ * console.log("The turn is complete");
54
+ * }
55
+ * },
56
+ * });
57
+ * ```
58
+ */
59
+ export function useSessionStream(sessionIdOrExternalId, options) {
60
+ const hookId = useId();
61
+ const idKey = options?.id ?? hookId;
62
+ const io = options?.io ?? "out";
63
+ const [initialRecordsFallback] = useState([]);
64
+ const { data: records, mutate: mutateRecords } = useSWR([idKey, sessionIdOrExternalId, io, "records"], null, {
65
+ fallbackData: initialRecordsFallback,
66
+ });
67
+ const recordsRef = useRef(records ?? []);
68
+ useEffect(() => {
69
+ recordsRef.current = records || [];
70
+ }, [records]);
71
+ const { data: lastEventId = undefined, mutate: setLastEventId } = useSWR([idKey, sessionIdOrExternalId, io, "lastEventId"], null);
72
+ const lastEventIdRef = useRef(lastEventId);
73
+ const channelIdentityRef = useRef(`${idKey}:${sessionIdOrExternalId}:${io}`);
74
+ useEffect(() => {
75
+ const identity = `${idKey}:${sessionIdOrExternalId}:${io}`;
76
+ if (channelIdentityRef.current !== identity) {
77
+ channelIdentityRef.current = identity;
78
+ lastEventIdRef.current = lastEventId;
79
+ }
80
+ }, [idKey, sessionIdOrExternalId, io, lastEventId]);
81
+ const { data: lastControl = undefined, mutate: setLastControl } = useSWR([idKey, sessionIdOrExternalId, io, "lastControl"], null);
82
+ const { data: _isComplete = false, mutate: setIsComplete } = useSWR([idKey, sessionIdOrExternalId, io, "complete"], null);
83
+ const { data: error = undefined, mutate: setError } = useSWR([idKey, sessionIdOrExternalId, io, "error"], null);
84
+ const abortControllerRef = useRef(null);
85
+ const stop = useCallback(() => {
86
+ if (abortControllerRef.current) {
87
+ abortControllerRef.current.abort();
88
+ abortControllerRef.current = null;
89
+ }
90
+ }, []);
91
+ const onRecordsCallback = options?.onRecords;
92
+ const onRecords = useCallback((recordsBatch) => {
93
+ if (onRecordsCallback) {
94
+ onRecordsCallback(recordsBatch);
95
+ }
96
+ }, [onRecordsCallback]);
97
+ const onControlCallback = options?.onControl;
98
+ const onControl = useCallback((event) => {
99
+ if (onControlCallback) {
100
+ onControlCallback(event);
101
+ }
102
+ }, [onControlCallback]);
103
+ const apiClient = useApiClient(options);
104
+ const timeoutInSeconds = options?.timeoutInSeconds;
105
+ const startEventId = options?.lastEventId;
106
+ const throttleInMs = options?.throttleInMs;
107
+ const from = options?.from;
108
+ const maxRecords = options?.maxRecords;
109
+ useEffect(() => {
110
+ if (maxRecords != null && maxRecords >= 0) {
111
+ const current = recordsRef.current;
112
+ if (current.length > maxRecords) {
113
+ mutateRecords(current.slice(current.length - maxRecords));
114
+ }
115
+ }
116
+ }, [maxRecords, mutateRecords]);
117
+ const triggerRequest = useCallback(async () => {
118
+ try {
119
+ if (!sessionIdOrExternalId || !apiClient) {
120
+ return;
121
+ }
122
+ const abortController = new AbortController();
123
+ abortControllerRef.current = abortController;
124
+ await processSessionStream(sessionIdOrExternalId, io, apiClient, mutateRecords, recordsRef, setLastEventId, setLastControl, setError, onRecords, onControl, abortControllerRef, timeoutInSeconds, startEventId !== undefined ? String(startEventId) : lastEventIdRef.current, throttleInMs ?? 16, from, maxRecords);
125
+ }
126
+ catch (err) {
127
+ if (err.name === "AbortError") {
128
+ abortControllerRef.current = null;
129
+ return;
130
+ }
131
+ setError(err);
132
+ }
133
+ finally {
134
+ if (abortControllerRef.current) {
135
+ abortControllerRef.current = null;
136
+ }
137
+ setIsComplete(true);
138
+ }
139
+ }, [
140
+ sessionIdOrExternalId,
141
+ io,
142
+ apiClient,
143
+ mutateRecords,
144
+ setLastEventId,
145
+ setLastControl,
146
+ setError,
147
+ setIsComplete,
148
+ onRecords,
149
+ onControl,
150
+ timeoutInSeconds,
151
+ startEventId,
152
+ throttleInMs,
153
+ from,
154
+ maxRecords,
155
+ ]);
156
+ const requestSubscription = useStableRequestCallback(triggerRequest);
157
+ useEffect(() => {
158
+ if (typeof options?.enabled === "boolean" && !options.enabled) {
159
+ return;
160
+ }
161
+ if (!sessionIdOrExternalId) {
162
+ return;
163
+ }
164
+ requestSubscription().finally(() => { });
165
+ return () => {
166
+ stop();
167
+ };
168
+ }, [sessionIdOrExternalId, io, stop, options?.enabled, requestSubscription]);
169
+ return { records: records ?? initialRecordsFallback, lastEventId, lastControl, error, stop };
170
+ }
171
+ async function processSessionStream(sessionIdOrExternalId, io, apiClient, mutateRecordsData, existingRecordsRef, setLastEventId, setLastControl, onError, onRecords, onControl, abortControllerRef, timeoutInSeconds, lastEventId, throttleInMs, from, maxRecords) {
172
+ let lastSeenEventId;
173
+ let publishedEventId;
174
+ let partsBatch = [];
175
+ const publishLastEventId = () => {
176
+ if (lastSeenEventId !== publishedEventId) {
177
+ publishedEventId = lastSeenEventId;
178
+ setLastEventId(lastSeenEventId);
179
+ }
180
+ };
181
+ const flushParts = () => {
182
+ if (partsBatch.length === 0)
183
+ return;
184
+ const batch = partsBatch;
185
+ partsBatch = [];
186
+ onRecords(batch);
187
+ };
188
+ try {
189
+ const stream = await apiClient.subscribeToSessionStream(sessionIdOrExternalId, io, {
190
+ signal: abortControllerRef.current?.signal,
191
+ timeoutInSeconds,
192
+ lastEventId,
193
+ from,
194
+ onPart: (part) => {
195
+ lastSeenEventId = part.id;
196
+ partsBatch.push(part);
197
+ },
198
+ onControl: (event) => {
199
+ setLastControl(event);
200
+ onControl(event);
201
+ },
202
+ });
203
+ const recordsQueue = createThrottledQueue(async (newRecords) => {
204
+ const combined = [...existingRecordsRef.current, ...newRecords];
205
+ const bounded = maxRecords != null && maxRecords >= 0 && combined.length > maxRecords
206
+ ? combined.slice(combined.length - maxRecords)
207
+ : combined;
208
+ existingRecordsRef.current = bounded;
209
+ mutateRecordsData(bounded);
210
+ publishLastEventId();
211
+ flushParts();
212
+ }, throttleInMs);
213
+ for await (const record of stream) {
214
+ recordsQueue.add(record);
215
+ }
216
+ await recordsQueue.flush();
217
+ publishLastEventId();
218
+ flushParts();
219
+ }
220
+ catch (err) {
221
+ if (err.name === "AbortError") {
222
+ return;
223
+ }
224
+ if (err instanceof Error) {
225
+ onError(err);
226
+ }
227
+ else {
228
+ onError(new Error(String(err)));
229
+ }
230
+ throw err;
231
+ }
232
+ }
233
+ //# sourceMappingURL=useSessionStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSessionStream.js","sourceRoot":"","sources":["../../../src/hooks/useSessionStream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import type { AnySessionChannel, ControlEvent, SessionChannelIn, SessionChannelName, SessionChannelOut, SSEStreamPart } from "@trigger.dev/core/v3";
2
+ import type { UseApiClientOptions } from "./useApiClient.js";
3
+ type ChannelRecord<TChannel extends AnySessionChannel, S extends "in" | "out"> = S extends "out" ? SessionChannelOut<TChannel> : SessionChannelIn<TChannel>;
4
+ export type UseSessionStreamChannelInstance<TRecord> = {
5
+ /** The records received so far on the channel, in arrival order. */
6
+ records: Array<TRecord>;
7
+ /** The cursor of the last record seen; pass back as `lastEventId` to resume. */
8
+ lastEventId: string | undefined;
9
+ /** The last control record seen on the channel. */
10
+ lastControl: ControlEvent | undefined;
11
+ error: Error | undefined;
12
+ /** Abort the current request immediately, keep the records received so far. */
13
+ stop: () => void;
14
+ };
15
+ export type UseSessionStreamChannelOptions<TChannel extends AnySessionChannel, S extends "in" | "out"> = UseApiClientOptions & {
16
+ /**
17
+ * The id or external id of the session that owns the channel. May be
18
+ * undefined while it resolves; the subscription starts once it is set.
19
+ */
20
+ sessionId?: string;
21
+ id?: string;
22
+ enabled?: boolean;
23
+ /**
24
+ * Which side of the channel to read.
25
+ *
26
+ * @default "out"
27
+ */
28
+ io?: S;
29
+ /**
30
+ * The number of milliseconds to throttle the record updates.
31
+ *
32
+ * @default 16
33
+ */
34
+ throttleInMs?: number;
35
+ /**
36
+ * The number of seconds to wait for new data before the stream closes.
37
+ *
38
+ * @default 60 seconds
39
+ */
40
+ timeoutInSeconds?: number;
41
+ /** The cursor to resume from. If not provided, reads per `from`. */
42
+ lastEventId?: string | number;
43
+ /**
44
+ * Where a fresh subscription (no `lastEventId`) starts reading.
45
+ *
46
+ * - `"beginning"` (default): replay the full channel history, then live-tail.
47
+ * - `"latest"`: start at the current tail, for a last-value / live view.
48
+ *
49
+ * Ignored when `lastEventId` is set.
50
+ */
51
+ from?: "beginning" | "latest";
52
+ /**
53
+ * Cap the number of records kept in `records`. Use `maxRecords: 1` with
54
+ * `from: "latest"` for a bounded last-value view.
55
+ */
56
+ maxRecords?: number;
57
+ /** Invoked once per throttled flush with the batch of records (control records included). */
58
+ onRecords?: (records: Array<SSEStreamPart<ChannelRecord<TChannel, S>>>) => void;
59
+ /** Called when a control record is received on the channel. */
60
+ onControl?: (event: ControlEvent) => void;
61
+ };
62
+ /**
63
+ * Read one side of a named Session side channel, with record types inferred
64
+ * from a `defineSessionChannel` declaration passed as the type argument.
65
+ *
66
+ * The channel name is typesafe (`SessionChannelName<TChannel>`) and `records`
67
+ * is typed from the channel's `.out` / `.in` record type. Called without the
68
+ * type argument, the channel name is any string and `records` is `unknown`.
69
+ *
70
+ * Requires a Public Access Token scoped to the session (or to the channel).
71
+ *
72
+ * @example
73
+ * ```tsx
74
+ * import type { screenshotsChannel } from "./shared/channels";
75
+ *
76
+ * const { records } = useSessionStreamChannel<typeof screenshotsChannel>("screenshots", {
77
+ * sessionId,
78
+ * accessToken,
79
+ * io: "out",
80
+ * from: "latest",
81
+ * maxRecords: 1,
82
+ * });
83
+ * ```
84
+ */
85
+ export declare function useSessionStreamChannel<TChannel extends AnySessionChannel = AnySessionChannel, S extends "in" | "out" = "out">(channel: SessionChannelName<TChannel>, options: UseSessionStreamChannelOptions<TChannel, S>): UseSessionStreamChannelInstance<ChannelRecord<TChannel, S>>;
86
+ export {};