@restless-stream/react 0.1.0

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 ADDED
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ DEFAULT_MAX_EVENTS: () => DEFAULT_MAX_EVENTS,
24
+ RestlessProvider: () => RestlessProvider,
25
+ useDirectStream: () => useDirectStream,
26
+ useManagedStream: () => useManagedStream,
27
+ useRestlessClient: () => useRestlessClient,
28
+ useStreamSubscription: () => useStreamSubscription
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // src/provider.ts
33
+ var import_core = require("@restless-stream/core");
34
+ var import_react = require("react");
35
+ var RestlessClientContext = (0, import_react.createContext)(null);
36
+ function RestlessProvider({
37
+ apiBaseUrl,
38
+ apiKey,
39
+ children,
40
+ client,
41
+ config,
42
+ fetch,
43
+ headers,
44
+ streamBaseUrl
45
+ }) {
46
+ const inlineConfig = (0, import_react.useMemo)(
47
+ () => compactConfig({
48
+ apiBaseUrl,
49
+ apiKey,
50
+ fetch,
51
+ headers,
52
+ streamBaseUrl
53
+ }),
54
+ [apiBaseUrl, apiKey, fetch, headers, streamBaseUrl]
55
+ );
56
+ const providerConfig = config ?? inlineConfig;
57
+ const contextClient = (0, import_react.useMemo)(() => client ?? (0, import_core.createRestlessClient)(providerConfig), [client, providerConfig]);
58
+ return (0, import_react.createElement)(RestlessClientContext.Provider, { value: contextClient }, children);
59
+ }
60
+ function useRestlessClient() {
61
+ const client = (0, import_react.useContext)(RestlessClientContext);
62
+ if (!client) {
63
+ throw new Error("useRestlessClient must be used within a RestlessProvider or with an explicit client option.");
64
+ }
65
+ return client;
66
+ }
67
+ function useOptionalRestlessClient() {
68
+ return (0, import_react.useContext)(RestlessClientContext);
69
+ }
70
+ function compactConfig(config) {
71
+ return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== void 0));
72
+ }
73
+
74
+ // src/stream-subscription.ts
75
+ var import_react2 = require("react");
76
+
77
+ // src/stream-subscription-types.ts
78
+ var DEFAULT_MAX_EVENTS = 100;
79
+
80
+ // src/stream-subscription-utils.ts
81
+ function isNonEmptyString(value) {
82
+ return typeof value === "string" && value.length > 0;
83
+ }
84
+ function appendEvent(currentEvents, event, maxEvents) {
85
+ if (maxEvents === 0) {
86
+ return [];
87
+ }
88
+ const nextEvents = [...currentEvents, event];
89
+ return nextEvents.length > maxEvents ? nextEvents.slice(nextEvents.length - maxEvents) : nextEvents;
90
+ }
91
+ function normalizeMaxEvents(maxEvents) {
92
+ if (!Number.isFinite(maxEvents)) {
93
+ return DEFAULT_MAX_EVENTS;
94
+ }
95
+ return Math.max(0, Math.floor(maxEvents));
96
+ }
97
+ function resolveReconnectDelay(delay, attempt) {
98
+ const resolvedDelay = typeof delay === "function" ? delay(attempt) : delay;
99
+ return Number.isFinite(resolvedDelay) ? Math.max(0, resolvedDelay) : 0;
100
+ }
101
+ async function sleep(milliseconds, signal) {
102
+ if (milliseconds === 0 || signal.aborted) {
103
+ return;
104
+ }
105
+ return new Promise((resolve) => {
106
+ const timeout = setTimeout(resolve, milliseconds);
107
+ signal.addEventListener(
108
+ "abort",
109
+ () => {
110
+ clearTimeout(timeout);
111
+ resolve();
112
+ },
113
+ { once: true }
114
+ );
115
+ });
116
+ }
117
+ function stableOptionsKey(options) {
118
+ return JSON.stringify(sortRecord(options));
119
+ }
120
+ function toError(error) {
121
+ if (error instanceof Error) {
122
+ return error;
123
+ }
124
+ return new Error(typeof error === "string" ? error : "Restless stream subscription failed.");
125
+ }
126
+ function sortRecord(value) {
127
+ if (Array.isArray(value)) {
128
+ return value.map((item) => sortRecord(item));
129
+ }
130
+ if (typeof value === "function") {
131
+ return value.toString();
132
+ }
133
+ if (value === null || value === void 0 || typeof value !== "object") {
134
+ return value;
135
+ }
136
+ return Object.fromEntries(
137
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nestedValue]) => [key, sortRecord(nestedValue)])
138
+ );
139
+ }
140
+
141
+ // src/stream-subscription.ts
142
+ function useStreamSubscription(options) {
143
+ const {
144
+ client: explicitClient,
145
+ enabled = true,
146
+ maxEvents = DEFAULT_MAX_EVENTS,
147
+ maxReconnectAttempts = 0,
148
+ onError,
149
+ onEvent,
150
+ reconnect = false,
151
+ reconnectDelayMs = 1e3,
152
+ ...subscribeOptions
153
+ } = options;
154
+ const contextClient = useOptionalRestlessClient();
155
+ const client = explicitClient ?? contextClient;
156
+ if (!client) {
157
+ throw new Error("useStreamSubscription requires a RestlessProvider or an explicit client option.");
158
+ }
159
+ const subscriptionClient = client;
160
+ const boundedMaxEvents = normalizeMaxEvents(maxEvents);
161
+ const subscribeOptionsKey = stableOptionsKey(subscribeOptions);
162
+ const abortControllerRef = (0, import_react2.useRef)(null);
163
+ const onErrorRef = (0, import_react2.useRef)(onError);
164
+ const onEventRef = (0, import_react2.useRef)(onEvent);
165
+ const reconnectDelayRef = (0, import_react2.useRef)(reconnectDelayMs);
166
+ const subscribeOptionsRef = (0, import_react2.useRef)(subscribeOptions);
167
+ const [connectionToken, setConnectionToken] = (0, import_react2.useState)(0);
168
+ const [events, setEvents] = (0, import_react2.useState)([]);
169
+ const [error, setError] = (0, import_react2.useState)(null);
170
+ const [shouldConnect, setShouldConnect] = (0, import_react2.useState)(enabled);
171
+ const [status, setStatus] = (0, import_react2.useState)(enabled ? "connecting" : "idle");
172
+ onErrorRef.current = onError;
173
+ onEventRef.current = onEvent;
174
+ reconnectDelayRef.current = reconnectDelayMs;
175
+ subscribeOptionsRef.current = subscribeOptions;
176
+ (0, import_react2.useEffect)(() => {
177
+ setShouldConnect(enabled);
178
+ setStatus((currentStatus) => {
179
+ if (enabled) {
180
+ return currentStatus === "idle" || currentStatus === "closed" ? "connecting" : currentStatus;
181
+ }
182
+ return "idle";
183
+ });
184
+ }, [enabled]);
185
+ const connect = (0, import_react2.useCallback)(() => {
186
+ setError(null);
187
+ setShouldConnect(true);
188
+ setStatus((currentStatus) => currentStatus === "open" ? currentStatus : "connecting");
189
+ setConnectionToken((currentToken) => currentToken + 1);
190
+ }, []);
191
+ const disconnect = (0, import_react2.useCallback)(() => {
192
+ setShouldConnect(false);
193
+ abortControllerRef.current?.abort();
194
+ abortControllerRef.current = null;
195
+ setStatus("closed");
196
+ }, []);
197
+ const reset = (0, import_react2.useCallback)(() => {
198
+ setEvents([]);
199
+ setError(null);
200
+ }, []);
201
+ (0, import_react2.useEffect)(() => {
202
+ if (!shouldConnect) {
203
+ return;
204
+ }
205
+ let cancelled = false;
206
+ const abortController = new AbortController();
207
+ abortControllerRef.current?.abort();
208
+ abortControllerRef.current = abortController;
209
+ async function subscribe() {
210
+ let reconnectAttempt = 0;
211
+ while (!cancelled) {
212
+ setStatus(reconnectAttempt > 0 ? "reconnecting" : "connecting");
213
+ try {
214
+ const stream = subscriptionClient.streams.subscribeSse({
215
+ ...subscribeOptionsRef.current,
216
+ signal: abortController.signal
217
+ });
218
+ setError(null);
219
+ setStatus("open");
220
+ for await (const event of stream) {
221
+ if (cancelled) {
222
+ break;
223
+ }
224
+ onEventRef.current?.(event);
225
+ setEvents((currentEvents) => appendEvent(currentEvents, event, boundedMaxEvents));
226
+ }
227
+ if (!cancelled) {
228
+ setStatus("closed");
229
+ }
230
+ break;
231
+ } catch (caughtError) {
232
+ if (cancelled || abortController.signal.aborted) {
233
+ break;
234
+ }
235
+ const normalizedError = toError(caughtError);
236
+ setError(normalizedError);
237
+ onErrorRef.current?.(normalizedError);
238
+ if (!reconnect || reconnectAttempt >= maxReconnectAttempts) {
239
+ setStatus("error");
240
+ break;
241
+ }
242
+ reconnectAttempt += 1;
243
+ setStatus("reconnecting");
244
+ await sleep(resolveReconnectDelay(reconnectDelayRef.current, reconnectAttempt), abortController.signal);
245
+ }
246
+ }
247
+ }
248
+ void subscribe();
249
+ return () => {
250
+ cancelled = true;
251
+ abortController.abort();
252
+ if (abortControllerRef.current === abortController) {
253
+ abortControllerRef.current = null;
254
+ }
255
+ };
256
+ }, [
257
+ boundedMaxEvents,
258
+ connectionToken,
259
+ maxReconnectAttempts,
260
+ reconnect,
261
+ shouldConnect,
262
+ subscribeOptionsKey,
263
+ subscriptionClient
264
+ ]);
265
+ const latestEvent = events.at(-1) ?? null;
266
+ return (0, import_react2.useMemo)(
267
+ () => ({
268
+ connect,
269
+ disconnect,
270
+ error,
271
+ events,
272
+ isConnected: status === "open",
273
+ isConnecting: status === "connecting" || status === "reconnecting",
274
+ latestEvent,
275
+ reset,
276
+ status
277
+ }),
278
+ [connect, disconnect, error, events, latestEvent, reset, status]
279
+ );
280
+ }
281
+ function useManagedStream(options) {
282
+ const { sseUrl, stream, enabled, ...subscriptionOptions } = options;
283
+ const managedSseUrl = sseUrl ?? stream?.sseUrl ?? "";
284
+ const shouldEnable = typeof enabled === "boolean" ? enabled : isNonEmptyString(managedSseUrl);
285
+ return useStreamSubscription({
286
+ ...subscriptionOptions,
287
+ enabled: shouldEnable,
288
+ sseUrl: managedSseUrl
289
+ });
290
+ }
291
+ function useDirectStream(options) {
292
+ const { directUrl, enabled, session, sseUrl, ...subscriptionOptions } = options;
293
+ const directSseUrl = directUrl ?? sseUrl ?? session?.sseUrl ?? "";
294
+ const shouldEnable = typeof enabled === "boolean" ? enabled : isNonEmptyString(directSseUrl);
295
+ return useStreamSubscription({
296
+ ...subscriptionOptions,
297
+ enabled: shouldEnable,
298
+ sseUrl: directSseUrl
299
+ });
300
+ }
301
+ // Annotate the CommonJS export names for ESM import in node:
302
+ 0 && (module.exports = {
303
+ DEFAULT_MAX_EVENTS,
304
+ RestlessProvider,
305
+ useDirectStream,
306
+ useManagedStream,
307
+ useRestlessClient,
308
+ useStreamSubscription
309
+ });
310
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/provider.ts","../src/stream-subscription.ts","../src/stream-subscription-types.ts","../src/stream-subscription-utils.ts"],"sourcesContent":["export {\n RestlessProvider,\n type RestlessProviderProps,\n useRestlessClient,\n} from './provider.js';\nexport {\n DEFAULT_MAX_EVENTS,\n type StreamSubscriptionControls,\n type StreamSubscriptionStatus,\n type UseDirectStreamOptions,\n type UseManagedStreamOptions,\n type UseStreamSubscriptionOptions,\n type UseStreamSubscriptionResult,\n useDirectStream,\n useManagedStream,\n useStreamSubscription,\n} from './stream-subscription.js';\n","import { createRestlessClient, type RestlessClient, type RestlessClientConfig } from '@restless-stream/core';\nimport { createContext, createElement, type ReactNode, useContext, useMemo } from 'react';\n\nconst RestlessClientContext = createContext<RestlessClient | null>(null);\ntype InlineRestlessClientConfig = {\n [K in keyof RestlessClientConfig]-?: RestlessClientConfig[K] | undefined;\n};\n\nexport type RestlessProviderProps = Partial<RestlessClientConfig> & {\n children?: ReactNode;\n client?: RestlessClient;\n config?: RestlessClientConfig;\n};\n\nexport function RestlessProvider({\n apiBaseUrl,\n apiKey,\n children,\n client,\n config,\n fetch,\n headers,\n streamBaseUrl,\n}: RestlessProviderProps) {\n const inlineConfig = useMemo(\n () =>\n compactConfig({\n apiBaseUrl,\n apiKey,\n fetch,\n headers,\n streamBaseUrl,\n }),\n [apiBaseUrl, apiKey, fetch, headers, streamBaseUrl],\n );\n const providerConfig = config ?? inlineConfig;\n const contextClient = useMemo(() => client ?? createRestlessClient(providerConfig), [client, providerConfig]);\n\n return createElement(RestlessClientContext.Provider, { value: contextClient }, children);\n}\n\nexport function useRestlessClient(): RestlessClient {\n const client = useContext(RestlessClientContext);\n\n if (!client) {\n throw new Error('useRestlessClient must be used within a RestlessProvider or with an explicit client option.');\n }\n\n return client;\n}\n\nexport function useOptionalRestlessClient(): RestlessClient | null {\n return useContext(RestlessClientContext);\n}\n\nfunction compactConfig(config: InlineRestlessClientConfig): RestlessClientConfig {\n return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== undefined));\n}\n","import type { RestlessStreamEvent } from '@restless-stream/core';\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useOptionalRestlessClient } from './provider.js';\nimport {\n DEFAULT_MAX_EVENTS,\n type StreamSubscriptionStatus,\n type UseDirectStreamOptions,\n type UseManagedStreamOptions,\n type UseStreamSubscriptionOptions,\n type UseStreamSubscriptionResult,\n} from './stream-subscription-types.js';\nimport {\n appendEvent,\n isNonEmptyString,\n normalizeMaxEvents,\n resolveReconnectDelay,\n sleep,\n stableOptionsKey,\n toError,\n} from './stream-subscription-utils.js';\n\nexport { DEFAULT_MAX_EVENTS } from './stream-subscription-types.js';\nexport type {\n StreamSubscriptionControls,\n StreamSubscriptionStatus,\n UseDirectStreamOptions,\n UseManagedStreamOptions,\n UseStreamSubscriptionOptions,\n UseStreamSubscriptionResult,\n} from './stream-subscription-types.js';\n\nexport function useStreamSubscription(options: UseStreamSubscriptionOptions): UseStreamSubscriptionResult {\n const {\n client: explicitClient,\n enabled = true,\n maxEvents = DEFAULT_MAX_EVENTS,\n maxReconnectAttempts = 0,\n onError,\n onEvent,\n reconnect = false,\n reconnectDelayMs = 1000,\n ...subscribeOptions\n } = options;\n const contextClient = useOptionalRestlessClient();\n const client = explicitClient ?? contextClient;\n\n if (!client) {\n throw new Error('useStreamSubscription requires a RestlessProvider or an explicit client option.');\n }\n\n const subscriptionClient = client;\n\n const boundedMaxEvents = normalizeMaxEvents(maxEvents);\n const subscribeOptionsKey = stableOptionsKey(subscribeOptions);\n const abortControllerRef = useRef<AbortController | null>(null);\n const onErrorRef = useRef(onError);\n const onEventRef = useRef(onEvent);\n const reconnectDelayRef = useRef(reconnectDelayMs);\n const subscribeOptionsRef = useRef(subscribeOptions);\n const [connectionToken, setConnectionToken] = useState(0);\n const [events, setEvents] = useState<RestlessStreamEvent[]>([]);\n const [error, setError] = useState<Error | null>(null);\n const [shouldConnect, setShouldConnect] = useState(enabled);\n const [status, setStatus] = useState<StreamSubscriptionStatus>(enabled ? 'connecting' : 'idle');\n\n onErrorRef.current = onError;\n onEventRef.current = onEvent;\n reconnectDelayRef.current = reconnectDelayMs;\n subscribeOptionsRef.current = subscribeOptions;\n\n useEffect(() => {\n setShouldConnect(enabled);\n setStatus((currentStatus) => {\n if (enabled) {\n return currentStatus === 'idle' || currentStatus === 'closed' ? 'connecting' : currentStatus;\n }\n\n return 'idle';\n });\n }, [enabled]);\n\n const connect = useCallback(() => {\n setError(null);\n setShouldConnect(true);\n setStatus((currentStatus) => (currentStatus === 'open' ? currentStatus : 'connecting'));\n setConnectionToken((currentToken) => currentToken + 1);\n }, []);\n\n const disconnect = useCallback(() => {\n setShouldConnect(false);\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n setStatus('closed');\n }, []);\n\n const reset = useCallback(() => {\n setEvents([]);\n setError(null);\n }, []);\n\n useEffect(() => {\n if (!shouldConnect) {\n return;\n }\n\n let cancelled = false;\n const abortController = new AbortController();\n abortControllerRef.current?.abort();\n abortControllerRef.current = abortController;\n\n async function subscribe() {\n let reconnectAttempt = 0;\n\n while (!cancelled) {\n setStatus(reconnectAttempt > 0 ? 'reconnecting' : 'connecting');\n\n try {\n const stream = subscriptionClient.streams.subscribeSse({\n ...subscribeOptionsRef.current,\n signal: abortController.signal,\n });\n\n setError(null);\n setStatus('open');\n\n for await (const event of stream) {\n if (cancelled) {\n break;\n }\n\n onEventRef.current?.(event);\n setEvents((currentEvents) => appendEvent(currentEvents, event, boundedMaxEvents));\n }\n\n if (!cancelled) {\n setStatus('closed');\n }\n\n break;\n } catch (caughtError) {\n if (cancelled || abortController.signal.aborted) {\n break;\n }\n\n const normalizedError = toError(caughtError);\n setError(normalizedError);\n onErrorRef.current?.(normalizedError);\n\n if (!reconnect || reconnectAttempt >= maxReconnectAttempts) {\n setStatus('error');\n break;\n }\n\n reconnectAttempt += 1;\n setStatus('reconnecting');\n await sleep(resolveReconnectDelay(reconnectDelayRef.current, reconnectAttempt), abortController.signal);\n }\n }\n }\n\n void subscribe();\n\n return () => {\n cancelled = true;\n abortController.abort();\n\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n };\n }, [\n boundedMaxEvents,\n connectionToken,\n maxReconnectAttempts,\n reconnect,\n shouldConnect,\n subscribeOptionsKey,\n subscriptionClient,\n ]);\n\n const latestEvent = events.at(-1) ?? null;\n\n return useMemo(\n () => ({\n connect,\n disconnect,\n error,\n events,\n isConnected: status === 'open',\n isConnecting: status === 'connecting' || status === 'reconnecting',\n latestEvent,\n reset,\n status,\n }),\n [connect, disconnect, error, events, latestEvent, reset, status],\n );\n}\n\nexport function useManagedStream(options: UseManagedStreamOptions): UseStreamSubscriptionResult {\n const { sseUrl, stream, enabled, ...subscriptionOptions } = options;\n const managedSseUrl = sseUrl ?? stream?.sseUrl ?? '';\n const shouldEnable = typeof enabled === 'boolean' ? enabled : isNonEmptyString(managedSseUrl);\n\n return useStreamSubscription({\n ...subscriptionOptions,\n enabled: shouldEnable,\n sseUrl: managedSseUrl,\n });\n}\n\nexport function useDirectStream(options: UseDirectStreamOptions): UseStreamSubscriptionResult {\n const { directUrl, enabled, session, sseUrl, ...subscriptionOptions } = options;\n const directSseUrl = directUrl ?? sseUrl ?? session?.sseUrl ?? '';\n const shouldEnable = typeof enabled === 'boolean' ? enabled : isNonEmptyString(directSseUrl);\n\n return useStreamSubscription({\n ...subscriptionOptions,\n enabled: shouldEnable,\n sseUrl: directSseUrl,\n });\n}\n","import type { RestlessClient, RestlessStreamEvent, SubscribeSseOptions } from '@restless-stream/core';\n\nexport const DEFAULT_MAX_EVENTS = 100;\n\nexport type StreamSubscriptionStatus = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed' | 'error';\n\nexport interface StreamSubscriptionControls {\n connect: () => void;\n disconnect: () => void;\n reset: () => void;\n}\n\nexport type UseStreamSubscriptionOptions = Omit<SubscribeSseOptions, 'reconnect' | 'reconnectDelayMs'> & {\n client?: RestlessClient;\n enabled?: boolean;\n maxEvents?: number;\n maxReconnectAttempts?: number;\n onError?: (error: Error) => void;\n onEvent?: (event: RestlessStreamEvent) => void;\n reconnect?: boolean;\n reconnectDelayMs?: number | ((attempt: number) => number);\n};\n\nexport type UseStreamSubscriptionResult = StreamSubscriptionControls & {\n error: Error | null;\n events: RestlessStreamEvent[];\n isConnected: boolean;\n isConnecting: boolean;\n latestEvent: RestlessStreamEvent | null;\n status: StreamSubscriptionStatus;\n};\n\nexport type UseManagedStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {\n sseUrl?: SubscribeSseOptions['sseUrl'] | null;\n stream?: { sseUrl?: string | null } | null;\n};\n\nexport type UseDirectStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {\n directUrl?: string | null;\n session?: { sseUrl?: string | null } | null;\n sseUrl?: SubscribeSseOptions['sseUrl'] | null;\n};\n","import type { RestlessStreamEvent, SubscribeSseOptions } from '@restless-stream/core';\n\nimport { DEFAULT_MAX_EVENTS } from './stream-subscription-types.js';\n\nexport function isNonEmptyString(value: unknown): boolean {\n return typeof value === 'string' && value.length > 0;\n}\n\nexport function appendEvent(\n currentEvents: RestlessStreamEvent[],\n event: RestlessStreamEvent,\n maxEvents: number,\n): RestlessStreamEvent[] {\n if (maxEvents === 0) {\n return [];\n }\n\n const nextEvents = [...currentEvents, event];\n return nextEvents.length > maxEvents ? nextEvents.slice(nextEvents.length - maxEvents) : nextEvents;\n}\n\nexport function normalizeMaxEvents(maxEvents: number): number {\n if (!Number.isFinite(maxEvents)) {\n return DEFAULT_MAX_EVENTS;\n }\n\n return Math.max(0, Math.floor(maxEvents));\n}\n\nexport function resolveReconnectDelay(delay: number | ((attempt: number) => number), attempt: number): number {\n const resolvedDelay = typeof delay === 'function' ? delay(attempt) : delay;\n return Number.isFinite(resolvedDelay) ? Math.max(0, resolvedDelay) : 0;\n}\n\nexport async function sleep(milliseconds: number, signal: AbortSignal): Promise<void> {\n if (milliseconds === 0 || signal.aborted) {\n return;\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(resolve, milliseconds);\n\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeout);\n resolve();\n },\n { once: true },\n );\n });\n}\n\nexport function stableOptionsKey(options: Partial<SubscribeSseOptions>): string {\n return JSON.stringify(sortRecord(options));\n}\n\nexport function toError(error: unknown): Error {\n if (error instanceof Error) {\n return error;\n }\n\n return new Error(typeof error === 'string' ? error : 'Restless stream subscription failed.');\n}\n\nfunction sortRecord(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => sortRecord(item));\n }\n\n if (typeof value === 'function') {\n return value.toString();\n }\n\n if (value === null || value === undefined || typeof value !== 'object') {\n return value;\n }\n\n return Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nestedValue]) => [key, sortRecord(nestedValue)]),\n );\n}\n"],"mappings":"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","names":["import_react"]}
@@ -0,0 +1,56 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { RestlessClientConfig, RestlessClient, SubscribeSseOptions, RestlessStreamEvent } from '@restless-stream/core';
4
+
5
+ type RestlessProviderProps = Partial<RestlessClientConfig> & {
6
+ children?: ReactNode;
7
+ client?: RestlessClient;
8
+ config?: RestlessClientConfig;
9
+ };
10
+ declare function RestlessProvider({ apiBaseUrl, apiKey, children, client, config, fetch, headers, streamBaseUrl, }: RestlessProviderProps): react.FunctionComponentElement<react.ProviderProps<RestlessClient | null>>;
11
+ declare function useRestlessClient(): RestlessClient;
12
+
13
+ declare const DEFAULT_MAX_EVENTS = 100;
14
+ type StreamSubscriptionStatus = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed' | 'error';
15
+ interface StreamSubscriptionControls {
16
+ connect: () => void;
17
+ disconnect: () => void;
18
+ reset: () => void;
19
+ }
20
+ type UseStreamSubscriptionOptions = Omit<SubscribeSseOptions, 'reconnect' | 'reconnectDelayMs'> & {
21
+ client?: RestlessClient;
22
+ enabled?: boolean;
23
+ maxEvents?: number;
24
+ maxReconnectAttempts?: number;
25
+ onError?: (error: Error) => void;
26
+ onEvent?: (event: RestlessStreamEvent) => void;
27
+ reconnect?: boolean;
28
+ reconnectDelayMs?: number | ((attempt: number) => number);
29
+ };
30
+ type UseStreamSubscriptionResult = StreamSubscriptionControls & {
31
+ error: Error | null;
32
+ events: RestlessStreamEvent[];
33
+ isConnected: boolean;
34
+ isConnecting: boolean;
35
+ latestEvent: RestlessStreamEvent | null;
36
+ status: StreamSubscriptionStatus;
37
+ };
38
+ type UseManagedStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {
39
+ sseUrl?: SubscribeSseOptions['sseUrl'] | null;
40
+ stream?: {
41
+ sseUrl?: string | null;
42
+ } | null;
43
+ };
44
+ type UseDirectStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {
45
+ directUrl?: string | null;
46
+ session?: {
47
+ sseUrl?: string | null;
48
+ } | null;
49
+ sseUrl?: SubscribeSseOptions['sseUrl'] | null;
50
+ };
51
+
52
+ declare function useStreamSubscription(options: UseStreamSubscriptionOptions): UseStreamSubscriptionResult;
53
+ declare function useManagedStream(options: UseManagedStreamOptions): UseStreamSubscriptionResult;
54
+ declare function useDirectStream(options: UseDirectStreamOptions): UseStreamSubscriptionResult;
55
+
56
+ export { DEFAULT_MAX_EVENTS, RestlessProvider, type RestlessProviderProps, type StreamSubscriptionControls, type StreamSubscriptionStatus, type UseDirectStreamOptions, type UseManagedStreamOptions, type UseStreamSubscriptionOptions, type UseStreamSubscriptionResult, useDirectStream, useManagedStream, useRestlessClient, useStreamSubscription };
@@ -0,0 +1,56 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { RestlessClientConfig, RestlessClient, SubscribeSseOptions, RestlessStreamEvent } from '@restless-stream/core';
4
+
5
+ type RestlessProviderProps = Partial<RestlessClientConfig> & {
6
+ children?: ReactNode;
7
+ client?: RestlessClient;
8
+ config?: RestlessClientConfig;
9
+ };
10
+ declare function RestlessProvider({ apiBaseUrl, apiKey, children, client, config, fetch, headers, streamBaseUrl, }: RestlessProviderProps): react.FunctionComponentElement<react.ProviderProps<RestlessClient | null>>;
11
+ declare function useRestlessClient(): RestlessClient;
12
+
13
+ declare const DEFAULT_MAX_EVENTS = 100;
14
+ type StreamSubscriptionStatus = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed' | 'error';
15
+ interface StreamSubscriptionControls {
16
+ connect: () => void;
17
+ disconnect: () => void;
18
+ reset: () => void;
19
+ }
20
+ type UseStreamSubscriptionOptions = Omit<SubscribeSseOptions, 'reconnect' | 'reconnectDelayMs'> & {
21
+ client?: RestlessClient;
22
+ enabled?: boolean;
23
+ maxEvents?: number;
24
+ maxReconnectAttempts?: number;
25
+ onError?: (error: Error) => void;
26
+ onEvent?: (event: RestlessStreamEvent) => void;
27
+ reconnect?: boolean;
28
+ reconnectDelayMs?: number | ((attempt: number) => number);
29
+ };
30
+ type UseStreamSubscriptionResult = StreamSubscriptionControls & {
31
+ error: Error | null;
32
+ events: RestlessStreamEvent[];
33
+ isConnected: boolean;
34
+ isConnecting: boolean;
35
+ latestEvent: RestlessStreamEvent | null;
36
+ status: StreamSubscriptionStatus;
37
+ };
38
+ type UseManagedStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {
39
+ sseUrl?: SubscribeSseOptions['sseUrl'] | null;
40
+ stream?: {
41
+ sseUrl?: string | null;
42
+ } | null;
43
+ };
44
+ type UseDirectStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {
45
+ directUrl?: string | null;
46
+ session?: {
47
+ sseUrl?: string | null;
48
+ } | null;
49
+ sseUrl?: SubscribeSseOptions['sseUrl'] | null;
50
+ };
51
+
52
+ declare function useStreamSubscription(options: UseStreamSubscriptionOptions): UseStreamSubscriptionResult;
53
+ declare function useManagedStream(options: UseManagedStreamOptions): UseStreamSubscriptionResult;
54
+ declare function useDirectStream(options: UseDirectStreamOptions): UseStreamSubscriptionResult;
55
+
56
+ export { DEFAULT_MAX_EVENTS, RestlessProvider, type RestlessProviderProps, type StreamSubscriptionControls, type StreamSubscriptionStatus, type UseDirectStreamOptions, type UseManagedStreamOptions, type UseStreamSubscriptionOptions, type UseStreamSubscriptionResult, useDirectStream, useManagedStream, useRestlessClient, useStreamSubscription };
package/dist/index.js ADDED
@@ -0,0 +1,278 @@
1
+ // src/provider.ts
2
+ import { createRestlessClient } from "@restless-stream/core";
3
+ import { createContext, createElement, useContext, useMemo } from "react";
4
+ var RestlessClientContext = createContext(null);
5
+ function RestlessProvider({
6
+ apiBaseUrl,
7
+ apiKey,
8
+ children,
9
+ client,
10
+ config,
11
+ fetch,
12
+ headers,
13
+ streamBaseUrl
14
+ }) {
15
+ const inlineConfig = useMemo(
16
+ () => compactConfig({
17
+ apiBaseUrl,
18
+ apiKey,
19
+ fetch,
20
+ headers,
21
+ streamBaseUrl
22
+ }),
23
+ [apiBaseUrl, apiKey, fetch, headers, streamBaseUrl]
24
+ );
25
+ const providerConfig = config ?? inlineConfig;
26
+ const contextClient = useMemo(() => client ?? createRestlessClient(providerConfig), [client, providerConfig]);
27
+ return createElement(RestlessClientContext.Provider, { value: contextClient }, children);
28
+ }
29
+ function useRestlessClient() {
30
+ const client = useContext(RestlessClientContext);
31
+ if (!client) {
32
+ throw new Error("useRestlessClient must be used within a RestlessProvider or with an explicit client option.");
33
+ }
34
+ return client;
35
+ }
36
+ function useOptionalRestlessClient() {
37
+ return useContext(RestlessClientContext);
38
+ }
39
+ function compactConfig(config) {
40
+ return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== void 0));
41
+ }
42
+
43
+ // src/stream-subscription.ts
44
+ import { useCallback, useEffect, useMemo as useMemo2, useRef, useState } from "react";
45
+
46
+ // src/stream-subscription-types.ts
47
+ var DEFAULT_MAX_EVENTS = 100;
48
+
49
+ // src/stream-subscription-utils.ts
50
+ function isNonEmptyString(value) {
51
+ return typeof value === "string" && value.length > 0;
52
+ }
53
+ function appendEvent(currentEvents, event, maxEvents) {
54
+ if (maxEvents === 0) {
55
+ return [];
56
+ }
57
+ const nextEvents = [...currentEvents, event];
58
+ return nextEvents.length > maxEvents ? nextEvents.slice(nextEvents.length - maxEvents) : nextEvents;
59
+ }
60
+ function normalizeMaxEvents(maxEvents) {
61
+ if (!Number.isFinite(maxEvents)) {
62
+ return DEFAULT_MAX_EVENTS;
63
+ }
64
+ return Math.max(0, Math.floor(maxEvents));
65
+ }
66
+ function resolveReconnectDelay(delay, attempt) {
67
+ const resolvedDelay = typeof delay === "function" ? delay(attempt) : delay;
68
+ return Number.isFinite(resolvedDelay) ? Math.max(0, resolvedDelay) : 0;
69
+ }
70
+ async function sleep(milliseconds, signal) {
71
+ if (milliseconds === 0 || signal.aborted) {
72
+ return;
73
+ }
74
+ return new Promise((resolve) => {
75
+ const timeout = setTimeout(resolve, milliseconds);
76
+ signal.addEventListener(
77
+ "abort",
78
+ () => {
79
+ clearTimeout(timeout);
80
+ resolve();
81
+ },
82
+ { once: true }
83
+ );
84
+ });
85
+ }
86
+ function stableOptionsKey(options) {
87
+ return JSON.stringify(sortRecord(options));
88
+ }
89
+ function toError(error) {
90
+ if (error instanceof Error) {
91
+ return error;
92
+ }
93
+ return new Error(typeof error === "string" ? error : "Restless stream subscription failed.");
94
+ }
95
+ function sortRecord(value) {
96
+ if (Array.isArray(value)) {
97
+ return value.map((item) => sortRecord(item));
98
+ }
99
+ if (typeof value === "function") {
100
+ return value.toString();
101
+ }
102
+ if (value === null || value === void 0 || typeof value !== "object") {
103
+ return value;
104
+ }
105
+ return Object.fromEntries(
106
+ Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(([key, nestedValue]) => [key, sortRecord(nestedValue)])
107
+ );
108
+ }
109
+
110
+ // src/stream-subscription.ts
111
+ function useStreamSubscription(options) {
112
+ const {
113
+ client: explicitClient,
114
+ enabled = true,
115
+ maxEvents = DEFAULT_MAX_EVENTS,
116
+ maxReconnectAttempts = 0,
117
+ onError,
118
+ onEvent,
119
+ reconnect = false,
120
+ reconnectDelayMs = 1e3,
121
+ ...subscribeOptions
122
+ } = options;
123
+ const contextClient = useOptionalRestlessClient();
124
+ const client = explicitClient ?? contextClient;
125
+ if (!client) {
126
+ throw new Error("useStreamSubscription requires a RestlessProvider or an explicit client option.");
127
+ }
128
+ const subscriptionClient = client;
129
+ const boundedMaxEvents = normalizeMaxEvents(maxEvents);
130
+ const subscribeOptionsKey = stableOptionsKey(subscribeOptions);
131
+ const abortControllerRef = useRef(null);
132
+ const onErrorRef = useRef(onError);
133
+ const onEventRef = useRef(onEvent);
134
+ const reconnectDelayRef = useRef(reconnectDelayMs);
135
+ const subscribeOptionsRef = useRef(subscribeOptions);
136
+ const [connectionToken, setConnectionToken] = useState(0);
137
+ const [events, setEvents] = useState([]);
138
+ const [error, setError] = useState(null);
139
+ const [shouldConnect, setShouldConnect] = useState(enabled);
140
+ const [status, setStatus] = useState(enabled ? "connecting" : "idle");
141
+ onErrorRef.current = onError;
142
+ onEventRef.current = onEvent;
143
+ reconnectDelayRef.current = reconnectDelayMs;
144
+ subscribeOptionsRef.current = subscribeOptions;
145
+ useEffect(() => {
146
+ setShouldConnect(enabled);
147
+ setStatus((currentStatus) => {
148
+ if (enabled) {
149
+ return currentStatus === "idle" || currentStatus === "closed" ? "connecting" : currentStatus;
150
+ }
151
+ return "idle";
152
+ });
153
+ }, [enabled]);
154
+ const connect = useCallback(() => {
155
+ setError(null);
156
+ setShouldConnect(true);
157
+ setStatus((currentStatus) => currentStatus === "open" ? currentStatus : "connecting");
158
+ setConnectionToken((currentToken) => currentToken + 1);
159
+ }, []);
160
+ const disconnect = useCallback(() => {
161
+ setShouldConnect(false);
162
+ abortControllerRef.current?.abort();
163
+ abortControllerRef.current = null;
164
+ setStatus("closed");
165
+ }, []);
166
+ const reset = useCallback(() => {
167
+ setEvents([]);
168
+ setError(null);
169
+ }, []);
170
+ useEffect(() => {
171
+ if (!shouldConnect) {
172
+ return;
173
+ }
174
+ let cancelled = false;
175
+ const abortController = new AbortController();
176
+ abortControllerRef.current?.abort();
177
+ abortControllerRef.current = abortController;
178
+ async function subscribe() {
179
+ let reconnectAttempt = 0;
180
+ while (!cancelled) {
181
+ setStatus(reconnectAttempt > 0 ? "reconnecting" : "connecting");
182
+ try {
183
+ const stream = subscriptionClient.streams.subscribeSse({
184
+ ...subscribeOptionsRef.current,
185
+ signal: abortController.signal
186
+ });
187
+ setError(null);
188
+ setStatus("open");
189
+ for await (const event of stream) {
190
+ if (cancelled) {
191
+ break;
192
+ }
193
+ onEventRef.current?.(event);
194
+ setEvents((currentEvents) => appendEvent(currentEvents, event, boundedMaxEvents));
195
+ }
196
+ if (!cancelled) {
197
+ setStatus("closed");
198
+ }
199
+ break;
200
+ } catch (caughtError) {
201
+ if (cancelled || abortController.signal.aborted) {
202
+ break;
203
+ }
204
+ const normalizedError = toError(caughtError);
205
+ setError(normalizedError);
206
+ onErrorRef.current?.(normalizedError);
207
+ if (!reconnect || reconnectAttempt >= maxReconnectAttempts) {
208
+ setStatus("error");
209
+ break;
210
+ }
211
+ reconnectAttempt += 1;
212
+ setStatus("reconnecting");
213
+ await sleep(resolveReconnectDelay(reconnectDelayRef.current, reconnectAttempt), abortController.signal);
214
+ }
215
+ }
216
+ }
217
+ void subscribe();
218
+ return () => {
219
+ cancelled = true;
220
+ abortController.abort();
221
+ if (abortControllerRef.current === abortController) {
222
+ abortControllerRef.current = null;
223
+ }
224
+ };
225
+ }, [
226
+ boundedMaxEvents,
227
+ connectionToken,
228
+ maxReconnectAttempts,
229
+ reconnect,
230
+ shouldConnect,
231
+ subscribeOptionsKey,
232
+ subscriptionClient
233
+ ]);
234
+ const latestEvent = events.at(-1) ?? null;
235
+ return useMemo2(
236
+ () => ({
237
+ connect,
238
+ disconnect,
239
+ error,
240
+ events,
241
+ isConnected: status === "open",
242
+ isConnecting: status === "connecting" || status === "reconnecting",
243
+ latestEvent,
244
+ reset,
245
+ status
246
+ }),
247
+ [connect, disconnect, error, events, latestEvent, reset, status]
248
+ );
249
+ }
250
+ function useManagedStream(options) {
251
+ const { sseUrl, stream, enabled, ...subscriptionOptions } = options;
252
+ const managedSseUrl = sseUrl ?? stream?.sseUrl ?? "";
253
+ const shouldEnable = typeof enabled === "boolean" ? enabled : isNonEmptyString(managedSseUrl);
254
+ return useStreamSubscription({
255
+ ...subscriptionOptions,
256
+ enabled: shouldEnable,
257
+ sseUrl: managedSseUrl
258
+ });
259
+ }
260
+ function useDirectStream(options) {
261
+ const { directUrl, enabled, session, sseUrl, ...subscriptionOptions } = options;
262
+ const directSseUrl = directUrl ?? sseUrl ?? session?.sseUrl ?? "";
263
+ const shouldEnable = typeof enabled === "boolean" ? enabled : isNonEmptyString(directSseUrl);
264
+ return useStreamSubscription({
265
+ ...subscriptionOptions,
266
+ enabled: shouldEnable,
267
+ sseUrl: directSseUrl
268
+ });
269
+ }
270
+ export {
271
+ DEFAULT_MAX_EVENTS,
272
+ RestlessProvider,
273
+ useDirectStream,
274
+ useManagedStream,
275
+ useRestlessClient,
276
+ useStreamSubscription
277
+ };
278
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provider.ts","../src/stream-subscription.ts","../src/stream-subscription-types.ts","../src/stream-subscription-utils.ts"],"sourcesContent":["import { createRestlessClient, type RestlessClient, type RestlessClientConfig } from '@restless-stream/core';\nimport { createContext, createElement, type ReactNode, useContext, useMemo } from 'react';\n\nconst RestlessClientContext = createContext<RestlessClient | null>(null);\ntype InlineRestlessClientConfig = {\n [K in keyof RestlessClientConfig]-?: RestlessClientConfig[K] | undefined;\n};\n\nexport type RestlessProviderProps = Partial<RestlessClientConfig> & {\n children?: ReactNode;\n client?: RestlessClient;\n config?: RestlessClientConfig;\n};\n\nexport function RestlessProvider({\n apiBaseUrl,\n apiKey,\n children,\n client,\n config,\n fetch,\n headers,\n streamBaseUrl,\n}: RestlessProviderProps) {\n const inlineConfig = useMemo(\n () =>\n compactConfig({\n apiBaseUrl,\n apiKey,\n fetch,\n headers,\n streamBaseUrl,\n }),\n [apiBaseUrl, apiKey, fetch, headers, streamBaseUrl],\n );\n const providerConfig = config ?? inlineConfig;\n const contextClient = useMemo(() => client ?? createRestlessClient(providerConfig), [client, providerConfig]);\n\n return createElement(RestlessClientContext.Provider, { value: contextClient }, children);\n}\n\nexport function useRestlessClient(): RestlessClient {\n const client = useContext(RestlessClientContext);\n\n if (!client) {\n throw new Error('useRestlessClient must be used within a RestlessProvider or with an explicit client option.');\n }\n\n return client;\n}\n\nexport function useOptionalRestlessClient(): RestlessClient | null {\n return useContext(RestlessClientContext);\n}\n\nfunction compactConfig(config: InlineRestlessClientConfig): RestlessClientConfig {\n return Object.fromEntries(Object.entries(config).filter(([, value]) => value !== undefined));\n}\n","import type { RestlessStreamEvent } from '@restless-stream/core';\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { useOptionalRestlessClient } from './provider.js';\nimport {\n DEFAULT_MAX_EVENTS,\n type StreamSubscriptionStatus,\n type UseDirectStreamOptions,\n type UseManagedStreamOptions,\n type UseStreamSubscriptionOptions,\n type UseStreamSubscriptionResult,\n} from './stream-subscription-types.js';\nimport {\n appendEvent,\n isNonEmptyString,\n normalizeMaxEvents,\n resolveReconnectDelay,\n sleep,\n stableOptionsKey,\n toError,\n} from './stream-subscription-utils.js';\n\nexport { DEFAULT_MAX_EVENTS } from './stream-subscription-types.js';\nexport type {\n StreamSubscriptionControls,\n StreamSubscriptionStatus,\n UseDirectStreamOptions,\n UseManagedStreamOptions,\n UseStreamSubscriptionOptions,\n UseStreamSubscriptionResult,\n} from './stream-subscription-types.js';\n\nexport function useStreamSubscription(options: UseStreamSubscriptionOptions): UseStreamSubscriptionResult {\n const {\n client: explicitClient,\n enabled = true,\n maxEvents = DEFAULT_MAX_EVENTS,\n maxReconnectAttempts = 0,\n onError,\n onEvent,\n reconnect = false,\n reconnectDelayMs = 1000,\n ...subscribeOptions\n } = options;\n const contextClient = useOptionalRestlessClient();\n const client = explicitClient ?? contextClient;\n\n if (!client) {\n throw new Error('useStreamSubscription requires a RestlessProvider or an explicit client option.');\n }\n\n const subscriptionClient = client;\n\n const boundedMaxEvents = normalizeMaxEvents(maxEvents);\n const subscribeOptionsKey = stableOptionsKey(subscribeOptions);\n const abortControllerRef = useRef<AbortController | null>(null);\n const onErrorRef = useRef(onError);\n const onEventRef = useRef(onEvent);\n const reconnectDelayRef = useRef(reconnectDelayMs);\n const subscribeOptionsRef = useRef(subscribeOptions);\n const [connectionToken, setConnectionToken] = useState(0);\n const [events, setEvents] = useState<RestlessStreamEvent[]>([]);\n const [error, setError] = useState<Error | null>(null);\n const [shouldConnect, setShouldConnect] = useState(enabled);\n const [status, setStatus] = useState<StreamSubscriptionStatus>(enabled ? 'connecting' : 'idle');\n\n onErrorRef.current = onError;\n onEventRef.current = onEvent;\n reconnectDelayRef.current = reconnectDelayMs;\n subscribeOptionsRef.current = subscribeOptions;\n\n useEffect(() => {\n setShouldConnect(enabled);\n setStatus((currentStatus) => {\n if (enabled) {\n return currentStatus === 'idle' || currentStatus === 'closed' ? 'connecting' : currentStatus;\n }\n\n return 'idle';\n });\n }, [enabled]);\n\n const connect = useCallback(() => {\n setError(null);\n setShouldConnect(true);\n setStatus((currentStatus) => (currentStatus === 'open' ? currentStatus : 'connecting'));\n setConnectionToken((currentToken) => currentToken + 1);\n }, []);\n\n const disconnect = useCallback(() => {\n setShouldConnect(false);\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n setStatus('closed');\n }, []);\n\n const reset = useCallback(() => {\n setEvents([]);\n setError(null);\n }, []);\n\n useEffect(() => {\n if (!shouldConnect) {\n return;\n }\n\n let cancelled = false;\n const abortController = new AbortController();\n abortControllerRef.current?.abort();\n abortControllerRef.current = abortController;\n\n async function subscribe() {\n let reconnectAttempt = 0;\n\n while (!cancelled) {\n setStatus(reconnectAttempt > 0 ? 'reconnecting' : 'connecting');\n\n try {\n const stream = subscriptionClient.streams.subscribeSse({\n ...subscribeOptionsRef.current,\n signal: abortController.signal,\n });\n\n setError(null);\n setStatus('open');\n\n for await (const event of stream) {\n if (cancelled) {\n break;\n }\n\n onEventRef.current?.(event);\n setEvents((currentEvents) => appendEvent(currentEvents, event, boundedMaxEvents));\n }\n\n if (!cancelled) {\n setStatus('closed');\n }\n\n break;\n } catch (caughtError) {\n if (cancelled || abortController.signal.aborted) {\n break;\n }\n\n const normalizedError = toError(caughtError);\n setError(normalizedError);\n onErrorRef.current?.(normalizedError);\n\n if (!reconnect || reconnectAttempt >= maxReconnectAttempts) {\n setStatus('error');\n break;\n }\n\n reconnectAttempt += 1;\n setStatus('reconnecting');\n await sleep(resolveReconnectDelay(reconnectDelayRef.current, reconnectAttempt), abortController.signal);\n }\n }\n }\n\n void subscribe();\n\n return () => {\n cancelled = true;\n abortController.abort();\n\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = null;\n }\n };\n }, [\n boundedMaxEvents,\n connectionToken,\n maxReconnectAttempts,\n reconnect,\n shouldConnect,\n subscribeOptionsKey,\n subscriptionClient,\n ]);\n\n const latestEvent = events.at(-1) ?? null;\n\n return useMemo(\n () => ({\n connect,\n disconnect,\n error,\n events,\n isConnected: status === 'open',\n isConnecting: status === 'connecting' || status === 'reconnecting',\n latestEvent,\n reset,\n status,\n }),\n [connect, disconnect, error, events, latestEvent, reset, status],\n );\n}\n\nexport function useManagedStream(options: UseManagedStreamOptions): UseStreamSubscriptionResult {\n const { sseUrl, stream, enabled, ...subscriptionOptions } = options;\n const managedSseUrl = sseUrl ?? stream?.sseUrl ?? '';\n const shouldEnable = typeof enabled === 'boolean' ? enabled : isNonEmptyString(managedSseUrl);\n\n return useStreamSubscription({\n ...subscriptionOptions,\n enabled: shouldEnable,\n sseUrl: managedSseUrl,\n });\n}\n\nexport function useDirectStream(options: UseDirectStreamOptions): UseStreamSubscriptionResult {\n const { directUrl, enabled, session, sseUrl, ...subscriptionOptions } = options;\n const directSseUrl = directUrl ?? sseUrl ?? session?.sseUrl ?? '';\n const shouldEnable = typeof enabled === 'boolean' ? enabled : isNonEmptyString(directSseUrl);\n\n return useStreamSubscription({\n ...subscriptionOptions,\n enabled: shouldEnable,\n sseUrl: directSseUrl,\n });\n}\n","import type { RestlessClient, RestlessStreamEvent, SubscribeSseOptions } from '@restless-stream/core';\n\nexport const DEFAULT_MAX_EVENTS = 100;\n\nexport type StreamSubscriptionStatus = 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed' | 'error';\n\nexport interface StreamSubscriptionControls {\n connect: () => void;\n disconnect: () => void;\n reset: () => void;\n}\n\nexport type UseStreamSubscriptionOptions = Omit<SubscribeSseOptions, 'reconnect' | 'reconnectDelayMs'> & {\n client?: RestlessClient;\n enabled?: boolean;\n maxEvents?: number;\n maxReconnectAttempts?: number;\n onError?: (error: Error) => void;\n onEvent?: (event: RestlessStreamEvent) => void;\n reconnect?: boolean;\n reconnectDelayMs?: number | ((attempt: number) => number);\n};\n\nexport type UseStreamSubscriptionResult = StreamSubscriptionControls & {\n error: Error | null;\n events: RestlessStreamEvent[];\n isConnected: boolean;\n isConnecting: boolean;\n latestEvent: RestlessStreamEvent | null;\n status: StreamSubscriptionStatus;\n};\n\nexport type UseManagedStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {\n sseUrl?: SubscribeSseOptions['sseUrl'] | null;\n stream?: { sseUrl?: string | null } | null;\n};\n\nexport type UseDirectStreamOptions = Omit<UseStreamSubscriptionOptions, 'sseUrl'> & {\n directUrl?: string | null;\n session?: { sseUrl?: string | null } | null;\n sseUrl?: SubscribeSseOptions['sseUrl'] | null;\n};\n","import type { RestlessStreamEvent, SubscribeSseOptions } from '@restless-stream/core';\n\nimport { DEFAULT_MAX_EVENTS } from './stream-subscription-types.js';\n\nexport function isNonEmptyString(value: unknown): boolean {\n return typeof value === 'string' && value.length > 0;\n}\n\nexport function appendEvent(\n currentEvents: RestlessStreamEvent[],\n event: RestlessStreamEvent,\n maxEvents: number,\n): RestlessStreamEvent[] {\n if (maxEvents === 0) {\n return [];\n }\n\n const nextEvents = [...currentEvents, event];\n return nextEvents.length > maxEvents ? nextEvents.slice(nextEvents.length - maxEvents) : nextEvents;\n}\n\nexport function normalizeMaxEvents(maxEvents: number): number {\n if (!Number.isFinite(maxEvents)) {\n return DEFAULT_MAX_EVENTS;\n }\n\n return Math.max(0, Math.floor(maxEvents));\n}\n\nexport function resolveReconnectDelay(delay: number | ((attempt: number) => number), attempt: number): number {\n const resolvedDelay = typeof delay === 'function' ? delay(attempt) : delay;\n return Number.isFinite(resolvedDelay) ? Math.max(0, resolvedDelay) : 0;\n}\n\nexport async function sleep(milliseconds: number, signal: AbortSignal): Promise<void> {\n if (milliseconds === 0 || signal.aborted) {\n return;\n }\n\n return new Promise((resolve) => {\n const timeout = setTimeout(resolve, milliseconds);\n\n signal.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeout);\n resolve();\n },\n { once: true },\n );\n });\n}\n\nexport function stableOptionsKey(options: Partial<SubscribeSseOptions>): string {\n return JSON.stringify(sortRecord(options));\n}\n\nexport function toError(error: unknown): Error {\n if (error instanceof Error) {\n return error;\n }\n\n return new Error(typeof error === 'string' ? error : 'Restless stream subscription failed.');\n}\n\nfunction sortRecord(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => sortRecord(item));\n }\n\n if (typeof value === 'function') {\n return value.toString();\n }\n\n if (value === null || value === undefined || typeof value !== 'object') {\n return value;\n }\n\n return Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nestedValue]) => [key, sortRecord(nestedValue)]),\n );\n}\n"],"mappings":"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","names":["useMemo","useMemo"]}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@restless-stream/react",
3
+ "version": "0.1.0",
4
+ "description": "Lightweight React bindings for the Restless Stream SDK.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "peerDependencies": {
21
+ "react": ">=18.2 <20"
22
+ },
23
+ "dependencies": {
24
+ "@restless-stream/core": "0.1.0"
25
+ },
26
+ "devDependencies": {
27
+ "@testing-library/react": "^16.3.0",
28
+ "@vitejs/plugin-react": "^5.1.2",
29
+ "@types/react": "^19.2.7",
30
+ "@types/react-dom": "^19.2.3",
31
+ "jsdom": "^27.2.0",
32
+ "react": "^19.2.1",
33
+ "react-dom": "^19.2.1"
34
+ },
35
+ "scripts": {
36
+ "build": "tsup",
37
+ "test": "vitest run",
38
+ "test:live": "vitest run src/live.test.tsx --config vitest.live.config.ts",
39
+ "typecheck": "tsc --noEmit"
40
+ }
41
+ }