supabase-edge-function-continuous-stream 1.0.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,507 @@
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
+ CONNECTION_TIMEOUT_MS: () => CONNECTION_TIMEOUT_MS,
24
+ DEFAULT_EDGE_WORKER_LIMITS: () => DEFAULT_EDGE_WORKER_LIMITS,
25
+ INITIAL_RETRY_DELAY_MS: () => INITIAL_RETRY_DELAY_MS,
26
+ MAX_RETRIES: () => MAX_RETRIES,
27
+ PACKAGE_NAME: () => PACKAGE_NAME,
28
+ connectEdgeSocket: () => connectEdgeSocket,
29
+ createStandardAiMessageHandler: () => createStandardAiMessageHandler,
30
+ createUseEdgeStream: () => createUseEdgeStream
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/constants.ts
35
+ var MAX_RETRIES = 200;
36
+ var INITIAL_RETRY_DELAY_MS = 1e3;
37
+ var CONNECTION_TIMEOUT_MS = 1e4;
38
+
39
+ // src/connection.ts
40
+ async function connectEdgeSocket(deps) {
41
+ const {
42
+ functionPath,
43
+ getAccessToken,
44
+ getSupabaseUrl,
45
+ wsRef,
46
+ isExplicitDisconnectRef,
47
+ isConnectingRef,
48
+ retryCountRef,
49
+ socketOpenedAtRef,
50
+ isWarmupReadyRef,
51
+ warmupWaitersRef,
52
+ passiveOnServerActionRef,
53
+ activeRequestRef,
54
+ lastWarmupPayloadRef,
55
+ setIsConnected,
56
+ closeSocket,
57
+ sendWarmupPayload,
58
+ settleWarmupWaiters
59
+ } = deps;
60
+ if (wsRef.current?.readyState === WebSocket.OPEN) return Promise.resolve();
61
+ if (isConnectingRef.current) {
62
+ return new Promise((resolve, reject) => {
63
+ const checkInterval = setInterval(() => {
64
+ if (wsRef.current?.readyState === WebSocket.OPEN) {
65
+ clearInterval(checkInterval);
66
+ resolve();
67
+ } else if (wsRef.current?.readyState === WebSocket.CLOSED) {
68
+ clearInterval(checkInterval);
69
+ reject(new Error("Connection closed during check"));
70
+ }
71
+ }, 100);
72
+ });
73
+ }
74
+ isConnectingRef.current = true;
75
+ isExplicitDisconnectRef.current = false;
76
+ try {
77
+ const accessToken = await getAccessToken();
78
+ const supabaseUrl = getSupabaseUrl();
79
+ if (!supabaseUrl) throw new Error("Missing Supabase URL");
80
+ const wsUrl = supabaseUrl.replace(
81
+ /^https?:\/\//,
82
+ (m) => m === "https://" ? "wss://" : "ws://"
83
+ );
84
+ const functionUrl = `${wsUrl}/functions/v1/${functionPath}`;
85
+ return await new Promise((resolveConnection, rejectConnection) => {
86
+ let connectionTimeout = null;
87
+ if (wsRef.current) {
88
+ try {
89
+ wsRef.current.onopen = null;
90
+ wsRef.current.onmessage = null;
91
+ wsRef.current.onerror = null;
92
+ wsRef.current.onclose = null;
93
+ if (wsRef.current.readyState === WebSocket.OPEN || wsRef.current.readyState === WebSocket.CONNECTING) {
94
+ wsRef.current.close();
95
+ }
96
+ } catch {
97
+ }
98
+ }
99
+ const urlWithToken = new URL(functionUrl);
100
+ urlWithToken.searchParams.set("jwt", accessToken);
101
+ wsRef.current = new WebSocket(urlWithToken.toString());
102
+ if (retryCountRef.current === 0) {
103
+ connectionTimeout = setTimeout(() => {
104
+ if (wsRef.current && wsRef.current.readyState !== WebSocket.OPEN) {
105
+ isExplicitDisconnectRef.current = false;
106
+ closeSocket();
107
+ }
108
+ }, CONNECTION_TIMEOUT_MS);
109
+ }
110
+ wsRef.current.onopen = () => {
111
+ if (connectionTimeout) clearTimeout(connectionTimeout);
112
+ retryCountRef.current = 0;
113
+ isConnectingRef.current = false;
114
+ setIsConnected(true);
115
+ socketOpenedAtRef.current = Date.now();
116
+ if (lastWarmupPayloadRef.current) {
117
+ sendWarmupPayload();
118
+ }
119
+ resolveConnection();
120
+ };
121
+ wsRef.current.onerror = () => {
122
+ };
123
+ const retryConnect = () => {
124
+ connectEdgeSocket(deps).then(resolveConnection).catch(rejectConnection);
125
+ };
126
+ wsRef.current.onclose = () => {
127
+ if (connectionTimeout) clearTimeout(connectionTimeout);
128
+ isConnectingRef.current = false;
129
+ setIsConnected(false);
130
+ isWarmupReadyRef.current = false;
131
+ const willRetry = !isExplicitDisconnectRef.current && retryCountRef.current < MAX_RETRIES;
132
+ if (!willRetry) {
133
+ settleWarmupWaiters(new Error("WebSocket closed"));
134
+ }
135
+ if (isExplicitDisconnectRef.current) {
136
+ rejectConnection(
137
+ new Error("WebSocket closed by server or aborted")
138
+ );
139
+ } else if (retryCountRef.current < MAX_RETRIES) {
140
+ retryCountRef.current++;
141
+ const delay = INITIAL_RETRY_DELAY_MS * Math.pow(2, retryCountRef.current - 1);
142
+ setTimeout(retryConnect, delay);
143
+ } else {
144
+ rejectConnection(
145
+ new Error(
146
+ `WebSocket connection failed after ${retryCountRef.current} retries`
147
+ )
148
+ );
149
+ }
150
+ };
151
+ wsRef.current.onmessage = (event) => {
152
+ try {
153
+ const message = JSON.parse(event.data);
154
+ if (message.type === "status" && message.data === "ready") {
155
+ isWarmupReadyRef.current = true;
156
+ settleWarmupWaiters();
157
+ }
158
+ if (message.type === "error" && warmupWaitersRef.current.length > 0 && !isWarmupReadyRef.current) {
159
+ isWarmupReadyRef.current = false;
160
+ settleWarmupWaiters(
161
+ new Error(String(message.data ?? "Warmup failed"))
162
+ );
163
+ }
164
+ const passive = passiveOnServerActionRef.current;
165
+ if (passive && !activeRequestRef.current) {
166
+ const isWarmupControl = message.type === "status" && (message.data === "ready" || message.data === "context");
167
+ if (!isWarmupControl) {
168
+ passive(message.type, message.data);
169
+ }
170
+ }
171
+ if (!activeRequestRef.current) return;
172
+ const { ctx, handler } = activeRequestRef.current;
173
+ handler(message, ctx);
174
+ } catch (error) {
175
+ if (!activeRequestRef.current) return;
176
+ const ctx = activeRequestRef.current.ctx;
177
+ ctx.reject(
178
+ new Error(
179
+ `Failed to parse WebSocket message: ${error instanceof Error ? error.message : String(error)}`
180
+ )
181
+ );
182
+ }
183
+ };
184
+ });
185
+ } catch (err) {
186
+ isConnectingRef.current = false;
187
+ throw err;
188
+ }
189
+ }
190
+
191
+ // src/handler.ts
192
+ function createStandardAiMessageHandler(defaults = {}, callbacks) {
193
+ const state = {};
194
+ const buildResponse = (overrides) => ({
195
+ ...defaults,
196
+ ...state,
197
+ ...overrides
198
+ });
199
+ return (message, ctx) => {
200
+ if (message.type === "response_text" && typeof message.data === "string") {
201
+ state.reply = message.data;
202
+ }
203
+ if (typeof message.data === "object" && message.data !== null && message.type !== "error") {
204
+ Object.assign(state, message.data);
205
+ }
206
+ if (message.type === "error") {
207
+ const raw = message.data ?? "Server error";
208
+ const userMsg = callbacks?.toUserMessage?.(raw);
209
+ if (userMsg) callbacks?.onServerAction?.("error", userMsg);
210
+ ctx.reject(new Error(raw));
211
+ return;
212
+ }
213
+ if (message.type === "complete") {
214
+ ctx.clearOverallTimeout();
215
+ callbacks?.onServerAction?.("complete", message.data);
216
+ if (!ctx.isResolved()) {
217
+ ctx.resolve(buildResponse(message.data));
218
+ }
219
+ return;
220
+ }
221
+ callbacks?.onServerAction?.(message.type, message.data);
222
+ };
223
+ }
224
+
225
+ // src/workerLimits.ts
226
+ var DEFAULT_EDGE_WORKER_LIMITS = {
227
+ edgeWorkerTtlMs: 15e4,
228
+ edgeRotateThresholdMs: 3e4,
229
+ overallTimeoutMs: 5 * 6e4
230
+ };
231
+
232
+ // src/createUseEdgeStream.ts
233
+ var import_react = require("react");
234
+ function createUseEdgeStream(deps) {
235
+ const {
236
+ getAccessToken,
237
+ getSupabaseUrl,
238
+ workerLimits,
239
+ toUserMessage,
240
+ invalidateTags
241
+ } = deps;
242
+ const { edgeWorkerTtlMs, edgeRotateThresholdMs, overallTimeoutMs } = workerLimits;
243
+ return function useEdgeStream(config) {
244
+ const [isLoading, setIsLoading] = (0, import_react.useState)(false);
245
+ const [error, setError] = (0, import_react.useState)(null);
246
+ const [data, setData] = (0, import_react.useState)(void 0);
247
+ const wsRef = (0, import_react.useRef)(null);
248
+ const isExplicitDisconnectRef = (0, import_react.useRef)(false);
249
+ const [isConnected, setIsConnected] = (0, import_react.useState)(false);
250
+ const isConnectingRef = (0, import_react.useRef)(false);
251
+ const lastWarmupPayloadRef = (0, import_react.useRef)(null);
252
+ const isWarmupReadyRef = (0, import_react.useRef)(false);
253
+ const warmupWaitersRef = (0, import_react.useRef)([]);
254
+ const retryCountRef = (0, import_react.useRef)(0);
255
+ const passiveOnServerActionRef = (0, import_react.useRef)(null);
256
+ const socketOpenedAtRef = (0, import_react.useRef)(null);
257
+ const settleWarmupWaiters = (0, import_react.useCallback)((warmupError) => {
258
+ const waiters = warmupWaitersRef.current;
259
+ warmupWaitersRef.current = [];
260
+ if (warmupError) {
261
+ waiters.forEach((w) => w.reject(warmupError));
262
+ return;
263
+ }
264
+ waiters.forEach((w) => w.resolve());
265
+ }, []);
266
+ const waitForWarmupReady = (0, import_react.useCallback)(() => {
267
+ if (isWarmupReadyRef.current) return Promise.resolve();
268
+ return new Promise((resolve, reject) => {
269
+ warmupWaitersRef.current.push({ resolve, reject });
270
+ });
271
+ }, []);
272
+ const sendWarmupPayload = (0, import_react.useCallback)(() => {
273
+ if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
274
+ return;
275
+ }
276
+ if (!lastWarmupPayloadRef.current) return;
277
+ isWarmupReadyRef.current = false;
278
+ wsRef.current.send(
279
+ JSON.stringify({
280
+ type: "client_warmup",
281
+ data: lastWarmupPayloadRef.current
282
+ })
283
+ );
284
+ }, []);
285
+ const activeRequestRef = (0, import_react.useRef)(null);
286
+ (0, import_react.useEffect)(() => {
287
+ return () => {
288
+ isExplicitDisconnectRef.current = true;
289
+ if (wsRef.current) {
290
+ try {
291
+ wsRef.current.close();
292
+ } catch {
293
+ }
294
+ }
295
+ };
296
+ }, []);
297
+ const closeSocket = (0, import_react.useCallback)(() => {
298
+ if (!wsRef.current) return;
299
+ try {
300
+ wsRef.current.close();
301
+ } catch {
302
+ }
303
+ socketOpenedAtRef.current = null;
304
+ }, []);
305
+ const abort = (0, import_react.useCallback)(() => {
306
+ isExplicitDisconnectRef.current = true;
307
+ closeSocket();
308
+ setIsLoading(false);
309
+ if (activeRequestRef.current) {
310
+ activeRequestRef.current.reject(new Error("Request aborted"));
311
+ if (activeRequestRef.current.overallTimeout) {
312
+ clearTimeout(activeRequestRef.current.overallTimeout);
313
+ }
314
+ activeRequestRef.current = null;
315
+ }
316
+ }, [closeSocket]);
317
+ const connectWebSocket = (0, import_react.useCallback)(
318
+ () => connectEdgeSocket({
319
+ functionPath: config.functionPath,
320
+ getAccessToken,
321
+ getSupabaseUrl,
322
+ wsRef,
323
+ isExplicitDisconnectRef,
324
+ isConnectingRef,
325
+ retryCountRef,
326
+ socketOpenedAtRef,
327
+ isWarmupReadyRef,
328
+ warmupWaitersRef,
329
+ passiveOnServerActionRef,
330
+ activeRequestRef,
331
+ lastWarmupPayloadRef,
332
+ setIsConnected,
333
+ closeSocket,
334
+ sendWarmupPayload,
335
+ settleWarmupWaiters
336
+ }),
337
+ [
338
+ config.functionPath,
339
+ closeSocket,
340
+ sendWarmupPayload,
341
+ settleWarmupWaiters
342
+ ]
343
+ );
344
+ const rotateConnectionIfNeeded = (0, import_react.useCallback)(
345
+ async (getExpiresAt) => {
346
+ const fallbackExpires = socketOpenedAtRef.current ? socketOpenedAtRef.current + edgeWorkerTtlMs : null;
347
+ const expiresAt = getExpiresAt?.() ?? fallbackExpires;
348
+ if (!expiresAt) return;
349
+ if (expiresAt - Date.now() >= edgeRotateThresholdMs) return;
350
+ isExplicitDisconnectRef.current = false;
351
+ closeSocket();
352
+ isWarmupReadyRef.current = false;
353
+ await connectWebSocket();
354
+ },
355
+ [closeSocket, connectWebSocket, edgeRotateThresholdMs, edgeWorkerTtlMs]
356
+ );
357
+ const warmup = (0, import_react.useCallback)(
358
+ async (payload, options) => {
359
+ if (options?.onServerAction) {
360
+ passiveOnServerActionRef.current = options.onServerAction;
361
+ }
362
+ lastWarmupPayloadRef.current = payload;
363
+ isWarmupReadyRef.current = false;
364
+ const wasOpen = wsRef.current?.readyState === WebSocket.OPEN;
365
+ await connectWebSocket();
366
+ if (!isWarmupReadyRef.current) {
367
+ if (wasOpen) sendWarmupPayload();
368
+ await waitForWarmupReady();
369
+ }
370
+ },
371
+ [connectWebSocket, sendWarmupPayload, waitForWarmupReady]
372
+ );
373
+ const send = (0, import_react.useCallback)(
374
+ async (payload, options) => {
375
+ if (!isLoading) {
376
+ await rotateConnectionIfNeeded(options.getWorkerExpiresAt);
377
+ }
378
+ setIsLoading(true);
379
+ setError(null);
380
+ setData(void 0);
381
+ isExplicitDisconnectRef.current = false;
382
+ let resolved = false;
383
+ const ctx = {
384
+ resolve: (value) => {
385
+ resolved = true;
386
+ setData(value);
387
+ setIsLoading(false);
388
+ if (options.invalidateTags && invalidateTags) {
389
+ const shouldInvalidate = options.invalidateTags.condition ? options.invalidateTags.condition(value) : true;
390
+ if (shouldInvalidate) {
391
+ invalidateTags(options.invalidateTags.tags);
392
+ }
393
+ }
394
+ if (activeRequestRef.current?.overallTimeout) {
395
+ clearTimeout(activeRequestRef.current.overallTimeout);
396
+ }
397
+ if (activeRequestRef.current?.resolve) {
398
+ activeRequestRef.current.resolve(value);
399
+ }
400
+ activeRequestRef.current = null;
401
+ },
402
+ reject: (err) => {
403
+ resolved = true;
404
+ setError(err);
405
+ setIsLoading(false);
406
+ if (activeRequestRef.current?.overallTimeout) {
407
+ clearTimeout(activeRequestRef.current.overallTimeout);
408
+ }
409
+ if (activeRequestRef.current?.reject) {
410
+ activeRequestRef.current.reject(err);
411
+ }
412
+ activeRequestRef.current = null;
413
+ },
414
+ closeSocket,
415
+ clearOverallTimeout: () => {
416
+ if (activeRequestRef.current?.overallTimeout) {
417
+ clearTimeout(activeRequestRef.current.overallTimeout);
418
+ }
419
+ },
420
+ isResolved: () => resolved
421
+ };
422
+ return new Promise((resolvePromise, rejectPromise) => {
423
+ const overallTimeout = setTimeout(() => {
424
+ if (!resolved) {
425
+ const err = new Error("WebSocket request timeout");
426
+ setError(err);
427
+ setIsLoading(false);
428
+ rejectPromise(err);
429
+ activeRequestRef.current = null;
430
+ }
431
+ }, overallTimeoutMs);
432
+ const handler = options.onMessage || createStandardAiMessageHandler(options.defaults || {}, {
433
+ onServerAction: options.onServerAction,
434
+ toUserMessage
435
+ });
436
+ activeRequestRef.current = {
437
+ payload,
438
+ options,
439
+ ctx,
440
+ resolve: resolvePromise,
441
+ reject: rejectPromise,
442
+ overallTimeout,
443
+ handler
444
+ };
445
+ connectWebSocket().then(async () => {
446
+ if (!activeRequestRef.current || activeRequestRef.current.sent) {
447
+ return;
448
+ }
449
+ if (!lastWarmupPayloadRef.current) {
450
+ throw new Error("Warmup required");
451
+ }
452
+ if (!isWarmupReadyRef.current) {
453
+ sendWarmupPayload();
454
+ await waitForWarmupReady();
455
+ }
456
+ if (wsRef.current?.readyState === WebSocket.OPEN && activeRequestRef.current && !activeRequestRef.current.sent) {
457
+ activeRequestRef.current.sent = true;
458
+ wsRef.current.send(
459
+ JSON.stringify({ type: "client_message", data: payload })
460
+ );
461
+ }
462
+ }).catch((err) => {
463
+ clearTimeout(overallTimeout);
464
+ setError(err);
465
+ setIsLoading(false);
466
+ rejectPromise(err);
467
+ activeRequestRef.current = null;
468
+ });
469
+ });
470
+ },
471
+ [
472
+ connectWebSocket,
473
+ closeSocket,
474
+ sendWarmupPayload,
475
+ waitForWarmupReady,
476
+ rotateConnectionIfNeeded,
477
+ isLoading,
478
+ overallTimeoutMs,
479
+ toUserMessage,
480
+ invalidateTags
481
+ ]
482
+ );
483
+ return {
484
+ warmup,
485
+ send,
486
+ abort,
487
+ isLoading,
488
+ error,
489
+ data,
490
+ isConnected
491
+ };
492
+ };
493
+ }
494
+
495
+ // src/index.ts
496
+ var PACKAGE_NAME = "supabase-edge-function-continuous-stream";
497
+ // Annotate the CommonJS export names for ESM import in node:
498
+ 0 && (module.exports = {
499
+ CONNECTION_TIMEOUT_MS,
500
+ DEFAULT_EDGE_WORKER_LIMITS,
501
+ INITIAL_RETRY_DELAY_MS,
502
+ MAX_RETRIES,
503
+ PACKAGE_NAME,
504
+ connectEdgeSocket,
505
+ createStandardAiMessageHandler,
506
+ createUseEdgeStream
507
+ });
@@ -0,0 +1,43 @@
1
+ import { R as Ref, E as EdgeFunctionRawMessage, a as EdgeFunctionMessageContext, S as StandardAiCallbacks } from './react-BgnmZ7M3.cjs';
2
+ export { D as DEFAULT_EDGE_WORKER_LIMITS, b as EdgeStreamConfig, c as EdgeStreamCoreDeps, d as EdgeWorkerLimits, e as StartStreamOptions, W as WarmupOptions, f as createUseEdgeStream } from './react-BgnmZ7M3.cjs';
3
+
4
+ declare const MAX_RETRIES = 200;
5
+ declare const INITIAL_RETRY_DELAY_MS = 1000;
6
+ declare const CONNECTION_TIMEOUT_MS = 10000;
7
+
8
+ type EdgeSocketConnectorDeps<TResponse extends Record<string, unknown>> = {
9
+ functionPath: string;
10
+ getAccessToken: () => Promise<string>;
11
+ getSupabaseUrl: () => string;
12
+ wsRef: Ref<WebSocket | null>;
13
+ isExplicitDisconnectRef: Ref<boolean>;
14
+ isConnectingRef: Ref<boolean>;
15
+ retryCountRef: Ref<number>;
16
+ socketOpenedAtRef: Ref<number | null>;
17
+ isWarmupReadyRef: Ref<boolean>;
18
+ warmupWaitersRef: Ref<Array<{
19
+ resolve: () => void;
20
+ reject: (err: Error) => void;
21
+ }>>;
22
+ passiveOnServerActionRef: Ref<((type: string, data: unknown) => void) | null>;
23
+ activeRequestRef: Ref<{
24
+ handler: (message: EdgeFunctionRawMessage, ctx: EdgeFunctionMessageContext<TResponse>) => void;
25
+ ctx: EdgeFunctionMessageContext<TResponse>;
26
+ } | null>;
27
+ lastWarmupPayloadRef: Ref<unknown>;
28
+ setIsConnected: (connected: boolean) => void;
29
+ closeSocket: () => void;
30
+ sendWarmupPayload: () => void;
31
+ settleWarmupWaiters: (error?: Error) => void;
32
+ };
33
+ /** Opens or reuses a WebSocket to a Supabase edge function */
34
+ declare function connectEdgeSocket<TResponse extends Record<string, unknown>>(deps: EdgeSocketConnectorDeps<TResponse>): Promise<void>;
35
+
36
+ /**
37
+ * Accumulates incoming WebSocket messages into a caller-defined object shape T.
38
+ */
39
+ declare function createStandardAiMessageHandler<T extends Record<string, unknown>>(defaults?: Partial<T>, callbacks?: StandardAiCallbacks): (message: EdgeFunctionRawMessage, ctx: EdgeFunctionMessageContext<T>) => void;
40
+
41
+ declare const PACKAGE_NAME: "supabase-edge-function-continuous-stream";
42
+
43
+ export { CONNECTION_TIMEOUT_MS, EdgeFunctionMessageContext, EdgeFunctionRawMessage, type EdgeSocketConnectorDeps, INITIAL_RETRY_DELAY_MS, MAX_RETRIES, PACKAGE_NAME, Ref, StandardAiCallbacks, connectEdgeSocket, createStandardAiMessageHandler };
@@ -0,0 +1,43 @@
1
+ import { R as Ref, E as EdgeFunctionRawMessage, a as EdgeFunctionMessageContext, S as StandardAiCallbacks } from './react-BgnmZ7M3.js';
2
+ export { D as DEFAULT_EDGE_WORKER_LIMITS, b as EdgeStreamConfig, c as EdgeStreamCoreDeps, d as EdgeWorkerLimits, e as StartStreamOptions, W as WarmupOptions, f as createUseEdgeStream } from './react-BgnmZ7M3.js';
3
+
4
+ declare const MAX_RETRIES = 200;
5
+ declare const INITIAL_RETRY_DELAY_MS = 1000;
6
+ declare const CONNECTION_TIMEOUT_MS = 10000;
7
+
8
+ type EdgeSocketConnectorDeps<TResponse extends Record<string, unknown>> = {
9
+ functionPath: string;
10
+ getAccessToken: () => Promise<string>;
11
+ getSupabaseUrl: () => string;
12
+ wsRef: Ref<WebSocket | null>;
13
+ isExplicitDisconnectRef: Ref<boolean>;
14
+ isConnectingRef: Ref<boolean>;
15
+ retryCountRef: Ref<number>;
16
+ socketOpenedAtRef: Ref<number | null>;
17
+ isWarmupReadyRef: Ref<boolean>;
18
+ warmupWaitersRef: Ref<Array<{
19
+ resolve: () => void;
20
+ reject: (err: Error) => void;
21
+ }>>;
22
+ passiveOnServerActionRef: Ref<((type: string, data: unknown) => void) | null>;
23
+ activeRequestRef: Ref<{
24
+ handler: (message: EdgeFunctionRawMessage, ctx: EdgeFunctionMessageContext<TResponse>) => void;
25
+ ctx: EdgeFunctionMessageContext<TResponse>;
26
+ } | null>;
27
+ lastWarmupPayloadRef: Ref<unknown>;
28
+ setIsConnected: (connected: boolean) => void;
29
+ closeSocket: () => void;
30
+ sendWarmupPayload: () => void;
31
+ settleWarmupWaiters: (error?: Error) => void;
32
+ };
33
+ /** Opens or reuses a WebSocket to a Supabase edge function */
34
+ declare function connectEdgeSocket<TResponse extends Record<string, unknown>>(deps: EdgeSocketConnectorDeps<TResponse>): Promise<void>;
35
+
36
+ /**
37
+ * Accumulates incoming WebSocket messages into a caller-defined object shape T.
38
+ */
39
+ declare function createStandardAiMessageHandler<T extends Record<string, unknown>>(defaults?: Partial<T>, callbacks?: StandardAiCallbacks): (message: EdgeFunctionRawMessage, ctx: EdgeFunctionMessageContext<T>) => void;
40
+
41
+ declare const PACKAGE_NAME: "supabase-edge-function-continuous-stream";
42
+
43
+ export { CONNECTION_TIMEOUT_MS, EdgeFunctionMessageContext, EdgeFunctionRawMessage, type EdgeSocketConnectorDeps, INITIAL_RETRY_DELAY_MS, MAX_RETRIES, PACKAGE_NAME, Ref, StandardAiCallbacks, connectEdgeSocket, createStandardAiMessageHandler };
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ import {
2
+ CONNECTION_TIMEOUT_MS,
3
+ INITIAL_RETRY_DELAY_MS,
4
+ MAX_RETRIES,
5
+ connectEdgeSocket,
6
+ createStandardAiMessageHandler,
7
+ createUseEdgeStream
8
+ } from "./chunk-GP2SEFBH.js";
9
+
10
+ // src/workerLimits.ts
11
+ var DEFAULT_EDGE_WORKER_LIMITS = {
12
+ edgeWorkerTtlMs: 15e4,
13
+ edgeRotateThresholdMs: 3e4,
14
+ overallTimeoutMs: 5 * 6e4
15
+ };
16
+
17
+ // src/index.ts
18
+ var PACKAGE_NAME = "supabase-edge-function-continuous-stream";
19
+ export {
20
+ CONNECTION_TIMEOUT_MS,
21
+ DEFAULT_EDGE_WORKER_LIMITS,
22
+ INITIAL_RETRY_DELAY_MS,
23
+ MAX_RETRIES,
24
+ PACKAGE_NAME,
25
+ connectEdgeSocket,
26
+ createStandardAiMessageHandler,
27
+ createUseEdgeStream
28
+ };
@@ -0,0 +1,75 @@
1
+ type EdgeFunctionRawMessage = Record<string, unknown> & {
2
+ type: string;
3
+ };
4
+ interface EdgeFunctionMessageContext<TResponse extends Record<string, unknown>> {
5
+ resolve: (value: TResponse) => void;
6
+ reject: (reason: Error) => void;
7
+ closeSocket: () => void;
8
+ clearOverallTimeout: () => void;
9
+ isResolved: () => boolean;
10
+ }
11
+ type EdgeStreamConfig = {
12
+ functionPath: string;
13
+ };
14
+ /** Callbacks a caller can wire up for side-effects during streaming. */
15
+ interface StandardAiCallbacks {
16
+ onServerAction?: (type: string, data: unknown) => void;
17
+ toUserMessage?: (error: unknown) => string | null;
18
+ }
19
+ type WarmupOptions = {
20
+ onServerAction?: (type: string, data: unknown) => void;
21
+ };
22
+ type StartStreamOptions<TResponse extends Record<string, unknown>> = {
23
+ /** Optional: Use a custom message handler. If omitted, it uses the Standard AI Protocol handler. */
24
+ onMessage?: (message: EdgeFunctionRawMessage, ctx: EdgeFunctionMessageContext<TResponse>) => void;
25
+ /** Optional: Defaults to seed the response object if not sent by the server. */
26
+ defaults?: Partial<TResponse>;
27
+ /** Optional: Listener for server actions (status, complete, etc.) */
28
+ onServerAction?: (type: string, data: unknown) => void;
29
+ /** Optional: Tags to invalidate on completion. */
30
+ invalidateTags?: {
31
+ tags: Array<string | {
32
+ type: string;
33
+ id?: string | number;
34
+ }>;
35
+ condition?: (response?: TResponse) => boolean;
36
+ };
37
+ /** Optional: Edge worker expiry from server checkpoint metadata */
38
+ getWorkerExpiresAt?: () => number | null;
39
+ };
40
+ /** Mutable ref bag used by connectEdgeSocket without React */
41
+ type Ref<T> = {
42
+ current: T;
43
+ };
44
+
45
+ /** Client-side edge worker timing configuration */
46
+ type EdgeWorkerLimits = {
47
+ edgeWorkerTtlMs: number;
48
+ edgeRotateThresholdMs: number;
49
+ overallTimeoutMs: number;
50
+ };
51
+ /** Default limits aligned with short-lived edge workers (~2.5 min TTL) */
52
+ declare const DEFAULT_EDGE_WORKER_LIMITS: EdgeWorkerLimits;
53
+
54
+ type EdgeStreamCoreDeps = {
55
+ getAccessToken: () => Promise<string>;
56
+ getSupabaseUrl: () => string;
57
+ workerLimits: EdgeWorkerLimits;
58
+ toUserMessage?: (error: unknown) => string | null;
59
+ invalidateTags?: (tags: Array<string | {
60
+ type: string;
61
+ id?: string | number;
62
+ }>) => void;
63
+ };
64
+ /** Factory for a WebSocket streaming hook wired to a host app */
65
+ declare function createUseEdgeStream(deps: EdgeStreamCoreDeps): <TPayload, TResponse extends Record<string, unknown>>(config: EdgeStreamConfig) => {
66
+ warmup: (payload: TPayload, options?: WarmupOptions) => Promise<void>;
67
+ send: (payload: TPayload, options: StartStreamOptions<TResponse>) => Promise<TResponse>;
68
+ abort: () => void;
69
+ isLoading: boolean;
70
+ error: Error | null;
71
+ data: TResponse | undefined;
72
+ isConnected: boolean;
73
+ };
74
+
75
+ export { DEFAULT_EDGE_WORKER_LIMITS as D, type EdgeFunctionRawMessage as E, type Ref as R, type StandardAiCallbacks as S, type WarmupOptions as W, type EdgeFunctionMessageContext as a, type EdgeStreamConfig as b, type EdgeStreamCoreDeps as c, type EdgeWorkerLimits as d, type StartStreamOptions as e, createUseEdgeStream as f };