@flamingo-stack/openframe-frontend-core 0.0.555 → 0.0.556
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/{chunk-6ER3AYJM.js → chunk-5756Q6UN.js} +3 -3
- package/dist/{chunk-E2IPBWGT.js → chunk-5EES5HD4.js} +6 -6
- package/dist/{chunk-ODRMPI3Y.js → chunk-5JWLZJK5.js} +2 -2
- package/dist/{chunk-CVCQW7WG.js → chunk-5M4PS3XX.js} +3 -3
- package/dist/{chunk-6QRKCVYR.cjs → chunk-7Q7GFIFB.cjs} +7 -7
- package/dist/{chunk-6QRKCVYR.cjs.map → chunk-7Q7GFIFB.cjs.map} +1 -1
- package/dist/{chunk-UXW63WCV.cjs → chunk-BNMKHG2L.cjs} +4 -4
- package/dist/{chunk-UXW63WCV.cjs.map → chunk-BNMKHG2L.cjs.map} +1 -1
- package/dist/{chunk-SST2YI7S.cjs → chunk-BZKUID4R.cjs} +14 -14
- package/dist/{chunk-SST2YI7S.cjs.map → chunk-BZKUID4R.cjs.map} +1 -1
- package/dist/{chunk-YAOBDOI5.cjs → chunk-C74PZU6M.cjs} +32 -32
- package/dist/{chunk-YAOBDOI5.cjs.map → chunk-C74PZU6M.cjs.map} +1 -1
- package/dist/{chunk-P7MSXPGL.js → chunk-CGUSNV6R.js} +8 -8
- package/dist/{chunk-N5A4AC2J.cjs → chunk-CRGIXNUE.cjs} +23 -23
- package/dist/{chunk-N5A4AC2J.cjs.map → chunk-CRGIXNUE.cjs.map} +1 -1
- package/dist/{chunk-Q6RC3YGW.cjs → chunk-DFJ7TWH6.cjs} +3 -3
- package/dist/{chunk-Q6RC3YGW.cjs.map → chunk-DFJ7TWH6.cjs.map} +1 -1
- package/dist/{chunk-HO3Q7NXJ.js → chunk-FLSEJSTN.js} +2 -2
- package/dist/{chunk-UL3WQWTF.js → chunk-FZT7BYNF.js} +2 -2
- package/dist/{chunk-XOKRFZBD.cjs → chunk-HOEOGFQM.cjs} +97 -97
- package/dist/{chunk-XOKRFZBD.cjs.map → chunk-HOEOGFQM.cjs.map} +1 -1
- package/dist/{chunk-MLVF3OXU.cjs → chunk-HYABNMBN.cjs} +76 -76
- package/dist/{chunk-MLVF3OXU.cjs.map → chunk-HYABNMBN.cjs.map} +1 -1
- package/dist/{chunk-J76LCSR4.cjs → chunk-ICG7JRYT.cjs} +40 -40
- package/dist/{chunk-J76LCSR4.cjs.map → chunk-ICG7JRYT.cjs.map} +1 -1
- package/dist/{chunk-P22HM2MO.cjs → chunk-JDUPAHJR.cjs} +18 -18
- package/dist/{chunk-P22HM2MO.cjs.map → chunk-JDUPAHJR.cjs.map} +1 -1
- package/dist/{chunk-B2GEKAAA.js → chunk-JRWOHH7K.js} +2 -2
- package/dist/{chunk-TXV444YV.js → chunk-JXJ4FTKN.js} +3 -3
- package/dist/{chunk-CU6Q2KQT.cjs → chunk-LZD3VSPS.cjs} +12 -12
- package/dist/{chunk-CU6Q2KQT.cjs.map → chunk-LZD3VSPS.cjs.map} +1 -1
- package/dist/{chunk-M5TWHFIN.cjs → chunk-Q5SZ2QTZ.cjs} +42 -42
- package/dist/{chunk-M5TWHFIN.cjs.map → chunk-Q5SZ2QTZ.cjs.map} +1 -1
- package/dist/{chunk-GSONSIWG.js → chunk-R7A7WHXP.js} +60 -35
- package/dist/chunk-R7A7WHXP.js.map +1 -0
- package/dist/{chunk-E56VNTBV.cjs → chunk-S4LL6QI6.cjs} +147 -147
- package/dist/{chunk-E56VNTBV.cjs.map → chunk-S4LL6QI6.cjs.map} +1 -1
- package/dist/{chunk-KQKU4VXB.cjs → chunk-STHVHAV4.cjs} +82 -57
- package/dist/chunk-STHVHAV4.cjs.map +1 -0
- package/dist/{chunk-A7ES3NZK.cjs → chunk-SXMJLNOF.cjs} +3 -3
- package/dist/{chunk-A7ES3NZK.cjs.map → chunk-SXMJLNOF.cjs.map} +1 -1
- package/dist/{chunk-CTTY5EKH.js → chunk-VF4DCOPD.js} +5 -5
- package/dist/{chunk-QTO3WYB2.cjs → chunk-VJTD2DGF.cjs} +3 -3
- package/dist/{chunk-QTO3WYB2.cjs.map → chunk-VJTD2DGF.cjs.map} +1 -1
- package/dist/{chunk-VGLDA77S.js → chunk-VRX6Y6A7.js} +2 -2
- package/dist/{chunk-L36R3G7Z.js → chunk-VXNMOGRP.js} +6 -6
- package/dist/{chunk-OR67WC32.js → chunk-WBQ3NOH7.js} +2 -2
- package/dist/{chunk-QMQQ37RW.js → chunk-WX5UCLL7.js} +2 -2
- package/dist/{chunk-QZ64JOE2.js → chunk-XGBVHQP7.js} +3 -3
- package/dist/{chunk-K432ES7N.js → chunk-YM2WHYYK.js} +4 -4
- package/dist/{chunk-KXLQITOX.cjs → chunk-Z3R3LOC5.cjs} +5 -5
- package/dist/{chunk-KXLQITOX.cjs.map → chunk-Z3R3LOC5.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +10 -10
- package/dist/components/case-studies/index.js +4 -4
- package/dist/components/chat/index.cjs +4 -4
- package/dist/components/chat/index.js +3 -3
- package/dist/components/contact/index.cjs +5 -5
- package/dist/components/contact/index.js +4 -4
- package/dist/components/docs/index.cjs +7 -7
- package/dist/components/docs/index.js +6 -6
- package/dist/components/embeds/index.cjs +5 -5
- package/dist/components/embeds/index.js +4 -4
- package/dist/components/faq/index.cjs +6 -6
- package/dist/components/faq/index.js +5 -5
- package/dist/components/features/index.cjs +4 -4
- package/dist/components/features/index.js +3 -3
- package/dist/components/help-center-pages/index.cjs +25 -25
- package/dist/components/help-center-pages/index.js +16 -16
- package/dist/components/index.cjs +138 -138
- package/dist/components/index.js +14 -14
- package/dist/components/meeting-scheduler/index.cjs +35 -35
- package/dist/components/meeting-scheduler/index.js +5 -5
- package/dist/components/navigation/index.cjs +4 -4
- package/dist/components/navigation/index.js +3 -3
- package/dist/components/onboarding-guides/index.cjs +8 -8
- package/dist/components/onboarding-guides/index.js +7 -7
- package/dist/components/related-content/index.cjs +6 -6
- package/dist/components/related-content/index.js +5 -5
- package/dist/components/tickets/index.cjs +8 -8
- package/dist/components/tickets/index.js +7 -7
- package/dist/components/ui/index.cjs +4 -4
- package/dist/components/ui/index.js +3 -3
- package/dist/hooks/index.cjs +3 -3
- package/dist/hooks/index.js +2 -2
- package/dist/index.cjs +4 -4
- package/dist/index.js +3 -3
- package/dist/nats/index.cjs +2 -2
- package/dist/nats/index.js +1 -1
- package/dist/nats/nats.d.ts.map +1 -1
- package/dist/nats/shared-connection.d.ts +13 -6
- package/dist/nats/shared-connection.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/nats/__tests__/reconnect-lifecycle.test.tsx +814 -0
- package/src/nats/nats.ts +56 -13
- package/src/nats/shared-connection.ts +132 -36
- package/dist/chunk-GSONSIWG.js.map +0 -1
- package/dist/chunk-KQKU4VXB.cjs.map +0 -1
- /package/dist/{chunk-6ER3AYJM.js.map → chunk-5756Q6UN.js.map} +0 -0
- /package/dist/{chunk-E2IPBWGT.js.map → chunk-5EES5HD4.js.map} +0 -0
- /package/dist/{chunk-ODRMPI3Y.js.map → chunk-5JWLZJK5.js.map} +0 -0
- /package/dist/{chunk-CVCQW7WG.js.map → chunk-5M4PS3XX.js.map} +0 -0
- /package/dist/{chunk-P7MSXPGL.js.map → chunk-CGUSNV6R.js.map} +0 -0
- /package/dist/{chunk-HO3Q7NXJ.js.map → chunk-FLSEJSTN.js.map} +0 -0
- /package/dist/{chunk-UL3WQWTF.js.map → chunk-FZT7BYNF.js.map} +0 -0
- /package/dist/{chunk-B2GEKAAA.js.map → chunk-JRWOHH7K.js.map} +0 -0
- /package/dist/{chunk-TXV444YV.js.map → chunk-JXJ4FTKN.js.map} +0 -0
- /package/dist/{chunk-CTTY5EKH.js.map → chunk-VF4DCOPD.js.map} +0 -0
- /package/dist/{chunk-VGLDA77S.js.map → chunk-VRX6Y6A7.js.map} +0 -0
- /package/dist/{chunk-L36R3G7Z.js.map → chunk-VXNMOGRP.js.map} +0 -0
- /package/dist/{chunk-OR67WC32.js.map → chunk-WBQ3NOH7.js.map} +0 -0
- /package/dist/{chunk-QMQQ37RW.js.map → chunk-WX5UCLL7.js.map} +0 -0
- /package/dist/{chunk-QZ64JOE2.js.map → chunk-XGBVHQP7.js.map} +0 -0
- /package/dist/{chunk-K432ES7N.js.map → chunk-YM2WHYYK.js.map} +0 -0
package/src/nats/nats.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type {
|
|
|
3
3
|
Consumer,
|
|
4
4
|
ConsumerEvents,
|
|
5
5
|
ConsumerMessages,
|
|
6
|
+
DebugEvents,
|
|
7
|
+
Events,
|
|
6
8
|
JsMsg,
|
|
7
9
|
MsgHdrs as NatsHeaders,
|
|
8
10
|
Msg,
|
|
@@ -278,14 +280,58 @@ function mapOptionsToConnectionOptions(
|
|
|
278
280
|
}
|
|
279
281
|
}
|
|
280
282
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
283
|
+
/**
|
|
284
|
+
* Map a nats.ws status event onto our own status vocabulary.
|
|
285
|
+
*
|
|
286
|
+
* Exact enum matches, never substring tests. Substring matching reported
|
|
287
|
+
* `staleConnection` and `client initiated reconnect` as CONNECTED — both
|
|
288
|
+
* contain "connect" and neither contains "disconnect" — so the wrapper
|
|
289
|
+
* announced a live connection at the exact moment the client had given up on
|
|
290
|
+
* one. Downstream that is worse than silence: `NatsProvider` reads a
|
|
291
|
+
* `connected` that follows an earlier connection as a RECONNECT and bumps
|
|
292
|
+
* `reconnectionCount`, which every subscriber takes as "the tail came back" —
|
|
293
|
+
* resubscribing against a dying client and refetching persisted history.
|
|
294
|
+
* `staleConnection` is the one an idle mobile app hits routinely: no pong
|
|
295
|
+
* within `pingInterval * maxPingOut`, i.e. after a spell in the background.
|
|
296
|
+
*
|
|
297
|
+
* `StaleConnection` maps to `disconnected` rather than being ignored on the
|
|
298
|
+
* grounds that `Disconnect` follows it anyway — it does not always. nats.ws
|
|
299
|
+
* tears the transport down by draining `bufferedAmount` first, and a
|
|
300
|
+
* black-holed socket (no FIN, which is how a mobile link usually dies) never
|
|
301
|
+
* drains, so `Disconnect` can fail to arrive at all. Reporting it is then the
|
|
302
|
+
* only honest answer available: consumers stop trusting a tail the client has
|
|
303
|
+
* already given up on. It does NOT by itself recover that connection — the
|
|
304
|
+
* retry it arms short-circuits, because a protocol that never closed still
|
|
305
|
+
* reports `isConnected()`. Getting the socket back from that state needs a
|
|
306
|
+
* force-close path the client does not currently expose.
|
|
307
|
+
*
|
|
308
|
+
* The enums arrive as arguments rather than through a module-scope import
|
|
309
|
+
* because `nats.ws` is loaded lazily (see {@link importNats}) to keep this
|
|
310
|
+
* module safe to import from a Next server bundle. A hand-copied literal is
|
|
311
|
+
* what this is undoing, so it is not reintroduced here.
|
|
312
|
+
*/
|
|
313
|
+
function mapNatsTypeToStatus(
|
|
314
|
+
type: Events | DebugEvents,
|
|
315
|
+
events: typeof import('nats.ws').Events,
|
|
316
|
+
debugEvents: typeof import('nats.ws').DebugEvents,
|
|
317
|
+
): NatsStatus | null {
|
|
318
|
+
switch (type) {
|
|
319
|
+
case events.Disconnect:
|
|
320
|
+
return 'disconnected'
|
|
321
|
+
case events.Reconnect:
|
|
322
|
+
return 'connected'
|
|
323
|
+
case events.Error:
|
|
324
|
+
return 'error'
|
|
325
|
+
case debugEvents.Reconnecting:
|
|
326
|
+
case debugEvents.ClientInitiatedReconnect:
|
|
327
|
+
return 'reconnecting'
|
|
328
|
+
case debugEvents.StaleConnection:
|
|
329
|
+
return 'disconnected'
|
|
330
|
+
// Events.Update (cluster gossip), Events.LDM and DebugEvents.PingTimer say
|
|
331
|
+
// nothing about reachability.
|
|
332
|
+
default:
|
|
333
|
+
return null
|
|
334
|
+
}
|
|
289
335
|
}
|
|
290
336
|
|
|
291
337
|
/**
|
|
@@ -372,15 +418,12 @@ export function createNatsClient(options: NatsClientOptions): NatsClient {
|
|
|
372
418
|
try {
|
|
373
419
|
for await (const s of conn.status()) {
|
|
374
420
|
if (signal.aborted) return
|
|
375
|
-
const mapped = mapNatsTypeToStatus(
|
|
421
|
+
const mapped = mapNatsTypeToStatus(s.type, nats.Events, nats.DebugEvents)
|
|
376
422
|
if (mapped) {
|
|
377
423
|
if (mapped === 'connected' && backoff) {
|
|
378
424
|
backoff.reset()
|
|
379
425
|
}
|
|
380
|
-
emitStatus({ status: mapped, data:
|
|
381
|
-
if (mapped === 'closed') {
|
|
382
|
-
nc = null
|
|
383
|
-
}
|
|
426
|
+
emitStatus({ status: mapped, data: s.data })
|
|
384
427
|
}
|
|
385
428
|
}
|
|
386
429
|
} catch (e) {
|
|
@@ -26,17 +26,24 @@ export interface NatsReconnectionBackoff {
|
|
|
26
26
|
export interface SharedConnection {
|
|
27
27
|
wsUrl: string
|
|
28
28
|
client: NatsClient
|
|
29
|
-
connectPromise: Promise<void> | null
|
|
30
29
|
refCount: number
|
|
31
30
|
closeTimer: ReturnType<typeof setTimeout> | null
|
|
32
31
|
retryTimer: ReturnType<typeof setTimeout> | null
|
|
33
32
|
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
33
|
+
* The lifecycle driving reconnect, held as its own `scheduleRetry`. When set,
|
|
34
|
+
* other consumers observe status only and skip their own scheduleRetry —
|
|
35
|
+
* otherwise every disconnect starts one backoff schedule per attached
|
|
36
|
+
* consumer, all dialling the same connection on their own clocks.
|
|
38
37
|
*/
|
|
39
|
-
retryOwner:
|
|
38
|
+
retryOwner: (() => void) | null
|
|
39
|
+
/**
|
|
40
|
+
* The `scheduleRetry` of every lifecycle currently observing this connection,
|
|
41
|
+
* in attach order — the same value that goes into `retryOwner`, so a
|
|
42
|
+
* lifecycle has one identity rather than two. Only the owner drives
|
|
43
|
+
* reconnect, so when it gives the loop up the roster is what a successor is
|
|
44
|
+
* found in; see {@link handOffRetry}.
|
|
45
|
+
*/
|
|
46
|
+
retrySchedulers: Set<() => void>
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
export interface AcquireClientOptions {
|
|
@@ -99,11 +106,11 @@ export function acquireClient(url: string, opts?: AcquireClientOptions): SharedC
|
|
|
99
106
|
conn = {
|
|
100
107
|
wsUrl: url,
|
|
101
108
|
client,
|
|
102
|
-
connectPromise: null,
|
|
103
109
|
refCount: 0,
|
|
104
110
|
closeTimer: null,
|
|
105
111
|
retryTimer: null,
|
|
106
112
|
retryOwner: null,
|
|
113
|
+
retrySchedulers: new Set(),
|
|
107
114
|
}
|
|
108
115
|
connections.set(url, conn)
|
|
109
116
|
}
|
|
@@ -146,11 +153,13 @@ export function getSharedConnectionFor(url: string | null | undefined): SharedCo
|
|
|
146
153
|
|
|
147
154
|
// ---------------------------------------------------------------------------
|
|
148
155
|
// Connection lifecycle: retry + status loop, shared by NatsProvider and the
|
|
149
|
-
// chat hooks.
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
156
|
+
// chat hooks. Every consumer that wants to observe or drive reconnect calls
|
|
157
|
+
// startConnectionLifecycle(). The first to claim retryOwner runs the actual
|
|
158
|
+
// retry loop; later attachers observe status only. When the owner gives the
|
|
159
|
+
// loop up it is handed to a survivor if the connection still needs one; while
|
|
160
|
+
// the connection is healthy no handoff is needed, because the next status
|
|
161
|
+
// event lets a survivor claim ownership opportunistically inside
|
|
162
|
+
// scheduleRetry.
|
|
154
163
|
// ---------------------------------------------------------------------------
|
|
155
164
|
|
|
156
165
|
export interface ConnectionLifecycleOptions {
|
|
@@ -176,14 +185,30 @@ export interface ConnectionLifecycleHandle {
|
|
|
176
185
|
|
|
177
186
|
const defaultShouldRetryOn = (status: NatsStatus) => status === 'closed' || status === 'disconnected'
|
|
178
187
|
|
|
188
|
+
/**
|
|
189
|
+
* Offer the retry loop to another lifecycle on this connection.
|
|
190
|
+
*
|
|
191
|
+
* A retry is only ever armed from a status event or a failed dial, and a
|
|
192
|
+
* connection that is already down produces neither on its own — so whenever
|
|
193
|
+
* the owner gives the loop up while the connection still needs dialling, a
|
|
194
|
+
* successor has to be pushed rather than left waiting for an event that is not
|
|
195
|
+
* coming.
|
|
196
|
+
*
|
|
197
|
+
* Candidates that cannot service this URL decline inside `scheduleRetry`, so
|
|
198
|
+
* this walks the roster until one actually takes it. Callers must clear any
|
|
199
|
+
* timer of their own first: a successor that claims while a timer is still
|
|
200
|
+
* armed will not arm one, and the departing owner's timer no longer speaks for
|
|
201
|
+
* the connection.
|
|
202
|
+
*/
|
|
203
|
+
function handOffRetry(conn: SharedConnection): void {
|
|
204
|
+
for (const takeOver of conn.retrySchedulers) {
|
|
205
|
+
takeOver()
|
|
206
|
+
if (conn.retryOwner) return
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
179
210
|
export function startConnectionLifecycle(options: ConnectionLifecycleOptions): ConnectionLifecycleHandle {
|
|
180
211
|
const { conn, wsUrl } = options
|
|
181
|
-
// Each lifecycle gets its own identity for retry ownership. The first lifecycle to claim
|
|
182
|
-
// an unowned connection drives reconnect; later attachers observe status only until the
|
|
183
|
-
// owner releases (see opportunistic claim in scheduleRetry).
|
|
184
|
-
const ownerToken = {}
|
|
185
|
-
if (!conn.retryOwner) conn.retryOwner = ownerToken
|
|
186
|
-
|
|
187
212
|
let closed = false
|
|
188
213
|
let retryAttempt = 0
|
|
189
214
|
|
|
@@ -195,16 +220,33 @@ export function startConnectionLifecycle(options: ConnectionLifecycleOptions): C
|
|
|
195
220
|
}
|
|
196
221
|
}
|
|
197
222
|
|
|
223
|
+
// A lifecycle's identity IS its `scheduleRetry`: that function goes on the
|
|
224
|
+
// connection's roster and, for whichever lifecycle holds the loop, into
|
|
225
|
+
// `retryOwner` — one identity, not two.
|
|
226
|
+
conn.retrySchedulers.add(scheduleRetry)
|
|
227
|
+
if (!conn.retryOwner) conn.retryOwner = scheduleRetry
|
|
228
|
+
|
|
198
229
|
function scheduleRetry() {
|
|
199
230
|
if (closed) return
|
|
200
231
|
if (getSharedConnectionFor(wsUrl) !== conn) return
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
232
|
+
// This lifecycle now wants a different URL, so it must not drive this
|
|
233
|
+
// connection — not from its own status events, and not as a handoff
|
|
234
|
+
// successor. Without the guard, two consumers that had both moved on
|
|
235
|
+
// passed ownership back and forth indefinitely, running onBeforeReconnect
|
|
236
|
+
// — a token refresh in the real callers — on every pass and dialling
|
|
237
|
+
// nothing. The armed callback re-checks, because the URL can move between
|
|
238
|
+
// arming and firing.
|
|
239
|
+
if (options.getFreshUrl() !== wsUrl) return
|
|
240
|
+
if (!conn.retryOwner) conn.retryOwner = scheduleRetry
|
|
241
|
+
if (conn.retryOwner !== scheduleRetry) return
|
|
242
|
+
|
|
243
|
+
// One outage can raise more than one status: nats.ws reports
|
|
244
|
+
// `staleConnection` and then, once the transport is down, `disconnect`.
|
|
245
|
+
// Re-arming on the second would spend a backoff tier on the same outage —
|
|
246
|
+
// on the defaults, waiting 2000ms where the caller asked for 1000. The
|
|
247
|
+
// armed timer re-validates everything when it fires, so the first one
|
|
248
|
+
// scheduled for an outage is always the right one to keep.
|
|
249
|
+
if (conn.retryTimer) return
|
|
208
250
|
|
|
209
251
|
const cfg = options.backoff ?? {}
|
|
210
252
|
const fastRetries = cfg.fastRetries ?? 0
|
|
@@ -225,6 +267,16 @@ export function startConnectionLifecycle(options: ConnectionLifecycleOptions): C
|
|
|
225
267
|
if (closed) return
|
|
226
268
|
if (getSharedConnectionFor(wsUrl) !== conn) return
|
|
227
269
|
|
|
270
|
+
// Nothing to reconnect. Reached when the dial that was already in flight
|
|
271
|
+
// succeeded while this retry sat in its backoff — a handoff during a
|
|
272
|
+
// healthy first connect is enough to get here — and `onBeforeReconnect`
|
|
273
|
+
// is a token refresh in the real callers, so it must not run on a
|
|
274
|
+
// connection that came back on its own.
|
|
275
|
+
if (conn.client.isConnected()) {
|
|
276
|
+
retryAttempt = 0
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
|
|
228
280
|
try {
|
|
229
281
|
await options.onBeforeReconnect?.()
|
|
230
282
|
} catch {
|
|
@@ -234,17 +286,33 @@ export function startConnectionLifecycle(options: ConnectionLifecycleOptions): C
|
|
|
234
286
|
if (getSharedConnectionFor(wsUrl) !== conn) return
|
|
235
287
|
|
|
236
288
|
const freshUrl = options.getFreshUrl()
|
|
237
|
-
if (freshUrl !== wsUrl)
|
|
289
|
+
if (freshUrl !== wsUrl) {
|
|
290
|
+
// This lifecycle has moved to a different URL, so it must not dial this
|
|
291
|
+
// connection again while holding the loop — that is the dead end the
|
|
292
|
+
// rest of this module exists to avoid. Give the loop up and push it to
|
|
293
|
+
// someone still on this URL.
|
|
294
|
+
if (conn.retryOwner === scheduleRetry) {
|
|
295
|
+
conn.retryOwner = null
|
|
296
|
+
// A status that landed while the refresh above was awaited can have
|
|
297
|
+
// armed a new timer under this ownership. It belongs to a lifecycle
|
|
298
|
+
// that is leaving, and a successor claiming while it is armed would
|
|
299
|
+
// return early believing the loop is covered — then this timer fires,
|
|
300
|
+
// finds someone else owns the loop, and bails without handing on.
|
|
301
|
+
if (conn.retryTimer) {
|
|
302
|
+
clearTimeout(conn.retryTimer)
|
|
303
|
+
conn.retryTimer = null
|
|
304
|
+
}
|
|
305
|
+
handOffRetry(conn)
|
|
306
|
+
}
|
|
307
|
+
return
|
|
308
|
+
}
|
|
238
309
|
|
|
239
310
|
try {
|
|
240
|
-
conn.
|
|
241
|
-
conn.connectPromise = conn.client.connect()
|
|
242
|
-
await conn.connectPromise
|
|
311
|
+
await conn.client.connect()
|
|
243
312
|
if (!closed && getSharedConnectionFor(wsUrl) === conn) {
|
|
244
313
|
retryAttempt = 0
|
|
245
314
|
}
|
|
246
315
|
} catch {
|
|
247
|
-
conn.connectPromise = null
|
|
248
316
|
if (!closed && getSharedConnectionFor(wsUrl) === conn) {
|
|
249
317
|
scheduleRetry()
|
|
250
318
|
}
|
|
@@ -269,12 +337,20 @@ export function startConnectionLifecycle(options: ConnectionLifecycleOptions): C
|
|
|
269
337
|
emitSynthetic('connected')
|
|
270
338
|
}
|
|
271
339
|
|
|
340
|
+
// Dial through the client rather than caching a promise on the connection.
|
|
341
|
+
// A `conn.connectPromise` used to be assigned here with `||=` and then left
|
|
342
|
+
// in place after a SUCCESSFUL connect, so it doubled as "this connection has
|
|
343
|
+
// been dialled at some point": a consumer that acquired a still-cached
|
|
344
|
+
// connection whose socket had since died joined that long-settled promise
|
|
345
|
+
// instead of dialling. No connect, no failure, so nothing scheduled a retry,
|
|
346
|
+
// and no status event was coming to arm one either — the connection stayed
|
|
347
|
+
// dead for as long as anything held a reference to it. `NatsClient.connect()`
|
|
348
|
+
// already short-circuits on a live connection and joins its own in-flight
|
|
349
|
+
// dial, so the second copy of that bookkeeping bought nothing.
|
|
272
350
|
void (async () => {
|
|
273
351
|
try {
|
|
274
|
-
|
|
275
|
-
await conn.connectPromise
|
|
352
|
+
await conn.client.connect()
|
|
276
353
|
} catch {
|
|
277
|
-
conn.connectPromise = null
|
|
278
354
|
if (closed) return
|
|
279
355
|
|
|
280
356
|
emitSynthetic('disconnected')
|
|
@@ -286,13 +362,33 @@ export function startConnectionLifecycle(options: ConnectionLifecycleOptions): C
|
|
|
286
362
|
stop() {
|
|
287
363
|
closed = true
|
|
288
364
|
unsubStatus()
|
|
365
|
+
conn.retrySchedulers.delete(scheduleRetry)
|
|
366
|
+
|
|
367
|
+
if (conn.retryOwner !== scheduleRetry) {
|
|
368
|
+
// Not ours to cancel. `retryTimer` lives on the CONNECTION, and
|
|
369
|
+
// clearing it unconditionally cancelled the owner's armed retry — so
|
|
370
|
+
// any consumer merely re-running its effect (an `enabled` toggle, a
|
|
371
|
+
// dialog closing, a route change) during a backoff killed reconnect
|
|
372
|
+
// for everyone. Nothing rescheduled it either, for the reason
|
|
373
|
+
// handOffRetry exists. The connection stayed down for good.
|
|
374
|
+
return
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
conn.retryOwner = null
|
|
378
|
+
const wasRetrying = conn.retryTimer !== null
|
|
289
379
|
if (conn.retryTimer) {
|
|
290
380
|
clearTimeout(conn.retryTimer)
|
|
291
381
|
conn.retryTimer = null
|
|
292
382
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
383
|
+
|
|
384
|
+
// A healthy connection with nothing armed needs no successor: the next
|
|
385
|
+
// disconnect lets a survivor claim opportunistically. `wasRetrying` is
|
|
386
|
+
// read before the clearTimeout above, and the remaining case — the timer
|
|
387
|
+
// already fired and its callback is mid-dial — is caught by the
|
|
388
|
+
// connection not being connected. The successor backs off on its OWN
|
|
389
|
+
// attempt counter, which is zero unless it has driven the loop before.
|
|
390
|
+
if (!wasRetrying && conn.client.isConnected()) return
|
|
391
|
+
handOffRetry(conn)
|
|
296
392
|
},
|
|
297
393
|
}
|
|
298
394
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/nats/nats.ts","../src/nats/shared-connection.ts","../src/nats/nats-provider.tsx","../src/nats/use-nats-subscription.ts"],"sourcesContent":["import type {\n ConnectionOptions,\n Consumer,\n ConsumerEvents,\n ConsumerMessages,\n JsMsg,\n MsgHdrs as NatsHeaders,\n Msg,\n NatsConnection,\n Subscription,\n} from 'nats.ws'\n\nexport type { JsMsg, Msg, Subscription } from 'nats.ws'\n\nexport type JetStreamDeliverPolicy = 'new' | 'byStartSequence'\n\nexport interface JetStreamOrderedSubscribeOptions {\n streamName: string\n filterSubject: string\n deliverPolicy: JetStreamDeliverPolicy\n /** Required when deliverPolicy === 'byStartSequence'. */\n optStartSeq?: number\n /**\n * Auto-cleanup the ephemeral consumer after this idle time. Defaults to\n * nats.ws's own. Applies to the consumer created first: nats.ws only reads it\n * when the start sequence is the one it was built with, so every consumer it\n * recreates afterwards reverts to the library default.\n */\n inactiveThresholdMs?: number\n /** AbortSignal to tear down the consumer. */\n signal?: AbortSignal\n /**\n * The ordered consumer had to be rebuilt: the server dropped the ephemeral\n * consumer (its inactivity threshold elapsed while the page was suspended or\n * offline), heartbeats were missed, or a sequence gap was detected. nats.ws\n * recreates it from the last delivered sequence, so this is NOT an error —\n * but everything that aged out of the stream while it was gone is\n * unrecoverable from the tail, and no connection event announces it (the\n * WebSocket never dropped). Callers that keep persisted history alongside\n * the live tail must refetch it here, or they keep showing a snapshot from\n * before the gap.\n */\n onRecovered?: () => void\n}\n\nexport interface JetStreamSubscriptionHandle {\n unsubscribe(): void\n}\n\nexport interface NatsClientOptions {\n /**\n * NATS server URL(s), for example:\n * - \"wss://nats.example.com:443\"\n * - [\"wss://nats-1.example.com:443\", \"wss://nats-2.example.com:443\"]\n */\n servers: string | string[]\n\n /**\n * Connection name (shows up in NATS monitoring).\n */\n name?: string\n\n /**\n * Auth options (pick one: token or user/pass).\n */\n token?: string\n user?: string\n pass?: string\n\n /**\n * Reconnect behavior.\n */\n reconnect?: boolean\n maxReconnectAttempts?: number\n reconnectTimeWaitMs?: number\n\n /**\n * Exponential backoff for reconnect delays.\n * When set, uses `reconnectDelayHandler` from nats.ws under the hood,\n * overriding `reconnectTimeWaitMs`.\n */\n exponentialBackoff?: {\n /** Initial delay in ms (default: 1000) */\n initialDelayMs?: number\n /** Maximum delay cap in ms (default: 30000) */\n maxDelayMs?: number\n /** Multiplier per attempt (default: 2) */\n multiplier?: number\n /** Add random jitter 0-50% of delay to prevent thundering herd (default: true) */\n jitter?: boolean\n }\n\n /**\n * Ping behavior (keep-alive).\n */\n pingIntervalMs?: number\n maxPingOut?: number\n\n /**\n * Optional inbox prefix (useful if you want to isolate request/reply inboxes).\n */\n inboxPrefix?: string\n\n /**\n * Connection timeout in milliseconds (maps to `nats.ws` connect option `timeout`).\n * If you see `NatsError: TIMEOUT` during connect, increase this.\n */\n connectTimeoutMs?: number\n}\n\nexport interface NatsSubscribeOptions {\n /**\n * Queue group for load-balancing messages across subscribers.\n */\n queue?: string\n\n /**\n * Auto-unsubscribe after receiving this many messages.\n */\n max?: number\n\n /**\n * Abort signal to stop message iteration and unsubscribe.\n */\n signal?: AbortSignal\n}\n\nexport type NatsHeadersInit = Record<string, string> | NatsHeaders | undefined\n\nexport interface NatsPublishOptions {\n headers?: NatsHeadersInit\n}\n\nexport interface NatsRequestOptions {\n timeoutMs?: number\n headers?: NatsHeadersInit\n}\n\nexport interface NatsSubscriptionHandle {\n readonly subscription: Subscription\n unsubscribe(): void\n}\n\nexport type NatsStatus =\n | 'connecting'\n | 'connected'\n | 'disconnected'\n | 'reconnecting'\n | 'closed'\n | 'error'\n\nexport interface NatsStatusEvent {\n status: NatsStatus\n data?: unknown\n}\n\nexport interface NatsClient {\n connect(): Promise<void>\n close(): Promise<void>\n\n isConnected(): boolean\n\n publishBytes(subject: string, payload: Uint8Array, options?: NatsPublishOptions): void\n publishString(subject: string, payload: string, options?: NatsPublishOptions): void\n publishJson<T>(subject: string, payload: T, options?: NatsPublishOptions): void\n\n requestBytes(subject: string, payload: Uint8Array, options?: NatsRequestOptions): Promise<Msg>\n requestString(subject: string, payload: string, options?: NatsRequestOptions): Promise<string>\n requestJson<TResponse, TRequest = unknown>(\n subject: string,\n payload: TRequest,\n options?: NatsRequestOptions,\n ): Promise<TResponse>\n\n subscribeBytes(\n subject: string,\n onMessage: (msg: Msg) => void | Promise<void>,\n options?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle\n subscribeString(\n subject: string,\n onMessage: (payload: string, msg: Msg) => void | Promise<void>,\n options?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle\n subscribeJson<T>(\n subject: string,\n onMessage: (payload: T, msg: Msg) => void | Promise<void>,\n options?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle\n\n /**\n * Subscribe to a JetStream subject via an ephemeral OrderedConsumer (no acks).\n * Use `optStartSeq` with `deliverPolicy: 'byStartSequence'` to resume from a known offset,\n * or `deliverPolicy: 'new'` to live-tail.\n */\n subscribeJetStreamOrdered(\n onMessage: (msg: JsMsg) => void | Promise<void>,\n options: JetStreamOrderedSubscribeOptions,\n ): Promise<JetStreamSubscriptionHandle>\n\n onStatus(listener: (event: NatsStatusEvent) => void): () => void\n}\n\nfunction assertClientSide(): void {\n // This wrapper is meant for browser/Tauri usage via WebSockets.\n // Keep it safe to import from Next.js server bundles by throwing only when used.\n if (typeof window === 'undefined') {\n throw new Error('NATS client can only connect from the browser/runtime with WebSocket support (window is undefined).')\n }\n}\n\nasync function importNats(): Promise<typeof import('nats.ws')> {\n // Browser/Tauri only: always use the websocket client (no Node-only deps).\n return await import('nats.ws')\n}\n\nfunction toNatsHeaders(nats: typeof import('nats.ws'), init: NatsHeadersInit): NatsHeaders | undefined {\n if (!init) return undefined\n if (typeof (init as NatsHeaders).get === 'function') return init as NatsHeaders\n\n const h = nats.headers()\n for (const [k, v] of Object.entries(init as Record<string, string>)) {\n if (v !== undefined && v !== null) h.set(k, String(v))\n }\n return h\n}\n\n/** Returns a cryptographically random float in [0, 1). */\nfunction cryptoRandom(): number {\n const buf = new Uint32Array(1)\n crypto.getRandomValues(buf)\n return buf[0] / (0xffffffff + 1)\n}\n\ninterface ExponentialBackoffHandle {\n handler: () => number\n reset: () => void\n}\n\nfunction createExponentialBackoffHandler(opts: NonNullable<NatsClientOptions['exponentialBackoff']>): ExponentialBackoffHandle {\n const initialDelay = opts.initialDelayMs ?? 1000\n const maxDelay = opts.maxDelayMs ?? 30_000\n const multiplier = opts.multiplier ?? 2\n const jitter = opts.jitter ?? true\n\n let attempt = 0\n\n return {\n handler: (): number => {\n const delay = Math.min(initialDelay * multiplier ** attempt, maxDelay)\n attempt++\n return jitter ? delay * (0.5 + cryptoRandom() * 0.5) : delay\n },\n reset: () => {\n attempt = 0\n },\n }\n}\n\nfunction mapOptionsToConnectionOptions(\n opts: NatsClientOptions,\n backoff?: ExponentialBackoffHandle,\n): ConnectionOptions {\n return {\n servers: opts.servers,\n name: opts.name,\n token: opts.token,\n user: opts.user,\n pass: opts.pass,\n timeout: opts.connectTimeoutMs ?? 15000,\n reconnect: opts.reconnect ?? true,\n maxReconnectAttempts: opts.maxReconnectAttempts,\n reconnectTimeWait: opts.reconnectTimeWaitMs,\n reconnectDelayHandler: backoff?.handler,\n pingInterval: opts.pingIntervalMs,\n maxPingOut: opts.maxPingOut,\n inboxPrefix: opts.inboxPrefix,\n }\n}\n\nfunction mapNatsTypeToStatus(type: unknown): NatsStatus | null {\n const t = String(type).toLowerCase()\n if (t.includes('disconnect')) return 'disconnected'\n if (t === 'reconnecting') return 'reconnecting'\n if (t === 'reconnect' || t.includes('connect')) return 'connected'\n if (t.includes('error')) return 'error'\n if (t.includes('close')) return 'closed'\n return null\n}\n\n/**\n * Report an ordered consumer rebuilding itself.\n *\n * nats.ws announces this on the message iterator's status channel and nowhere\n * else — the iterator keeps yielding, the WebSocket never drops, and nothing\n * consumes that channel unless someone asks for it. That silence is what lets a\n * tail resume after a suspended page having permanently missed whatever expired\n * from the stream meanwhile: the recreation resumes from the last delivered\n * sequence, so the gap is invisible from the tail alone.\n *\n * `recreatedEvent` is nats.ws's own enum value rather than a literal of ours: a\n * copy that drifted from the library would kill this signal silently, which is\n * the failure mode the signal exists to prevent.\n *\n * The status channel is closed along with the iterator, which ends this loop.\n */\nfunction watchForRecovery(\n iter: ConsumerMessages,\n recreatedEvent: ConsumerEvents.OrderedConsumerRecreated,\n onRecovered?: () => void,\n): void {\n if (!onRecovered) return\n void (async () => {\n try {\n const status = await iter.status()\n for await (const event of status) {\n if (event.type !== recreatedEvent) continue\n try {\n onRecovered()\n } catch (e) {\n // A caller that throws must not take the watch down with it — this\n // loop is the only thing reporting recoveries for this subscription.\n console.warn('[nats] onRecovered threw:', e)\n }\n }\n } catch {\n // Status ends with the subscription — nothing left to report.\n }\n })()\n}\n\nexport function createNatsClient(options: NatsClientOptions): NatsClient {\n let nc: NatsConnection | null = null\n let statusLoopAbort: AbortController | null = null\n let connectInFlight: Promise<void> | null = null\n\n const backoff = options.exponentialBackoff\n ? createExponentialBackoffHandler(options.exponentialBackoff)\n : undefined\n\n const statusListeners = new Set<(event: NatsStatusEvent) => void>()\n\n function emitStatus(event: NatsStatusEvent) {\n for (const listener of statusListeners) {\n try {\n listener(event)\n } catch {\n // ignore listener failures\n }\n }\n }\n\n async function connect(): Promise<void> {\n if (nc && !nc.isClosed()) return\n if (connectInFlight) return connectInFlight\n assertClientSide()\n\n connectInFlight = (async () => {\n try {\n emitStatus({ status: 'connecting' })\n\n const nats = await importNats()\n const conn = await nats.connect(mapOptionsToConnectionOptions(options, backoff))\n nc = conn\n\n emitStatus({ status: 'connected' })\n\n statusLoopAbort = new AbortController()\n const signal = statusLoopAbort.signal\n\n ;(async () => {\n try {\n for await (const s of conn.status()) {\n if (signal.aborted) return\n const mapped = mapNatsTypeToStatus((s as any)?.type)\n if (mapped) {\n if (mapped === 'connected' && backoff) {\n backoff.reset()\n }\n emitStatus({ status: mapped, data: (s as any)?.data })\n if (mapped === 'closed') {\n nc = null\n }\n }\n }\n } catch (e) {\n if (!signal.aborted) {\n emitStatus({ status: 'error', data: e })\n \n if (nc === conn && conn.isClosed()) {\n nc = null\n emitStatus({ status: 'closed' })\n }\n }\n }\n })().catch(() => {\n // ignore\n })\n } finally {\n connectInFlight = null\n }\n })()\n\n return connectInFlight\n }\n\n async function close(): Promise<void> {\n const conn = nc\n nc = null\n\n if (statusLoopAbort) {\n try {\n statusLoopAbort.abort()\n } catch {\n // ignore\n }\n statusLoopAbort = null\n }\n\n if (!conn) return\n try {\n await conn.drain()\n } finally {\n try {\n await conn.close()\n } finally {\n emitStatus({ status: 'closed' })\n }\n }\n }\n\n function requireConnection(): NatsConnection {\n if (!nc) throw new Error('NATS is not connected. Call client.connect() first.')\n return nc\n }\n\n function isConnected(): boolean {\n return Boolean(nc) && !nc!.isClosed()\n }\n\n function publishBytes(subject: string, payload: Uint8Array, opts?: NatsPublishOptions): void {\n const conn = requireConnection()\n ;(async () => {\n const nats = await importNats()\n conn.publish(subject, payload, { headers: toNatsHeaders(nats, opts?.headers) })\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n }\n\n function publishString(subject: string, payload: string, opts?: NatsPublishOptions): void {\n ;(async () => {\n const nats = await importNats()\n const sc = nats.StringCodec()\n publishBytes(subject, sc.encode(payload), opts)\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n }\n\n function publishJson<T>(subject: string, payload: T, opts?: NatsPublishOptions): void {\n ;(async () => {\n const nats = await importNats()\n const jc = nats.JSONCodec<T>()\n publishBytes(subject, jc.encode(payload), opts)\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n }\n\n async function requestBytes(subject: string, payload: Uint8Array, opts?: NatsRequestOptions): Promise<Msg> {\n const conn = requireConnection()\n const nats = await importNats()\n const msg = await conn.request(subject, payload, {\n timeout: opts?.timeoutMs ?? 2000,\n headers: toNatsHeaders(nats, opts?.headers),\n })\n return msg\n }\n\n async function requestString(subject: string, payload: string, opts?: NatsRequestOptions): Promise<string> {\n const nats = await importNats()\n const sc = nats.StringCodec()\n const msg = await requestBytes(subject, sc.encode(payload), opts)\n return sc.decode(msg.data)\n }\n\n async function requestJson<TResponse, TRequest = unknown>(\n subject: string,\n payload: TRequest,\n opts?: NatsRequestOptions,\n ): Promise<TResponse> {\n const nats = await importNats()\n const reqCodec = nats.JSONCodec<TRequest>()\n const resCodec = nats.JSONCodec<TResponse>()\n const msg = await requestBytes(subject, reqCodec.encode(payload), opts)\n return resCodec.decode(msg.data)\n }\n\n function subscribeBytes(\n subject: string,\n onMessage: (msg: Msg) => void | Promise<void>,\n opts?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle {\n const conn = requireConnection()\n const sub = conn.subscribe(subject, { queue: opts?.queue })\n if (typeof opts?.max === 'number') sub.unsubscribe(opts.max)\n\n const abortController = new AbortController()\n const signal = opts?.signal ?? abortController.signal\n\n ;(async () => {\n try {\n for await (const msg of sub) {\n if (signal.aborted) break\n await onMessage(msg)\n }\n } catch (e) {\n emitStatus({ status: 'error', data: e })\n } finally {\n try {\n sub.unsubscribe()\n } catch {\n // ignore\n }\n }\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n\n return {\n subscription: sub,\n unsubscribe() {\n try {\n abortController.abort()\n } catch {\n // ignore\n }\n try {\n sub.unsubscribe()\n } catch {\n // ignore\n }\n },\n }\n }\n\n function subscribeString(\n subject: string,\n onMessage: (payload: string, msg: Msg) => void | Promise<void>,\n opts?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle {\n return subscribeBytes(\n subject,\n async (msg) => {\n const nats = await importNats()\n const sc = nats.StringCodec()\n await onMessage(sc.decode(msg.data), msg)\n },\n opts,\n )\n }\n\n function subscribeJson<T>(\n subject: string,\n onMessage: (payload: T, msg: Msg) => void | Promise<void>,\n opts?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle {\n return subscribeBytes(\n subject,\n async (msg) => {\n const nats = await importNats()\n const jc = nats.JSONCodec<T>()\n await onMessage(jc.decode(msg.data), msg)\n },\n opts,\n )\n }\n\n async function subscribeJetStreamOrdered(\n onMessage: (msg: JsMsg) => void | Promise<void>,\n opts: JetStreamOrderedSubscribeOptions,\n ): Promise<JetStreamSubscriptionHandle> {\n const conn = requireConnection()\n if (opts.signal?.aborted) {\n return { unsubscribe() {} }\n }\n\n const nats = await importNats()\n if (opts.signal?.aborted) {\n return { unsubscribe() {} }\n }\n\n const js = conn.jetstream()\n const consumer: Consumer = await js.consumers.get(opts.streamName, {\n filterSubjects: opts.filterSubject,\n deliver_policy: nats.DeliverPolicy.StartSequence,\n opt_start_seq: opts.optStartSeq ?? 0,\n // Milliseconds, NOT nanoseconds: nats.ws runs this through its own\n // `nanos()` before it reaches the server. Pre-converting here multiplied\n // it by 1e6 twice, putting the threshold ~9.5 years out — so the ephemeral\n // consumer never expired. Two consequences, both silent: every reconnect\n // and dialog switch orphaned a consumer server-side, and a client that\n // stopped pulling never got the `consumer deleted` that drives the ordered\n // consumer's self-repair. Undefined stays undefined so nats.ws applies its\n // own default rather than a copy of it.\n inactive_threshold: opts.inactiveThresholdMs,\n })\n\n const iterRef: { current: ConsumerMessages | null } = { current: null }\n let closed = false\n\n const onAbort = () => {\n void teardown()\n }\n opts.signal?.addEventListener('abort', onAbort, { once: true })\n\n async function teardown(): Promise<void> {\n if (closed) return\n closed = true\n opts.signal?.removeEventListener('abort', onAbort)\n const iter = iterRef.current\n iterRef.current = null\n if (iter) {\n try {\n await iter.close()\n } catch {\n // ignore\n }\n }\n }\n\n if (opts.signal?.aborted) {\n void teardown()\n return { unsubscribe() {} }\n }\n\n ;(async () => {\n try {\n const iter = await consumer.consume()\n if (closed) {\n try {\n await iter.close()\n } catch {\n // ignore\n }\n return\n }\n iterRef.current = iter\n watchForRecovery(iter, nats.ConsumerEvents.OrderedConsumerRecreated, opts.onRecovered)\n for await (const msg of iter) {\n if (closed) break\n try {\n await onMessage(msg)\n } catch (e) {\n emitStatus({ status: 'error', data: e })\n }\n }\n } catch (e) {\n if (!closed) emitStatus({ status: 'error', data: e })\n }\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n\n return {\n unsubscribe() {\n void teardown()\n },\n }\n }\n\n function onStatus(listener: (event: NatsStatusEvent) => void): () => void {\n statusListeners.add(listener)\n return () => statusListeners.delete(listener)\n }\n\n return {\n connect,\n close,\n isConnected,\n publishBytes,\n publishString,\n publishJson,\n requestBytes,\n requestString,\n requestJson,\n subscribeBytes,\n subscribeString,\n subscribeJson,\n subscribeJetStreamOrdered,\n onStatus,\n }\n}\n","import { createNatsClient, type NatsClient, type NatsStatus, type NatsStatusEvent } from './nats'\n\nexport const NATS_DEFAULTS = {\n SHARED_CLOSE_DELAY_MS: 3000,\n CONNECT_TIMEOUT_MS: 10_000,\n PING_INTERVAL_MS: 30_000,\n MAX_PING_OUT: 3,\n RETRY_INITIAL_DELAY_MS: 1000,\n RETRY_MAX_DELAY_MS: 30_000,\n RETRY_MULTIPLIER: 2,\n} as const\n\nexport interface NatsReconnectionBackoff {\n /** Number of fast retries before exponential phase kicks in. Default: 0. */\n fastRetries?: number\n /** Delay used during the fast-retry phase. Default: RETRY_INITIAL_DELAY_MS. */\n fastRetryDelayMs?: number\n /** Base delay for the exponential phase. Default: RETRY_INITIAL_DELAY_MS. */\n initialDelayMs?: number\n /** Upper cap on any single retry delay. Default: RETRY_MAX_DELAY_MS. */\n maxDelayMs?: number\n /** Per-attempt multiplier during exponential phase. Default: RETRY_MULTIPLIER. */\n multiplier?: number\n}\n\nexport interface SharedConnection {\n wsUrl: string\n client: NatsClient\n connectPromise: Promise<void> | null\n refCount: number\n closeTimer: ReturnType<typeof setTimeout> | null\n retryTimer: ReturnType<typeof setTimeout> | null\n /**\n * Identity of the consumer driving reconnect. When set, other consumers\n * should observe status only and skip their own scheduleRetry — otherwise\n * each disconnect triggers multiple concurrent connect() calls racing over\n * connectPromise.\n */\n retryOwner: object | null\n}\n\nexport interface AcquireClientOptions {\n name?: string\n user?: string\n pass?: string\n connectTimeoutMs?: number\n pingIntervalMs?: number\n maxPingOut?: number\n}\n\nexport interface ReleaseClientOptions {\n delayMs?: number\n}\n\n// One shared connection PER URL. The previous single-slot implementation\n// force-closed whatever was connected the moment any consumer acquired a\n// DIFFERENT URL — even with live refs on it. With two chat surfaces on\n// distinct endpoints (`/ws/nats` client chat vs `/ws/nats-api` dashboard)\n// mounted at once, each acquire killed the other's socket, and the loser's\n// retry loop self-cancelled (connection identity mismatch) leaving a dead\n// subscription that silently received nothing.\nconst connections = new Map<string, SharedConnection>()\n\n/** Legacy accessor from the single-slot era: returns the first live shared\n * connection, or null. With MULTIPLE URLs connected (e.g. `/ws/nats` client\n * chat + `/ws/nats-api` dashboard mounted together) \"first\" is whichever\n * surface acquired first — an arbitrary, mount-order-dependent answer.\n * Prefer `getSharedConnectionFor(url)`; this stays only for external\n * registry-pinned consumers of the old single-connection API. */\nexport function getSharedConnection(): SharedConnection | null {\n const first = connections.values().next()\n return first.done ? null : first.value\n}\n\nexport function acquireClient(url: string, opts?: AcquireClientOptions): SharedConnection {\n let conn = connections.get(url)\n\n if (!conn) {\n const {\n name = 'openframe-frontend',\n user = 'machine',\n pass = '',\n connectTimeoutMs = NATS_DEFAULTS.CONNECT_TIMEOUT_MS,\n pingIntervalMs = NATS_DEFAULTS.PING_INTERVAL_MS,\n maxPingOut = NATS_DEFAULTS.MAX_PING_OUT,\n } = opts ?? {}\n\n const client = createNatsClient({\n servers: url,\n name,\n user,\n pass,\n connectTimeoutMs,\n reconnect: false,\n pingIntervalMs,\n maxPingOut,\n })\n\n conn = {\n wsUrl: url,\n client,\n connectPromise: null,\n refCount: 0,\n closeTimer: null,\n retryTimer: null,\n retryOwner: null,\n }\n connections.set(url, conn)\n }\n\n conn.refCount += 1\n if (conn.closeTimer) {\n clearTimeout(conn.closeTimer)\n conn.closeTimer = null\n }\n return conn\n}\n\nexport function releaseClient(url: string, opts?: ReleaseClientOptions): void {\n const conn = connections.get(url)\n if (!conn) return\n\n conn.refCount = Math.max(0, conn.refCount - 1)\n if (conn.refCount > 0) return\n\n const delay = opts?.delayMs ?? NATS_DEFAULTS.SHARED_CLOSE_DELAY_MS\n conn.closeTimer = setTimeout(() => {\n conn.closeTimer = null\n // A new acquire may have raced in during the grace period.\n if (conn.refCount > 0) return\n if (connections.get(url) === conn) {\n connections.delete(url)\n }\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n void conn.client.close().catch(() => {})\n }, delay)\n}\n\nexport function getSharedConnectionFor(url: string | null | undefined): SharedConnection | null {\n if (!url) return null\n return connections.get(url) ?? null\n}\n\n// ---------------------------------------------------------------------------\n// Connection lifecycle: retry + status loop, shared by NatsProvider and the\n// chat hooks. Each consumer that wants to drive reconnect creates an\n// ownerToken and calls startConnectionLifecycle(). The first caller to claim\n// retryOwner runs the actual retry loop; subsequent claimants observe status\n// only. When the owner unmounts and releases retryOwner, the next status\n// event lets a surviving consumer pick up ownership opportunistically.\n// ---------------------------------------------------------------------------\n\nexport interface ConnectionLifecycleOptions {\n conn: SharedConnection\n wsUrl: string\n onBeforeReconnect?: () => Promise<void> | void\n backoff?: NatsReconnectionBackoff\n getFreshUrl: () => string | null\n /** Called on every status change (after closed-guard). */\n onStatusChange?: (status: NatsStatus, evt: NatsStatusEvent) => void\n /**\n * Decide which statuses should trigger a retry attempt. Defaults to closed +\n * disconnected. Override to skip 'error' (JetStream protocol errors that\n * don't close the WS) or include it.\n */\n shouldRetryOn?: (status: NatsStatus) => boolean\n}\n\nexport interface ConnectionLifecycleHandle {\n /** Stop observing status, clear any pending retry, release ownership if held. */\n stop(): void\n}\n\nconst defaultShouldRetryOn = (status: NatsStatus) => status === 'closed' || status === 'disconnected'\n\nexport function startConnectionLifecycle(options: ConnectionLifecycleOptions): ConnectionLifecycleHandle {\n const { conn, wsUrl } = options\n // Each lifecycle gets its own identity for retry ownership. The first lifecycle to claim\n // an unowned connection drives reconnect; later attachers observe status only until the\n // owner releases (see opportunistic claim in scheduleRetry).\n const ownerToken = {}\n if (!conn.retryOwner) conn.retryOwner = ownerToken\n\n let closed = false\n let retryAttempt = 0\n\n function emitSynthetic(status: NatsStatus) {\n if (closed) return\n options.onStatusChange?.(status, { status })\n if (status === 'connected') {\n retryAttempt = 0\n }\n }\n\n function scheduleRetry() {\n if (closed) return\n if (getSharedConnectionFor(wsUrl) !== conn) return\n if (!conn.retryOwner) conn.retryOwner = ownerToken\n if (conn.retryOwner !== ownerToken) return\n\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n\n const cfg = options.backoff ?? {}\n const fastRetries = cfg.fastRetries ?? 0\n const fastDelay = cfg.fastRetryDelayMs ?? NATS_DEFAULTS.RETRY_INITIAL_DELAY_MS\n const baseDelay = cfg.initialDelayMs ?? NATS_DEFAULTS.RETRY_INITIAL_DELAY_MS\n const maxDelay = cfg.maxDelayMs ?? NATS_DEFAULTS.RETRY_MAX_DELAY_MS\n const multiplier = cfg.multiplier ?? NATS_DEFAULTS.RETRY_MULTIPLIER\n\n const delay =\n retryAttempt < fastRetries\n ? fastDelay\n : Math.min(baseDelay * multiplier ** (retryAttempt - fastRetries), maxDelay)\n const jitteredDelay = delay * (0.5 + Math.random() * 0.5)\n retryAttempt++\n\n conn.retryTimer = setTimeout(async () => {\n conn.retryTimer = null\n if (closed) return\n if (getSharedConnectionFor(wsUrl) !== conn) return\n\n try {\n await options.onBeforeReconnect?.()\n } catch {\n // continue regardless of token-refresh outcome\n }\n if (closed) return\n if (getSharedConnectionFor(wsUrl) !== conn) return\n\n const freshUrl = options.getFreshUrl()\n if (freshUrl !== wsUrl) return\n\n try {\n conn.connectPromise = null\n conn.connectPromise = conn.client.connect()\n await conn.connectPromise\n if (!closed && getSharedConnectionFor(wsUrl) === conn) {\n retryAttempt = 0\n }\n } catch {\n conn.connectPromise = null\n if (!closed && getSharedConnectionFor(wsUrl) === conn) {\n scheduleRetry()\n }\n }\n }, jitteredDelay)\n }\n\n const shouldRetryOn = options.shouldRetryOn ?? defaultShouldRetryOn\n\n const unsubStatus = conn.client.onStatus((evt) => {\n if (closed) return\n options.onStatusChange?.(evt.status, evt)\n if (evt.status === 'connected') {\n retryAttempt = 0\n }\n if (shouldRetryOn(evt.status)) {\n scheduleRetry()\n }\n })\n\n if (conn.client.isConnected()) {\n emitSynthetic('connected')\n }\n\n void (async () => {\n try {\n conn.connectPromise ||= conn.client.connect()\n await conn.connectPromise\n } catch {\n conn.connectPromise = null\n if (closed) return\n\n emitSynthetic('disconnected')\n scheduleRetry()\n }\n })()\n\n return {\n stop() {\n closed = true\n unsubStatus()\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n if (conn.retryOwner === ownerToken) {\n conn.retryOwner = null\n }\n },\n }\n}\n","'use client'\n\nimport * as React from 'react'\nimport type { NatsClient, NatsStatus } from './nats'\nimport {\n acquireClient,\n releaseClient,\n startConnectionLifecycle,\n type AcquireClientOptions,\n type NatsReconnectionBackoff,\n} from './shared-connection'\n\nexport type { NatsReconnectionBackoff } from './shared-connection'\n\nexport interface NatsProviderProps {\n children: React.ReactNode\n /** Return the current NATS WebSocket URL (or null when not yet available, e.g. unauthenticated). */\n getWsUrl: () => string | null\n /** Called before each reconnect attempt. */\n onBeforeReconnect?: () => Promise<void> | void\n clientConfig?: AcquireClientOptions\n reconnectionBackoff?: NatsReconnectionBackoff\n /**\n * Bump this to force re-evaluating `getWsUrl()` (e.g. when auth state flips).\n * Provider does not subscribe to external auth state by itself.\n */\n urlRevision?: unknown\n}\n\nexport interface NatsContextValue {\n client: NatsClient | null\n status: NatsStatus\n isReady: boolean\n reconnectionCount: number\n}\n\nconst NatsContext = React.createContext<NatsContextValue | null>(null)\n\nexport function NatsProvider({\n children,\n getWsUrl,\n onBeforeReconnect,\n clientConfig,\n reconnectionBackoff,\n urlRevision,\n}: NatsProviderProps) {\n const [client, setClient] = React.useState<NatsClient | null>(null)\n const [status, setStatus] = React.useState<NatsStatus>('closed')\n const [reconnectionCount, setReconnectionCount] = React.useState(0)\n\n const getWsUrlRef = React.useRef(getWsUrl)\n React.useEffect(() => {\n getWsUrlRef.current = getWsUrl\n }, [getWsUrl])\n\n const onBeforeReconnectRef = React.useRef(onBeforeReconnect)\n React.useEffect(() => {\n onBeforeReconnectRef.current = onBeforeReconnect\n }, [onBeforeReconnect])\n\n const reconnectionBackoffRef = React.useRef(reconnectionBackoff)\n React.useEffect(() => {\n reconnectionBackoffRef.current = reconnectionBackoff\n }, [reconnectionBackoff])\n\n const clientConfigRef = React.useRef(clientConfig)\n React.useEffect(() => {\n clientConfigRef.current = clientConfig\n }, [clientConfig])\n\n const heldUrlRef = React.useRef<string | null>(null)\n const hadConnectionBeforeRef = React.useRef(false)\n\n React.useEffect(() => {\n const wsUrl = getWsUrlRef.current()\n\n if (!wsUrl) {\n if (heldUrlRef.current) {\n releaseClient(heldUrlRef.current)\n heldUrlRef.current = null\n setClient(null)\n setStatus('closed')\n }\n return\n }\n\n if (heldUrlRef.current && heldUrlRef.current !== wsUrl) {\n releaseClient(heldUrlRef.current)\n heldUrlRef.current = null\n }\n\n const conn = acquireClient(wsUrl, clientConfigRef.current)\n heldUrlRef.current = wsUrl\n setClient(conn.client)\n setStatus(conn.client.isConnected() ? 'connected' : 'connecting')\n\n const lifecycle = startConnectionLifecycle({\n conn,\n wsUrl,\n onBeforeReconnect: () => onBeforeReconnectRef.current?.(),\n backoff: reconnectionBackoffRef.current,\n getFreshUrl: () => getWsUrlRef.current(),\n onStatusChange: (newStatus) => {\n if (newStatus === 'error') return\n setStatus(newStatus)\n if (newStatus === 'connected') {\n if (hadConnectionBeforeRef.current) {\n setReconnectionCount((c) => c + 1)\n }\n hadConnectionBeforeRef.current = true\n }\n },\n })\n\n return () => {\n lifecycle.stop()\n if (heldUrlRef.current) {\n releaseClient(heldUrlRef.current)\n heldUrlRef.current = null\n }\n setClient(null)\n setStatus('closed')\n }\n }, [urlRevision])\n\n const value = React.useMemo<NatsContextValue>(\n () => ({\n client,\n status,\n isReady: status === 'connected' && client !== null,\n reconnectionCount,\n }),\n [client, status, reconnectionCount],\n )\n\n return <NatsContext.Provider value={value}>{children}</NatsContext.Provider>\n}\n\nexport function useNats(): NatsContextValue {\n const ctx = React.useContext(NatsContext)\n if (!ctx) throw new Error('useNats must be used inside <NatsProvider>')\n return ctx\n}\n\nexport function useOptionalNats(): NatsContextValue | null {\n return React.useContext(NatsContext)\n}\n","'use client'\n\nimport * as React from 'react'\nimport type { JsMsg, Msg, NatsSubscribeOptions } from './nats'\nimport { useOptionalNats } from './nats-provider'\n\nexport interface UseNatsSubscriptionOptions extends NatsSubscribeOptions {\n enabled?: boolean\n}\n\nexport interface UseNatsSubscriptionReturn {\n isSubscribed: boolean\n isReady: boolean\n}\n\n/**\n * Subscribe to a NATS subject using the shared connection from <NatsProvider>.\n * Automatically (re)subscribes when the connection becomes ready, when the\n * subject changes, and after reconnections.\n */\nexport function useNatsSubscription(\n subject: string | null,\n onMessage: (msg: Msg) => void | Promise<void>,\n options?: UseNatsSubscriptionOptions,\n): UseNatsSubscriptionReturn {\n const nats = useOptionalNats()\n const handlerRef = React.useRef(onMessage)\n React.useEffect(() => {\n handlerRef.current = onMessage\n }, [onMessage])\n\n const [isSubscribed, setIsSubscribed] = React.useState(false)\n\n const enabled = options?.enabled !== false\n const queue = options?.queue\n const max = options?.max\n const reconnectionCount = nats?.reconnectionCount ?? 0\n const isReady = !!nats?.isReady\n const client = nats?.client ?? null\n\n React.useEffect(() => {\n if (!client || !isReady || !subject || !enabled) {\n setIsSubscribed(false)\n return\n }\n\n const sub = client.subscribeBytes(\n subject,\n (msg) => handlerRef.current(msg),\n { queue, max },\n )\n setIsSubscribed(true)\n\n return () => {\n setIsSubscribed(false)\n try {\n sub.unsubscribe()\n } catch {\n // ignore\n }\n }\n // reconnectionCount intentionally in deps: re-subscribe after reconnect\n }, [client, isReady, subject, enabled, queue, max, reconnectionCount])\n\n return { isSubscribed, isReady }\n}\n\nexport type UseNatsJsonSubscriptionOptions = UseNatsSubscriptionOptions\n\nexport function useNatsJsonSubscription<T = unknown>(\n subject: string | null,\n onPayload: (payload: T, msg: Msg) => void | Promise<void>,\n options?: UseNatsJsonSubscriptionOptions,\n): UseNatsSubscriptionReturn {\n const handlerRef = React.useRef(onPayload)\n React.useEffect(() => {\n handlerRef.current = onPayload\n }, [onPayload])\n\n const decoderRef = React.useRef<TextDecoder | null>(null)\n if (!decoderRef.current && typeof TextDecoder !== 'undefined') {\n decoderRef.current = new TextDecoder()\n }\n\n const wrapped = React.useCallback(async (msg: Msg) => {\n const decoder = decoderRef.current\n if (!decoder) return\n try {\n const parsed = JSON.parse(decoder.decode(msg.data)) as T\n await handlerRef.current(parsed, msg)\n } catch {\n // ignore malformed payloads\n }\n }, [])\n\n return useNatsSubscription(subject, wrapped, options)\n}\n\nexport type NatsJsMsg = JsMsg\n"],"mappings":"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","names":["React"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-KQKU4VXB.cjs","../src/nats/nats.ts","../src/nats/shared-connection.ts","../src/nats/nats-provider.tsx","../src/nats/use-nats-subscription.ts"],"names":[],"mappings":"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","file":"/home/runner/work/openframe-oss-lib/openframe-oss-lib/openframe-frontend-core/dist/chunk-KQKU4VXB.cjs","sourcesContent":[null,"import type {\n ConnectionOptions,\n Consumer,\n ConsumerEvents,\n ConsumerMessages,\n JsMsg,\n MsgHdrs as NatsHeaders,\n Msg,\n NatsConnection,\n Subscription,\n} from 'nats.ws'\n\nexport type { JsMsg, Msg, Subscription } from 'nats.ws'\n\nexport type JetStreamDeliverPolicy = 'new' | 'byStartSequence'\n\nexport interface JetStreamOrderedSubscribeOptions {\n streamName: string\n filterSubject: string\n deliverPolicy: JetStreamDeliverPolicy\n /** Required when deliverPolicy === 'byStartSequence'. */\n optStartSeq?: number\n /**\n * Auto-cleanup the ephemeral consumer after this idle time. Defaults to\n * nats.ws's own. Applies to the consumer created first: nats.ws only reads it\n * when the start sequence is the one it was built with, so every consumer it\n * recreates afterwards reverts to the library default.\n */\n inactiveThresholdMs?: number\n /** AbortSignal to tear down the consumer. */\n signal?: AbortSignal\n /**\n * The ordered consumer had to be rebuilt: the server dropped the ephemeral\n * consumer (its inactivity threshold elapsed while the page was suspended or\n * offline), heartbeats were missed, or a sequence gap was detected. nats.ws\n * recreates it from the last delivered sequence, so this is NOT an error —\n * but everything that aged out of the stream while it was gone is\n * unrecoverable from the tail, and no connection event announces it (the\n * WebSocket never dropped). Callers that keep persisted history alongside\n * the live tail must refetch it here, or they keep showing a snapshot from\n * before the gap.\n */\n onRecovered?: () => void\n}\n\nexport interface JetStreamSubscriptionHandle {\n unsubscribe(): void\n}\n\nexport interface NatsClientOptions {\n /**\n * NATS server URL(s), for example:\n * - \"wss://nats.example.com:443\"\n * - [\"wss://nats-1.example.com:443\", \"wss://nats-2.example.com:443\"]\n */\n servers: string | string[]\n\n /**\n * Connection name (shows up in NATS monitoring).\n */\n name?: string\n\n /**\n * Auth options (pick one: token or user/pass).\n */\n token?: string\n user?: string\n pass?: string\n\n /**\n * Reconnect behavior.\n */\n reconnect?: boolean\n maxReconnectAttempts?: number\n reconnectTimeWaitMs?: number\n\n /**\n * Exponential backoff for reconnect delays.\n * When set, uses `reconnectDelayHandler` from nats.ws under the hood,\n * overriding `reconnectTimeWaitMs`.\n */\n exponentialBackoff?: {\n /** Initial delay in ms (default: 1000) */\n initialDelayMs?: number\n /** Maximum delay cap in ms (default: 30000) */\n maxDelayMs?: number\n /** Multiplier per attempt (default: 2) */\n multiplier?: number\n /** Add random jitter 0-50% of delay to prevent thundering herd (default: true) */\n jitter?: boolean\n }\n\n /**\n * Ping behavior (keep-alive).\n */\n pingIntervalMs?: number\n maxPingOut?: number\n\n /**\n * Optional inbox prefix (useful if you want to isolate request/reply inboxes).\n */\n inboxPrefix?: string\n\n /**\n * Connection timeout in milliseconds (maps to `nats.ws` connect option `timeout`).\n * If you see `NatsError: TIMEOUT` during connect, increase this.\n */\n connectTimeoutMs?: number\n}\n\nexport interface NatsSubscribeOptions {\n /**\n * Queue group for load-balancing messages across subscribers.\n */\n queue?: string\n\n /**\n * Auto-unsubscribe after receiving this many messages.\n */\n max?: number\n\n /**\n * Abort signal to stop message iteration and unsubscribe.\n */\n signal?: AbortSignal\n}\n\nexport type NatsHeadersInit = Record<string, string> | NatsHeaders | undefined\n\nexport interface NatsPublishOptions {\n headers?: NatsHeadersInit\n}\n\nexport interface NatsRequestOptions {\n timeoutMs?: number\n headers?: NatsHeadersInit\n}\n\nexport interface NatsSubscriptionHandle {\n readonly subscription: Subscription\n unsubscribe(): void\n}\n\nexport type NatsStatus =\n | 'connecting'\n | 'connected'\n | 'disconnected'\n | 'reconnecting'\n | 'closed'\n | 'error'\n\nexport interface NatsStatusEvent {\n status: NatsStatus\n data?: unknown\n}\n\nexport interface NatsClient {\n connect(): Promise<void>\n close(): Promise<void>\n\n isConnected(): boolean\n\n publishBytes(subject: string, payload: Uint8Array, options?: NatsPublishOptions): void\n publishString(subject: string, payload: string, options?: NatsPublishOptions): void\n publishJson<T>(subject: string, payload: T, options?: NatsPublishOptions): void\n\n requestBytes(subject: string, payload: Uint8Array, options?: NatsRequestOptions): Promise<Msg>\n requestString(subject: string, payload: string, options?: NatsRequestOptions): Promise<string>\n requestJson<TResponse, TRequest = unknown>(\n subject: string,\n payload: TRequest,\n options?: NatsRequestOptions,\n ): Promise<TResponse>\n\n subscribeBytes(\n subject: string,\n onMessage: (msg: Msg) => void | Promise<void>,\n options?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle\n subscribeString(\n subject: string,\n onMessage: (payload: string, msg: Msg) => void | Promise<void>,\n options?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle\n subscribeJson<T>(\n subject: string,\n onMessage: (payload: T, msg: Msg) => void | Promise<void>,\n options?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle\n\n /**\n * Subscribe to a JetStream subject via an ephemeral OrderedConsumer (no acks).\n * Use `optStartSeq` with `deliverPolicy: 'byStartSequence'` to resume from a known offset,\n * or `deliverPolicy: 'new'` to live-tail.\n */\n subscribeJetStreamOrdered(\n onMessage: (msg: JsMsg) => void | Promise<void>,\n options: JetStreamOrderedSubscribeOptions,\n ): Promise<JetStreamSubscriptionHandle>\n\n onStatus(listener: (event: NatsStatusEvent) => void): () => void\n}\n\nfunction assertClientSide(): void {\n // This wrapper is meant for browser/Tauri usage via WebSockets.\n // Keep it safe to import from Next.js server bundles by throwing only when used.\n if (typeof window === 'undefined') {\n throw new Error('NATS client can only connect from the browser/runtime with WebSocket support (window is undefined).')\n }\n}\n\nasync function importNats(): Promise<typeof import('nats.ws')> {\n // Browser/Tauri only: always use the websocket client (no Node-only deps).\n return await import('nats.ws')\n}\n\nfunction toNatsHeaders(nats: typeof import('nats.ws'), init: NatsHeadersInit): NatsHeaders | undefined {\n if (!init) return undefined\n if (typeof (init as NatsHeaders).get === 'function') return init as NatsHeaders\n\n const h = nats.headers()\n for (const [k, v] of Object.entries(init as Record<string, string>)) {\n if (v !== undefined && v !== null) h.set(k, String(v))\n }\n return h\n}\n\n/** Returns a cryptographically random float in [0, 1). */\nfunction cryptoRandom(): number {\n const buf = new Uint32Array(1)\n crypto.getRandomValues(buf)\n return buf[0] / (0xffffffff + 1)\n}\n\ninterface ExponentialBackoffHandle {\n handler: () => number\n reset: () => void\n}\n\nfunction createExponentialBackoffHandler(opts: NonNullable<NatsClientOptions['exponentialBackoff']>): ExponentialBackoffHandle {\n const initialDelay = opts.initialDelayMs ?? 1000\n const maxDelay = opts.maxDelayMs ?? 30_000\n const multiplier = opts.multiplier ?? 2\n const jitter = opts.jitter ?? true\n\n let attempt = 0\n\n return {\n handler: (): number => {\n const delay = Math.min(initialDelay * multiplier ** attempt, maxDelay)\n attempt++\n return jitter ? delay * (0.5 + cryptoRandom() * 0.5) : delay\n },\n reset: () => {\n attempt = 0\n },\n }\n}\n\nfunction mapOptionsToConnectionOptions(\n opts: NatsClientOptions,\n backoff?: ExponentialBackoffHandle,\n): ConnectionOptions {\n return {\n servers: opts.servers,\n name: opts.name,\n token: opts.token,\n user: opts.user,\n pass: opts.pass,\n timeout: opts.connectTimeoutMs ?? 15000,\n reconnect: opts.reconnect ?? true,\n maxReconnectAttempts: opts.maxReconnectAttempts,\n reconnectTimeWait: opts.reconnectTimeWaitMs,\n reconnectDelayHandler: backoff?.handler,\n pingInterval: opts.pingIntervalMs,\n maxPingOut: opts.maxPingOut,\n inboxPrefix: opts.inboxPrefix,\n }\n}\n\nfunction mapNatsTypeToStatus(type: unknown): NatsStatus | null {\n const t = String(type).toLowerCase()\n if (t.includes('disconnect')) return 'disconnected'\n if (t === 'reconnecting') return 'reconnecting'\n if (t === 'reconnect' || t.includes('connect')) return 'connected'\n if (t.includes('error')) return 'error'\n if (t.includes('close')) return 'closed'\n return null\n}\n\n/**\n * Report an ordered consumer rebuilding itself.\n *\n * nats.ws announces this on the message iterator's status channel and nowhere\n * else — the iterator keeps yielding, the WebSocket never drops, and nothing\n * consumes that channel unless someone asks for it. That silence is what lets a\n * tail resume after a suspended page having permanently missed whatever expired\n * from the stream meanwhile: the recreation resumes from the last delivered\n * sequence, so the gap is invisible from the tail alone.\n *\n * `recreatedEvent` is nats.ws's own enum value rather than a literal of ours: a\n * copy that drifted from the library would kill this signal silently, which is\n * the failure mode the signal exists to prevent.\n *\n * The status channel is closed along with the iterator, which ends this loop.\n */\nfunction watchForRecovery(\n iter: ConsumerMessages,\n recreatedEvent: ConsumerEvents.OrderedConsumerRecreated,\n onRecovered?: () => void,\n): void {\n if (!onRecovered) return\n void (async () => {\n try {\n const status = await iter.status()\n for await (const event of status) {\n if (event.type !== recreatedEvent) continue\n try {\n onRecovered()\n } catch (e) {\n // A caller that throws must not take the watch down with it — this\n // loop is the only thing reporting recoveries for this subscription.\n console.warn('[nats] onRecovered threw:', e)\n }\n }\n } catch {\n // Status ends with the subscription — nothing left to report.\n }\n })()\n}\n\nexport function createNatsClient(options: NatsClientOptions): NatsClient {\n let nc: NatsConnection | null = null\n let statusLoopAbort: AbortController | null = null\n let connectInFlight: Promise<void> | null = null\n\n const backoff = options.exponentialBackoff\n ? createExponentialBackoffHandler(options.exponentialBackoff)\n : undefined\n\n const statusListeners = new Set<(event: NatsStatusEvent) => void>()\n\n function emitStatus(event: NatsStatusEvent) {\n for (const listener of statusListeners) {\n try {\n listener(event)\n } catch {\n // ignore listener failures\n }\n }\n }\n\n async function connect(): Promise<void> {\n if (nc && !nc.isClosed()) return\n if (connectInFlight) return connectInFlight\n assertClientSide()\n\n connectInFlight = (async () => {\n try {\n emitStatus({ status: 'connecting' })\n\n const nats = await importNats()\n const conn = await nats.connect(mapOptionsToConnectionOptions(options, backoff))\n nc = conn\n\n emitStatus({ status: 'connected' })\n\n statusLoopAbort = new AbortController()\n const signal = statusLoopAbort.signal\n\n ;(async () => {\n try {\n for await (const s of conn.status()) {\n if (signal.aborted) return\n const mapped = mapNatsTypeToStatus((s as any)?.type)\n if (mapped) {\n if (mapped === 'connected' && backoff) {\n backoff.reset()\n }\n emitStatus({ status: mapped, data: (s as any)?.data })\n if (mapped === 'closed') {\n nc = null\n }\n }\n }\n } catch (e) {\n if (!signal.aborted) {\n emitStatus({ status: 'error', data: e })\n \n if (nc === conn && conn.isClosed()) {\n nc = null\n emitStatus({ status: 'closed' })\n }\n }\n }\n })().catch(() => {\n // ignore\n })\n } finally {\n connectInFlight = null\n }\n })()\n\n return connectInFlight\n }\n\n async function close(): Promise<void> {\n const conn = nc\n nc = null\n\n if (statusLoopAbort) {\n try {\n statusLoopAbort.abort()\n } catch {\n // ignore\n }\n statusLoopAbort = null\n }\n\n if (!conn) return\n try {\n await conn.drain()\n } finally {\n try {\n await conn.close()\n } finally {\n emitStatus({ status: 'closed' })\n }\n }\n }\n\n function requireConnection(): NatsConnection {\n if (!nc) throw new Error('NATS is not connected. Call client.connect() first.')\n return nc\n }\n\n function isConnected(): boolean {\n return Boolean(nc) && !nc!.isClosed()\n }\n\n function publishBytes(subject: string, payload: Uint8Array, opts?: NatsPublishOptions): void {\n const conn = requireConnection()\n ;(async () => {\n const nats = await importNats()\n conn.publish(subject, payload, { headers: toNatsHeaders(nats, opts?.headers) })\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n }\n\n function publishString(subject: string, payload: string, opts?: NatsPublishOptions): void {\n ;(async () => {\n const nats = await importNats()\n const sc = nats.StringCodec()\n publishBytes(subject, sc.encode(payload), opts)\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n }\n\n function publishJson<T>(subject: string, payload: T, opts?: NatsPublishOptions): void {\n ;(async () => {\n const nats = await importNats()\n const jc = nats.JSONCodec<T>()\n publishBytes(subject, jc.encode(payload), opts)\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n }\n\n async function requestBytes(subject: string, payload: Uint8Array, opts?: NatsRequestOptions): Promise<Msg> {\n const conn = requireConnection()\n const nats = await importNats()\n const msg = await conn.request(subject, payload, {\n timeout: opts?.timeoutMs ?? 2000,\n headers: toNatsHeaders(nats, opts?.headers),\n })\n return msg\n }\n\n async function requestString(subject: string, payload: string, opts?: NatsRequestOptions): Promise<string> {\n const nats = await importNats()\n const sc = nats.StringCodec()\n const msg = await requestBytes(subject, sc.encode(payload), opts)\n return sc.decode(msg.data)\n }\n\n async function requestJson<TResponse, TRequest = unknown>(\n subject: string,\n payload: TRequest,\n opts?: NatsRequestOptions,\n ): Promise<TResponse> {\n const nats = await importNats()\n const reqCodec = nats.JSONCodec<TRequest>()\n const resCodec = nats.JSONCodec<TResponse>()\n const msg = await requestBytes(subject, reqCodec.encode(payload), opts)\n return resCodec.decode(msg.data)\n }\n\n function subscribeBytes(\n subject: string,\n onMessage: (msg: Msg) => void | Promise<void>,\n opts?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle {\n const conn = requireConnection()\n const sub = conn.subscribe(subject, { queue: opts?.queue })\n if (typeof opts?.max === 'number') sub.unsubscribe(opts.max)\n\n const abortController = new AbortController()\n const signal = opts?.signal ?? abortController.signal\n\n ;(async () => {\n try {\n for await (const msg of sub) {\n if (signal.aborted) break\n await onMessage(msg)\n }\n } catch (e) {\n emitStatus({ status: 'error', data: e })\n } finally {\n try {\n sub.unsubscribe()\n } catch {\n // ignore\n }\n }\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n\n return {\n subscription: sub,\n unsubscribe() {\n try {\n abortController.abort()\n } catch {\n // ignore\n }\n try {\n sub.unsubscribe()\n } catch {\n // ignore\n }\n },\n }\n }\n\n function subscribeString(\n subject: string,\n onMessage: (payload: string, msg: Msg) => void | Promise<void>,\n opts?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle {\n return subscribeBytes(\n subject,\n async (msg) => {\n const nats = await importNats()\n const sc = nats.StringCodec()\n await onMessage(sc.decode(msg.data), msg)\n },\n opts,\n )\n }\n\n function subscribeJson<T>(\n subject: string,\n onMessage: (payload: T, msg: Msg) => void | Promise<void>,\n opts?: NatsSubscribeOptions,\n ): NatsSubscriptionHandle {\n return subscribeBytes(\n subject,\n async (msg) => {\n const nats = await importNats()\n const jc = nats.JSONCodec<T>()\n await onMessage(jc.decode(msg.data), msg)\n },\n opts,\n )\n }\n\n async function subscribeJetStreamOrdered(\n onMessage: (msg: JsMsg) => void | Promise<void>,\n opts: JetStreamOrderedSubscribeOptions,\n ): Promise<JetStreamSubscriptionHandle> {\n const conn = requireConnection()\n if (opts.signal?.aborted) {\n return { unsubscribe() {} }\n }\n\n const nats = await importNats()\n if (opts.signal?.aborted) {\n return { unsubscribe() {} }\n }\n\n const js = conn.jetstream()\n const consumer: Consumer = await js.consumers.get(opts.streamName, {\n filterSubjects: opts.filterSubject,\n deliver_policy: nats.DeliverPolicy.StartSequence,\n opt_start_seq: opts.optStartSeq ?? 0,\n // Milliseconds, NOT nanoseconds: nats.ws runs this through its own\n // `nanos()` before it reaches the server. Pre-converting here multiplied\n // it by 1e6 twice, putting the threshold ~9.5 years out — so the ephemeral\n // consumer never expired. Two consequences, both silent: every reconnect\n // and dialog switch orphaned a consumer server-side, and a client that\n // stopped pulling never got the `consumer deleted` that drives the ordered\n // consumer's self-repair. Undefined stays undefined so nats.ws applies its\n // own default rather than a copy of it.\n inactive_threshold: opts.inactiveThresholdMs,\n })\n\n const iterRef: { current: ConsumerMessages | null } = { current: null }\n let closed = false\n\n const onAbort = () => {\n void teardown()\n }\n opts.signal?.addEventListener('abort', onAbort, { once: true })\n\n async function teardown(): Promise<void> {\n if (closed) return\n closed = true\n opts.signal?.removeEventListener('abort', onAbort)\n const iter = iterRef.current\n iterRef.current = null\n if (iter) {\n try {\n await iter.close()\n } catch {\n // ignore\n }\n }\n }\n\n if (opts.signal?.aborted) {\n void teardown()\n return { unsubscribe() {} }\n }\n\n ;(async () => {\n try {\n const iter = await consumer.consume()\n if (closed) {\n try {\n await iter.close()\n } catch {\n // ignore\n }\n return\n }\n iterRef.current = iter\n watchForRecovery(iter, nats.ConsumerEvents.OrderedConsumerRecreated, opts.onRecovered)\n for await (const msg of iter) {\n if (closed) break\n try {\n await onMessage(msg)\n } catch (e) {\n emitStatus({ status: 'error', data: e })\n }\n }\n } catch (e) {\n if (!closed) emitStatus({ status: 'error', data: e })\n }\n })().catch((e) => emitStatus({ status: 'error', data: e }))\n\n return {\n unsubscribe() {\n void teardown()\n },\n }\n }\n\n function onStatus(listener: (event: NatsStatusEvent) => void): () => void {\n statusListeners.add(listener)\n return () => statusListeners.delete(listener)\n }\n\n return {\n connect,\n close,\n isConnected,\n publishBytes,\n publishString,\n publishJson,\n requestBytes,\n requestString,\n requestJson,\n subscribeBytes,\n subscribeString,\n subscribeJson,\n subscribeJetStreamOrdered,\n onStatus,\n }\n}\n","import { createNatsClient, type NatsClient, type NatsStatus, type NatsStatusEvent } from './nats'\n\nexport const NATS_DEFAULTS = {\n SHARED_CLOSE_DELAY_MS: 3000,\n CONNECT_TIMEOUT_MS: 10_000,\n PING_INTERVAL_MS: 30_000,\n MAX_PING_OUT: 3,\n RETRY_INITIAL_DELAY_MS: 1000,\n RETRY_MAX_DELAY_MS: 30_000,\n RETRY_MULTIPLIER: 2,\n} as const\n\nexport interface NatsReconnectionBackoff {\n /** Number of fast retries before exponential phase kicks in. Default: 0. */\n fastRetries?: number\n /** Delay used during the fast-retry phase. Default: RETRY_INITIAL_DELAY_MS. */\n fastRetryDelayMs?: number\n /** Base delay for the exponential phase. Default: RETRY_INITIAL_DELAY_MS. */\n initialDelayMs?: number\n /** Upper cap on any single retry delay. Default: RETRY_MAX_DELAY_MS. */\n maxDelayMs?: number\n /** Per-attempt multiplier during exponential phase. Default: RETRY_MULTIPLIER. */\n multiplier?: number\n}\n\nexport interface SharedConnection {\n wsUrl: string\n client: NatsClient\n connectPromise: Promise<void> | null\n refCount: number\n closeTimer: ReturnType<typeof setTimeout> | null\n retryTimer: ReturnType<typeof setTimeout> | null\n /**\n * Identity of the consumer driving reconnect. When set, other consumers\n * should observe status only and skip their own scheduleRetry — otherwise\n * each disconnect triggers multiple concurrent connect() calls racing over\n * connectPromise.\n */\n retryOwner: object | null\n}\n\nexport interface AcquireClientOptions {\n name?: string\n user?: string\n pass?: string\n connectTimeoutMs?: number\n pingIntervalMs?: number\n maxPingOut?: number\n}\n\nexport interface ReleaseClientOptions {\n delayMs?: number\n}\n\n// One shared connection PER URL. The previous single-slot implementation\n// force-closed whatever was connected the moment any consumer acquired a\n// DIFFERENT URL — even with live refs on it. With two chat surfaces on\n// distinct endpoints (`/ws/nats` client chat vs `/ws/nats-api` dashboard)\n// mounted at once, each acquire killed the other's socket, and the loser's\n// retry loop self-cancelled (connection identity mismatch) leaving a dead\n// subscription that silently received nothing.\nconst connections = new Map<string, SharedConnection>()\n\n/** Legacy accessor from the single-slot era: returns the first live shared\n * connection, or null. With MULTIPLE URLs connected (e.g. `/ws/nats` client\n * chat + `/ws/nats-api` dashboard mounted together) \"first\" is whichever\n * surface acquired first — an arbitrary, mount-order-dependent answer.\n * Prefer `getSharedConnectionFor(url)`; this stays only for external\n * registry-pinned consumers of the old single-connection API. */\nexport function getSharedConnection(): SharedConnection | null {\n const first = connections.values().next()\n return first.done ? null : first.value\n}\n\nexport function acquireClient(url: string, opts?: AcquireClientOptions): SharedConnection {\n let conn = connections.get(url)\n\n if (!conn) {\n const {\n name = 'openframe-frontend',\n user = 'machine',\n pass = '',\n connectTimeoutMs = NATS_DEFAULTS.CONNECT_TIMEOUT_MS,\n pingIntervalMs = NATS_DEFAULTS.PING_INTERVAL_MS,\n maxPingOut = NATS_DEFAULTS.MAX_PING_OUT,\n } = opts ?? {}\n\n const client = createNatsClient({\n servers: url,\n name,\n user,\n pass,\n connectTimeoutMs,\n reconnect: false,\n pingIntervalMs,\n maxPingOut,\n })\n\n conn = {\n wsUrl: url,\n client,\n connectPromise: null,\n refCount: 0,\n closeTimer: null,\n retryTimer: null,\n retryOwner: null,\n }\n connections.set(url, conn)\n }\n\n conn.refCount += 1\n if (conn.closeTimer) {\n clearTimeout(conn.closeTimer)\n conn.closeTimer = null\n }\n return conn\n}\n\nexport function releaseClient(url: string, opts?: ReleaseClientOptions): void {\n const conn = connections.get(url)\n if (!conn) return\n\n conn.refCount = Math.max(0, conn.refCount - 1)\n if (conn.refCount > 0) return\n\n const delay = opts?.delayMs ?? NATS_DEFAULTS.SHARED_CLOSE_DELAY_MS\n conn.closeTimer = setTimeout(() => {\n conn.closeTimer = null\n // A new acquire may have raced in during the grace period.\n if (conn.refCount > 0) return\n if (connections.get(url) === conn) {\n connections.delete(url)\n }\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n void conn.client.close().catch(() => {})\n }, delay)\n}\n\nexport function getSharedConnectionFor(url: string | null | undefined): SharedConnection | null {\n if (!url) return null\n return connections.get(url) ?? null\n}\n\n// ---------------------------------------------------------------------------\n// Connection lifecycle: retry + status loop, shared by NatsProvider and the\n// chat hooks. Each consumer that wants to drive reconnect creates an\n// ownerToken and calls startConnectionLifecycle(). The first caller to claim\n// retryOwner runs the actual retry loop; subsequent claimants observe status\n// only. When the owner unmounts and releases retryOwner, the next status\n// event lets a surviving consumer pick up ownership opportunistically.\n// ---------------------------------------------------------------------------\n\nexport interface ConnectionLifecycleOptions {\n conn: SharedConnection\n wsUrl: string\n onBeforeReconnect?: () => Promise<void> | void\n backoff?: NatsReconnectionBackoff\n getFreshUrl: () => string | null\n /** Called on every status change (after closed-guard). */\n onStatusChange?: (status: NatsStatus, evt: NatsStatusEvent) => void\n /**\n * Decide which statuses should trigger a retry attempt. Defaults to closed +\n * disconnected. Override to skip 'error' (JetStream protocol errors that\n * don't close the WS) or include it.\n */\n shouldRetryOn?: (status: NatsStatus) => boolean\n}\n\nexport interface ConnectionLifecycleHandle {\n /** Stop observing status, clear any pending retry, release ownership if held. */\n stop(): void\n}\n\nconst defaultShouldRetryOn = (status: NatsStatus) => status === 'closed' || status === 'disconnected'\n\nexport function startConnectionLifecycle(options: ConnectionLifecycleOptions): ConnectionLifecycleHandle {\n const { conn, wsUrl } = options\n // Each lifecycle gets its own identity for retry ownership. The first lifecycle to claim\n // an unowned connection drives reconnect; later attachers observe status only until the\n // owner releases (see opportunistic claim in scheduleRetry).\n const ownerToken = {}\n if (!conn.retryOwner) conn.retryOwner = ownerToken\n\n let closed = false\n let retryAttempt = 0\n\n function emitSynthetic(status: NatsStatus) {\n if (closed) return\n options.onStatusChange?.(status, { status })\n if (status === 'connected') {\n retryAttempt = 0\n }\n }\n\n function scheduleRetry() {\n if (closed) return\n if (getSharedConnectionFor(wsUrl) !== conn) return\n if (!conn.retryOwner) conn.retryOwner = ownerToken\n if (conn.retryOwner !== ownerToken) return\n\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n\n const cfg = options.backoff ?? {}\n const fastRetries = cfg.fastRetries ?? 0\n const fastDelay = cfg.fastRetryDelayMs ?? NATS_DEFAULTS.RETRY_INITIAL_DELAY_MS\n const baseDelay = cfg.initialDelayMs ?? NATS_DEFAULTS.RETRY_INITIAL_DELAY_MS\n const maxDelay = cfg.maxDelayMs ?? NATS_DEFAULTS.RETRY_MAX_DELAY_MS\n const multiplier = cfg.multiplier ?? NATS_DEFAULTS.RETRY_MULTIPLIER\n\n const delay =\n retryAttempt < fastRetries\n ? fastDelay\n : Math.min(baseDelay * multiplier ** (retryAttempt - fastRetries), maxDelay)\n const jitteredDelay = delay * (0.5 + Math.random() * 0.5)\n retryAttempt++\n\n conn.retryTimer = setTimeout(async () => {\n conn.retryTimer = null\n if (closed) return\n if (getSharedConnectionFor(wsUrl) !== conn) return\n\n try {\n await options.onBeforeReconnect?.()\n } catch {\n // continue regardless of token-refresh outcome\n }\n if (closed) return\n if (getSharedConnectionFor(wsUrl) !== conn) return\n\n const freshUrl = options.getFreshUrl()\n if (freshUrl !== wsUrl) return\n\n try {\n conn.connectPromise = null\n conn.connectPromise = conn.client.connect()\n await conn.connectPromise\n if (!closed && getSharedConnectionFor(wsUrl) === conn) {\n retryAttempt = 0\n }\n } catch {\n conn.connectPromise = null\n if (!closed && getSharedConnectionFor(wsUrl) === conn) {\n scheduleRetry()\n }\n }\n }, jitteredDelay)\n }\n\n const shouldRetryOn = options.shouldRetryOn ?? defaultShouldRetryOn\n\n const unsubStatus = conn.client.onStatus((evt) => {\n if (closed) return\n options.onStatusChange?.(evt.status, evt)\n if (evt.status === 'connected') {\n retryAttempt = 0\n }\n if (shouldRetryOn(evt.status)) {\n scheduleRetry()\n }\n })\n\n if (conn.client.isConnected()) {\n emitSynthetic('connected')\n }\n\n void (async () => {\n try {\n conn.connectPromise ||= conn.client.connect()\n await conn.connectPromise\n } catch {\n conn.connectPromise = null\n if (closed) return\n\n emitSynthetic('disconnected')\n scheduleRetry()\n }\n })()\n\n return {\n stop() {\n closed = true\n unsubStatus()\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n if (conn.retryOwner === ownerToken) {\n conn.retryOwner = null\n }\n },\n }\n}\n","'use client'\n\nimport * as React from 'react'\nimport type { NatsClient, NatsStatus } from './nats'\nimport {\n acquireClient,\n releaseClient,\n startConnectionLifecycle,\n type AcquireClientOptions,\n type NatsReconnectionBackoff,\n} from './shared-connection'\n\nexport type { NatsReconnectionBackoff } from './shared-connection'\n\nexport interface NatsProviderProps {\n children: React.ReactNode\n /** Return the current NATS WebSocket URL (or null when not yet available, e.g. unauthenticated). */\n getWsUrl: () => string | null\n /** Called before each reconnect attempt. */\n onBeforeReconnect?: () => Promise<void> | void\n clientConfig?: AcquireClientOptions\n reconnectionBackoff?: NatsReconnectionBackoff\n /**\n * Bump this to force re-evaluating `getWsUrl()` (e.g. when auth state flips).\n * Provider does not subscribe to external auth state by itself.\n */\n urlRevision?: unknown\n}\n\nexport interface NatsContextValue {\n client: NatsClient | null\n status: NatsStatus\n isReady: boolean\n reconnectionCount: number\n}\n\nconst NatsContext = React.createContext<NatsContextValue | null>(null)\n\nexport function NatsProvider({\n children,\n getWsUrl,\n onBeforeReconnect,\n clientConfig,\n reconnectionBackoff,\n urlRevision,\n}: NatsProviderProps) {\n const [client, setClient] = React.useState<NatsClient | null>(null)\n const [status, setStatus] = React.useState<NatsStatus>('closed')\n const [reconnectionCount, setReconnectionCount] = React.useState(0)\n\n const getWsUrlRef = React.useRef(getWsUrl)\n React.useEffect(() => {\n getWsUrlRef.current = getWsUrl\n }, [getWsUrl])\n\n const onBeforeReconnectRef = React.useRef(onBeforeReconnect)\n React.useEffect(() => {\n onBeforeReconnectRef.current = onBeforeReconnect\n }, [onBeforeReconnect])\n\n const reconnectionBackoffRef = React.useRef(reconnectionBackoff)\n React.useEffect(() => {\n reconnectionBackoffRef.current = reconnectionBackoff\n }, [reconnectionBackoff])\n\n const clientConfigRef = React.useRef(clientConfig)\n React.useEffect(() => {\n clientConfigRef.current = clientConfig\n }, [clientConfig])\n\n const heldUrlRef = React.useRef<string | null>(null)\n const hadConnectionBeforeRef = React.useRef(false)\n\n React.useEffect(() => {\n const wsUrl = getWsUrlRef.current()\n\n if (!wsUrl) {\n if (heldUrlRef.current) {\n releaseClient(heldUrlRef.current)\n heldUrlRef.current = null\n setClient(null)\n setStatus('closed')\n }\n return\n }\n\n if (heldUrlRef.current && heldUrlRef.current !== wsUrl) {\n releaseClient(heldUrlRef.current)\n heldUrlRef.current = null\n }\n\n const conn = acquireClient(wsUrl, clientConfigRef.current)\n heldUrlRef.current = wsUrl\n setClient(conn.client)\n setStatus(conn.client.isConnected() ? 'connected' : 'connecting')\n\n const lifecycle = startConnectionLifecycle({\n conn,\n wsUrl,\n onBeforeReconnect: () => onBeforeReconnectRef.current?.(),\n backoff: reconnectionBackoffRef.current,\n getFreshUrl: () => getWsUrlRef.current(),\n onStatusChange: (newStatus) => {\n if (newStatus === 'error') return\n setStatus(newStatus)\n if (newStatus === 'connected') {\n if (hadConnectionBeforeRef.current) {\n setReconnectionCount((c) => c + 1)\n }\n hadConnectionBeforeRef.current = true\n }\n },\n })\n\n return () => {\n lifecycle.stop()\n if (heldUrlRef.current) {\n releaseClient(heldUrlRef.current)\n heldUrlRef.current = null\n }\n setClient(null)\n setStatus('closed')\n }\n }, [urlRevision])\n\n const value = React.useMemo<NatsContextValue>(\n () => ({\n client,\n status,\n isReady: status === 'connected' && client !== null,\n reconnectionCount,\n }),\n [client, status, reconnectionCount],\n )\n\n return <NatsContext.Provider value={value}>{children}</NatsContext.Provider>\n}\n\nexport function useNats(): NatsContextValue {\n const ctx = React.useContext(NatsContext)\n if (!ctx) throw new Error('useNats must be used inside <NatsProvider>')\n return ctx\n}\n\nexport function useOptionalNats(): NatsContextValue | null {\n return React.useContext(NatsContext)\n}\n","'use client'\n\nimport * as React from 'react'\nimport type { JsMsg, Msg, NatsSubscribeOptions } from './nats'\nimport { useOptionalNats } from './nats-provider'\n\nexport interface UseNatsSubscriptionOptions extends NatsSubscribeOptions {\n enabled?: boolean\n}\n\nexport interface UseNatsSubscriptionReturn {\n isSubscribed: boolean\n isReady: boolean\n}\n\n/**\n * Subscribe to a NATS subject using the shared connection from <NatsProvider>.\n * Automatically (re)subscribes when the connection becomes ready, when the\n * subject changes, and after reconnections.\n */\nexport function useNatsSubscription(\n subject: string | null,\n onMessage: (msg: Msg) => void | Promise<void>,\n options?: UseNatsSubscriptionOptions,\n): UseNatsSubscriptionReturn {\n const nats = useOptionalNats()\n const handlerRef = React.useRef(onMessage)\n React.useEffect(() => {\n handlerRef.current = onMessage\n }, [onMessage])\n\n const [isSubscribed, setIsSubscribed] = React.useState(false)\n\n const enabled = options?.enabled !== false\n const queue = options?.queue\n const max = options?.max\n const reconnectionCount = nats?.reconnectionCount ?? 0\n const isReady = !!nats?.isReady\n const client = nats?.client ?? null\n\n React.useEffect(() => {\n if (!client || !isReady || !subject || !enabled) {\n setIsSubscribed(false)\n return\n }\n\n const sub = client.subscribeBytes(\n subject,\n (msg) => handlerRef.current(msg),\n { queue, max },\n )\n setIsSubscribed(true)\n\n return () => {\n setIsSubscribed(false)\n try {\n sub.unsubscribe()\n } catch {\n // ignore\n }\n }\n // reconnectionCount intentionally in deps: re-subscribe after reconnect\n }, [client, isReady, subject, enabled, queue, max, reconnectionCount])\n\n return { isSubscribed, isReady }\n}\n\nexport type UseNatsJsonSubscriptionOptions = UseNatsSubscriptionOptions\n\nexport function useNatsJsonSubscription<T = unknown>(\n subject: string | null,\n onPayload: (payload: T, msg: Msg) => void | Promise<void>,\n options?: UseNatsJsonSubscriptionOptions,\n): UseNatsSubscriptionReturn {\n const handlerRef = React.useRef(onPayload)\n React.useEffect(() => {\n handlerRef.current = onPayload\n }, [onPayload])\n\n const decoderRef = React.useRef<TextDecoder | null>(null)\n if (!decoderRef.current && typeof TextDecoder !== 'undefined') {\n decoderRef.current = new TextDecoder()\n }\n\n const wrapped = React.useCallback(async (msg: Msg) => {\n const decoder = decoderRef.current\n if (!decoder) return\n try {\n const parsed = JSON.parse(decoder.decode(msg.data)) as T\n await handlerRef.current(parsed, msg)\n } catch {\n // ignore malformed payloads\n }\n }, [])\n\n return useNatsSubscription(subject, wrapped, options)\n}\n\nexport type NatsJsMsg = JsMsg\n"]}
|
|
File without changes
|