@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.
Files changed (113) hide show
  1. package/dist/{chunk-6ER3AYJM.js → chunk-5756Q6UN.js} +3 -3
  2. package/dist/{chunk-E2IPBWGT.js → chunk-5EES5HD4.js} +6 -6
  3. package/dist/{chunk-ODRMPI3Y.js → chunk-5JWLZJK5.js} +2 -2
  4. package/dist/{chunk-CVCQW7WG.js → chunk-5M4PS3XX.js} +3 -3
  5. package/dist/{chunk-6QRKCVYR.cjs → chunk-7Q7GFIFB.cjs} +7 -7
  6. package/dist/{chunk-6QRKCVYR.cjs.map → chunk-7Q7GFIFB.cjs.map} +1 -1
  7. package/dist/{chunk-UXW63WCV.cjs → chunk-BNMKHG2L.cjs} +4 -4
  8. package/dist/{chunk-UXW63WCV.cjs.map → chunk-BNMKHG2L.cjs.map} +1 -1
  9. package/dist/{chunk-SST2YI7S.cjs → chunk-BZKUID4R.cjs} +14 -14
  10. package/dist/{chunk-SST2YI7S.cjs.map → chunk-BZKUID4R.cjs.map} +1 -1
  11. package/dist/{chunk-YAOBDOI5.cjs → chunk-C74PZU6M.cjs} +32 -32
  12. package/dist/{chunk-YAOBDOI5.cjs.map → chunk-C74PZU6M.cjs.map} +1 -1
  13. package/dist/{chunk-P7MSXPGL.js → chunk-CGUSNV6R.js} +8 -8
  14. package/dist/{chunk-N5A4AC2J.cjs → chunk-CRGIXNUE.cjs} +23 -23
  15. package/dist/{chunk-N5A4AC2J.cjs.map → chunk-CRGIXNUE.cjs.map} +1 -1
  16. package/dist/{chunk-Q6RC3YGW.cjs → chunk-DFJ7TWH6.cjs} +3 -3
  17. package/dist/{chunk-Q6RC3YGW.cjs.map → chunk-DFJ7TWH6.cjs.map} +1 -1
  18. package/dist/{chunk-HO3Q7NXJ.js → chunk-FLSEJSTN.js} +2 -2
  19. package/dist/{chunk-UL3WQWTF.js → chunk-FZT7BYNF.js} +2 -2
  20. package/dist/{chunk-XOKRFZBD.cjs → chunk-HOEOGFQM.cjs} +97 -97
  21. package/dist/{chunk-XOKRFZBD.cjs.map → chunk-HOEOGFQM.cjs.map} +1 -1
  22. package/dist/{chunk-MLVF3OXU.cjs → chunk-HYABNMBN.cjs} +76 -76
  23. package/dist/{chunk-MLVF3OXU.cjs.map → chunk-HYABNMBN.cjs.map} +1 -1
  24. package/dist/{chunk-J76LCSR4.cjs → chunk-ICG7JRYT.cjs} +40 -40
  25. package/dist/{chunk-J76LCSR4.cjs.map → chunk-ICG7JRYT.cjs.map} +1 -1
  26. package/dist/{chunk-P22HM2MO.cjs → chunk-JDUPAHJR.cjs} +18 -18
  27. package/dist/{chunk-P22HM2MO.cjs.map → chunk-JDUPAHJR.cjs.map} +1 -1
  28. package/dist/{chunk-B2GEKAAA.js → chunk-JRWOHH7K.js} +2 -2
  29. package/dist/{chunk-TXV444YV.js → chunk-JXJ4FTKN.js} +3 -3
  30. package/dist/{chunk-CU6Q2KQT.cjs → chunk-LZD3VSPS.cjs} +12 -12
  31. package/dist/{chunk-CU6Q2KQT.cjs.map → chunk-LZD3VSPS.cjs.map} +1 -1
  32. package/dist/{chunk-M5TWHFIN.cjs → chunk-Q5SZ2QTZ.cjs} +42 -42
  33. package/dist/{chunk-M5TWHFIN.cjs.map → chunk-Q5SZ2QTZ.cjs.map} +1 -1
  34. package/dist/{chunk-GSONSIWG.js → chunk-R7A7WHXP.js} +60 -35
  35. package/dist/chunk-R7A7WHXP.js.map +1 -0
  36. package/dist/{chunk-E56VNTBV.cjs → chunk-S4LL6QI6.cjs} +147 -147
  37. package/dist/{chunk-E56VNTBV.cjs.map → chunk-S4LL6QI6.cjs.map} +1 -1
  38. package/dist/{chunk-KQKU4VXB.cjs → chunk-STHVHAV4.cjs} +82 -57
  39. package/dist/chunk-STHVHAV4.cjs.map +1 -0
  40. package/dist/{chunk-A7ES3NZK.cjs → chunk-SXMJLNOF.cjs} +3 -3
  41. package/dist/{chunk-A7ES3NZK.cjs.map → chunk-SXMJLNOF.cjs.map} +1 -1
  42. package/dist/{chunk-CTTY5EKH.js → chunk-VF4DCOPD.js} +5 -5
  43. package/dist/{chunk-QTO3WYB2.cjs → chunk-VJTD2DGF.cjs} +3 -3
  44. package/dist/{chunk-QTO3WYB2.cjs.map → chunk-VJTD2DGF.cjs.map} +1 -1
  45. package/dist/{chunk-VGLDA77S.js → chunk-VRX6Y6A7.js} +2 -2
  46. package/dist/{chunk-L36R3G7Z.js → chunk-VXNMOGRP.js} +6 -6
  47. package/dist/{chunk-OR67WC32.js → chunk-WBQ3NOH7.js} +2 -2
  48. package/dist/{chunk-QMQQ37RW.js → chunk-WX5UCLL7.js} +2 -2
  49. package/dist/{chunk-QZ64JOE2.js → chunk-XGBVHQP7.js} +3 -3
  50. package/dist/{chunk-K432ES7N.js → chunk-YM2WHYYK.js} +4 -4
  51. package/dist/{chunk-KXLQITOX.cjs → chunk-Z3R3LOC5.cjs} +5 -5
  52. package/dist/{chunk-KXLQITOX.cjs.map → chunk-Z3R3LOC5.cjs.map} +1 -1
  53. package/dist/components/case-studies/index.cjs +10 -10
  54. package/dist/components/case-studies/index.js +4 -4
  55. package/dist/components/chat/index.cjs +4 -4
  56. package/dist/components/chat/index.js +3 -3
  57. package/dist/components/contact/index.cjs +5 -5
  58. package/dist/components/contact/index.js +4 -4
  59. package/dist/components/docs/index.cjs +7 -7
  60. package/dist/components/docs/index.js +6 -6
  61. package/dist/components/embeds/index.cjs +5 -5
  62. package/dist/components/embeds/index.js +4 -4
  63. package/dist/components/faq/index.cjs +6 -6
  64. package/dist/components/faq/index.js +5 -5
  65. package/dist/components/features/index.cjs +4 -4
  66. package/dist/components/features/index.js +3 -3
  67. package/dist/components/help-center-pages/index.cjs +25 -25
  68. package/dist/components/help-center-pages/index.js +16 -16
  69. package/dist/components/index.cjs +138 -138
  70. package/dist/components/index.js +14 -14
  71. package/dist/components/meeting-scheduler/index.cjs +35 -35
  72. package/dist/components/meeting-scheduler/index.js +5 -5
  73. package/dist/components/navigation/index.cjs +4 -4
  74. package/dist/components/navigation/index.js +3 -3
  75. package/dist/components/onboarding-guides/index.cjs +8 -8
  76. package/dist/components/onboarding-guides/index.js +7 -7
  77. package/dist/components/related-content/index.cjs +6 -6
  78. package/dist/components/related-content/index.js +5 -5
  79. package/dist/components/tickets/index.cjs +8 -8
  80. package/dist/components/tickets/index.js +7 -7
  81. package/dist/components/ui/index.cjs +4 -4
  82. package/dist/components/ui/index.js +3 -3
  83. package/dist/hooks/index.cjs +3 -3
  84. package/dist/hooks/index.js +2 -2
  85. package/dist/index.cjs +4 -4
  86. package/dist/index.js +3 -3
  87. package/dist/nats/index.cjs +2 -2
  88. package/dist/nats/index.js +1 -1
  89. package/dist/nats/nats.d.ts.map +1 -1
  90. package/dist/nats/shared-connection.d.ts +13 -6
  91. package/dist/nats/shared-connection.d.ts.map +1 -1
  92. package/package.json +1 -1
  93. package/src/nats/__tests__/reconnect-lifecycle.test.tsx +814 -0
  94. package/src/nats/nats.ts +56 -13
  95. package/src/nats/shared-connection.ts +132 -36
  96. package/dist/chunk-GSONSIWG.js.map +0 -1
  97. package/dist/chunk-KQKU4VXB.cjs.map +0 -1
  98. /package/dist/{chunk-6ER3AYJM.js.map → chunk-5756Q6UN.js.map} +0 -0
  99. /package/dist/{chunk-E2IPBWGT.js.map → chunk-5EES5HD4.js.map} +0 -0
  100. /package/dist/{chunk-ODRMPI3Y.js.map → chunk-5JWLZJK5.js.map} +0 -0
  101. /package/dist/{chunk-CVCQW7WG.js.map → chunk-5M4PS3XX.js.map} +0 -0
  102. /package/dist/{chunk-P7MSXPGL.js.map → chunk-CGUSNV6R.js.map} +0 -0
  103. /package/dist/{chunk-HO3Q7NXJ.js.map → chunk-FLSEJSTN.js.map} +0 -0
  104. /package/dist/{chunk-UL3WQWTF.js.map → chunk-FZT7BYNF.js.map} +0 -0
  105. /package/dist/{chunk-B2GEKAAA.js.map → chunk-JRWOHH7K.js.map} +0 -0
  106. /package/dist/{chunk-TXV444YV.js.map → chunk-JXJ4FTKN.js.map} +0 -0
  107. /package/dist/{chunk-CTTY5EKH.js.map → chunk-VF4DCOPD.js.map} +0 -0
  108. /package/dist/{chunk-VGLDA77S.js.map → chunk-VRX6Y6A7.js.map} +0 -0
  109. /package/dist/{chunk-L36R3G7Z.js.map → chunk-VXNMOGRP.js.map} +0 -0
  110. /package/dist/{chunk-OR67WC32.js.map → chunk-WBQ3NOH7.js.map} +0 -0
  111. /package/dist/{chunk-QMQQ37RW.js.map → chunk-WX5UCLL7.js.map} +0 -0
  112. /package/dist/{chunk-QZ64JOE2.js.map → chunk-XGBVHQP7.js.map} +0 -0
  113. /package/dist/{chunk-K432ES7N.js.map → chunk-YM2WHYYK.js.map} +0 -0
@@ -57,14 +57,24 @@ function mapOptionsToConnectionOptions(opts, backoff) {
57
57
  inboxPrefix: opts.inboxPrefix
58
58
  };
59
59
  }
60
- function mapNatsTypeToStatus(type) {
61
- const t = String(type).toLowerCase();
62
- if (t.includes("disconnect")) return "disconnected";
63
- if (t === "reconnecting") return "reconnecting";
64
- if (t === "reconnect" || t.includes("connect")) return "connected";
65
- if (t.includes("error")) return "error";
66
- if (t.includes("close")) return "closed";
67
- return null;
60
+ function mapNatsTypeToStatus(type, events, debugEvents) {
61
+ switch (type) {
62
+ case events.Disconnect:
63
+ return "disconnected";
64
+ case events.Reconnect:
65
+ return "connected";
66
+ case events.Error:
67
+ return "error";
68
+ case debugEvents.Reconnecting:
69
+ case debugEvents.ClientInitiatedReconnect:
70
+ return "reconnecting";
71
+ case debugEvents.StaleConnection:
72
+ return "disconnected";
73
+ // Events.Update (cluster gossip), Events.LDM and DebugEvents.PingTimer say
74
+ // nothing about reachability.
75
+ default:
76
+ return null;
77
+ }
68
78
  }
69
79
  function watchForRecovery(iter, recreatedEvent, onRecovered) {
70
80
  if (!onRecovered) return;
@@ -114,15 +124,12 @@ function createNatsClient(options) {
114
124
  try {
115
125
  for await (const s of conn.status()) {
116
126
  if (signal.aborted) return;
117
- const mapped = mapNatsTypeToStatus(s?.type);
127
+ const mapped = mapNatsTypeToStatus(s.type, nats.Events, nats.DebugEvents);
118
128
  if (mapped) {
119
129
  if (mapped === "connected" && backoff) {
120
130
  backoff.reset();
121
131
  }
122
- emitStatus({ status: mapped, data: s?.data });
123
- if (mapped === "closed") {
124
- nc = null;
125
- }
132
+ emitStatus({ status: mapped, data: s.data });
126
133
  }
127
134
  }
128
135
  } catch (e) {
@@ -414,11 +421,11 @@ function acquireClient(url, opts) {
414
421
  conn = {
415
422
  wsUrl: url,
416
423
  client,
417
- connectPromise: null,
418
424
  refCount: 0,
419
425
  closeTimer: null,
420
426
  retryTimer: null,
421
- retryOwner: null
427
+ retryOwner: null,
428
+ retrySchedulers: /* @__PURE__ */ new Set()
422
429
  };
423
430
  connections.set(url, conn);
424
431
  }
@@ -454,10 +461,14 @@ function getSharedConnectionFor(url) {
454
461
  return connections.get(url) ?? null;
455
462
  }
456
463
  var defaultShouldRetryOn = (status) => status === "closed" || status === "disconnected";
464
+ function handOffRetry(conn) {
465
+ for (const takeOver of conn.retrySchedulers) {
466
+ takeOver();
467
+ if (conn.retryOwner) return;
468
+ }
469
+ }
457
470
  function startConnectionLifecycle(options) {
458
471
  const { conn, wsUrl } = options;
459
- const ownerToken = {};
460
- if (!conn.retryOwner) conn.retryOwner = ownerToken;
461
472
  let closed = false;
462
473
  let retryAttempt = 0;
463
474
  function emitSynthetic(status) {
@@ -467,15 +478,15 @@ function startConnectionLifecycle(options) {
467
478
  retryAttempt = 0;
468
479
  }
469
480
  }
481
+ conn.retrySchedulers.add(scheduleRetry);
482
+ if (!conn.retryOwner) conn.retryOwner = scheduleRetry;
470
483
  function scheduleRetry() {
471
484
  if (closed) return;
472
485
  if (getSharedConnectionFor(wsUrl) !== conn) return;
473
- if (!conn.retryOwner) conn.retryOwner = ownerToken;
474
- if (conn.retryOwner !== ownerToken) return;
475
- if (conn.retryTimer) {
476
- clearTimeout(conn.retryTimer);
477
- conn.retryTimer = null;
478
- }
486
+ if (options.getFreshUrl() !== wsUrl) return;
487
+ if (!conn.retryOwner) conn.retryOwner = scheduleRetry;
488
+ if (conn.retryOwner !== scheduleRetry) return;
489
+ if (conn.retryTimer) return;
479
490
  const cfg = options.backoff ?? {};
480
491
  const fastRetries = cfg.fastRetries ?? 0;
481
492
  const fastDelay = cfg.fastRetryDelayMs ?? NATS_DEFAULTS.RETRY_INITIAL_DELAY_MS;
@@ -489,6 +500,10 @@ function startConnectionLifecycle(options) {
489
500
  conn.retryTimer = null;
490
501
  if (closed) return;
491
502
  if (getSharedConnectionFor(wsUrl) !== conn) return;
503
+ if (conn.client.isConnected()) {
504
+ retryAttempt = 0;
505
+ return;
506
+ }
492
507
  try {
493
508
  await options.onBeforeReconnect?.();
494
509
  } catch {
@@ -496,16 +511,23 @@ function startConnectionLifecycle(options) {
496
511
  if (closed) return;
497
512
  if (getSharedConnectionFor(wsUrl) !== conn) return;
498
513
  const freshUrl = options.getFreshUrl();
499
- if (freshUrl !== wsUrl) return;
514
+ if (freshUrl !== wsUrl) {
515
+ if (conn.retryOwner === scheduleRetry) {
516
+ conn.retryOwner = null;
517
+ if (conn.retryTimer) {
518
+ clearTimeout(conn.retryTimer);
519
+ conn.retryTimer = null;
520
+ }
521
+ handOffRetry(conn);
522
+ }
523
+ return;
524
+ }
500
525
  try {
501
- conn.connectPromise = null;
502
- conn.connectPromise = conn.client.connect();
503
- await conn.connectPromise;
526
+ await conn.client.connect();
504
527
  if (!closed && getSharedConnectionFor(wsUrl) === conn) {
505
528
  retryAttempt = 0;
506
529
  }
507
530
  } catch {
508
- conn.connectPromise = null;
509
531
  if (!closed && getSharedConnectionFor(wsUrl) === conn) {
510
532
  scheduleRetry();
511
533
  }
@@ -528,10 +550,8 @@ function startConnectionLifecycle(options) {
528
550
  }
529
551
  void (async () => {
530
552
  try {
531
- conn.connectPromise || (conn.connectPromise = conn.client.connect());
532
- await conn.connectPromise;
553
+ await conn.client.connect();
533
554
  } catch {
534
- conn.connectPromise = null;
535
555
  if (closed) return;
536
556
  emitSynthetic("disconnected");
537
557
  scheduleRetry();
@@ -541,13 +561,18 @@ function startConnectionLifecycle(options) {
541
561
  stop() {
542
562
  closed = true;
543
563
  unsubStatus();
564
+ conn.retrySchedulers.delete(scheduleRetry);
565
+ if (conn.retryOwner !== scheduleRetry) {
566
+ return;
567
+ }
568
+ conn.retryOwner = null;
569
+ const wasRetrying = conn.retryTimer !== null;
544
570
  if (conn.retryTimer) {
545
571
  clearTimeout(conn.retryTimer);
546
572
  conn.retryTimer = null;
547
573
  }
548
- if (conn.retryOwner === ownerToken) {
549
- conn.retryOwner = null;
550
- }
574
+ if (!wasRetrying && conn.client.isConnected()) return;
575
+ handOffRetry(conn);
551
576
  }
552
577
  };
553
578
  }
@@ -722,4 +747,4 @@ export {
722
747
  useNatsSubscription,
723
748
  useNatsJsonSubscription
724
749
  };
725
- //# sourceMappingURL=chunk-GSONSIWG.js.map
750
+ //# sourceMappingURL=chunk-R7A7WHXP.js.map
@@ -0,0 +1 @@
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 DebugEvents,\n Events,\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\n/**\n * Map a nats.ws status event onto our own status vocabulary.\n *\n * Exact enum matches, never substring tests. Substring matching reported\n * `staleConnection` and `client initiated reconnect` as CONNECTED — both\n * contain \"connect\" and neither contains \"disconnect\" — so the wrapper\n * announced a live connection at the exact moment the client had given up on\n * one. Downstream that is worse than silence: `NatsProvider` reads a\n * `connected` that follows an earlier connection as a RECONNECT and bumps\n * `reconnectionCount`, which every subscriber takes as \"the tail came back\" —\n * resubscribing against a dying client and refetching persisted history.\n * `staleConnection` is the one an idle mobile app hits routinely: no pong\n * within `pingInterval * maxPingOut`, i.e. after a spell in the background.\n *\n * `StaleConnection` maps to `disconnected` rather than being ignored on the\n * grounds that `Disconnect` follows it anyway — it does not always. nats.ws\n * tears the transport down by draining `bufferedAmount` first, and a\n * black-holed socket (no FIN, which is how a mobile link usually dies) never\n * drains, so `Disconnect` can fail to arrive at all. Reporting it is then the\n * only honest answer available: consumers stop trusting a tail the client has\n * already given up on. It does NOT by itself recover that connection — the\n * retry it arms short-circuits, because a protocol that never closed still\n * reports `isConnected()`. Getting the socket back from that state needs a\n * force-close path the client does not currently expose.\n *\n * The enums arrive as arguments rather than through a module-scope import\n * because `nats.ws` is loaded lazily (see {@link importNats}) to keep this\n * module safe to import from a Next server bundle. A hand-copied literal is\n * what this is undoing, so it is not reintroduced here.\n */\nfunction mapNatsTypeToStatus(\n type: Events | DebugEvents,\n events: typeof import('nats.ws').Events,\n debugEvents: typeof import('nats.ws').DebugEvents,\n): NatsStatus | null {\n switch (type) {\n case events.Disconnect:\n return 'disconnected'\n case events.Reconnect:\n return 'connected'\n case events.Error:\n return 'error'\n case debugEvents.Reconnecting:\n case debugEvents.ClientInitiatedReconnect:\n return 'reconnecting'\n case debugEvents.StaleConnection:\n return 'disconnected'\n // Events.Update (cluster gossip), Events.LDM and DebugEvents.PingTimer say\n // nothing about reachability.\n default:\n return null\n }\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.type, nats.Events, nats.DebugEvents)\n if (mapped) {\n if (mapped === 'connected' && backoff) {\n backoff.reset()\n }\n emitStatus({ status: mapped, data: s.data })\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 refCount: number\n closeTimer: ReturnType<typeof setTimeout> | null\n retryTimer: ReturnType<typeof setTimeout> | null\n /**\n * The lifecycle driving reconnect, held as its own `scheduleRetry`. When set,\n * other consumers observe status only and skip their own scheduleRetry —\n * otherwise every disconnect starts one backoff schedule per attached\n * consumer, all dialling the same connection on their own clocks.\n */\n retryOwner: (() => void) | null\n /**\n * The `scheduleRetry` of every lifecycle currently observing this connection,\n * in attach order — the same value that goes into `retryOwner`, so a\n * lifecycle has one identity rather than two. Only the owner drives\n * reconnect, so when it gives the loop up the roster is what a successor is\n * found in; see {@link handOffRetry}.\n */\n retrySchedulers: Set<() => void>\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 refCount: 0,\n closeTimer: null,\n retryTimer: null,\n retryOwner: null,\n retrySchedulers: new Set(),\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. Every consumer that wants to observe or drive reconnect calls\n// startConnectionLifecycle(). The first to claim retryOwner runs the actual\n// retry loop; later attachers observe status only. When the owner gives the\n// loop up it is handed to a survivor if the connection still needs one; while\n// the connection is healthy no handoff is needed, because the next status\n// event lets a survivor claim ownership opportunistically inside\n// scheduleRetry.\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\n/**\n * Offer the retry loop to another lifecycle on this connection.\n *\n * A retry is only ever armed from a status event or a failed dial, and a\n * connection that is already down produces neither on its own — so whenever\n * the owner gives the loop up while the connection still needs dialling, a\n * successor has to be pushed rather than left waiting for an event that is not\n * coming.\n *\n * Candidates that cannot service this URL decline inside `scheduleRetry`, so\n * this walks the roster until one actually takes it. Callers must clear any\n * timer of their own first: a successor that claims while a timer is still\n * armed will not arm one, and the departing owner's timer no longer speaks for\n * the connection.\n */\nfunction handOffRetry(conn: SharedConnection): void {\n for (const takeOver of conn.retrySchedulers) {\n takeOver()\n if (conn.retryOwner) return\n }\n}\n\nexport function startConnectionLifecycle(options: ConnectionLifecycleOptions): ConnectionLifecycleHandle {\n const { conn, wsUrl } = options\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 // A lifecycle's identity IS its `scheduleRetry`: that function goes on the\n // connection's roster and, for whichever lifecycle holds the loop, into\n // `retryOwner` — one identity, not two.\n conn.retrySchedulers.add(scheduleRetry)\n if (!conn.retryOwner) conn.retryOwner = scheduleRetry\n\n function scheduleRetry() {\n if (closed) return\n if (getSharedConnectionFor(wsUrl) !== conn) return\n // This lifecycle now wants a different URL, so it must not drive this\n // connection — not from its own status events, and not as a handoff\n // successor. Without the guard, two consumers that had both moved on\n // passed ownership back and forth indefinitely, running onBeforeReconnect\n // — a token refresh in the real callers — on every pass and dialling\n // nothing. The armed callback re-checks, because the URL can move between\n // arming and firing.\n if (options.getFreshUrl() !== wsUrl) return\n if (!conn.retryOwner) conn.retryOwner = scheduleRetry\n if (conn.retryOwner !== scheduleRetry) return\n\n // One outage can raise more than one status: nats.ws reports\n // `staleConnection` and then, once the transport is down, `disconnect`.\n // Re-arming on the second would spend a backoff tier on the same outage —\n // on the defaults, waiting 2000ms where the caller asked for 1000. The\n // armed timer re-validates everything when it fires, so the first one\n // scheduled for an outage is always the right one to keep.\n if (conn.retryTimer) return\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 // Nothing to reconnect. Reached when the dial that was already in flight\n // succeeded while this retry sat in its backoff — a handoff during a\n // healthy first connect is enough to get here — and `onBeforeReconnect`\n // is a token refresh in the real callers, so it must not run on a\n // connection that came back on its own.\n if (conn.client.isConnected()) {\n retryAttempt = 0\n return\n }\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) {\n // This lifecycle has moved to a different URL, so it must not dial this\n // connection again while holding the loop — that is the dead end the\n // rest of this module exists to avoid. Give the loop up and push it to\n // someone still on this URL.\n if (conn.retryOwner === scheduleRetry) {\n conn.retryOwner = null\n // A status that landed while the refresh above was awaited can have\n // armed a new timer under this ownership. It belongs to a lifecycle\n // that is leaving, and a successor claiming while it is armed would\n // return early believing the loop is covered — then this timer fires,\n // finds someone else owns the loop, and bails without handing on.\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n handOffRetry(conn)\n }\n return\n }\n\n try {\n await conn.client.connect()\n if (!closed && getSharedConnectionFor(wsUrl) === conn) {\n retryAttempt = 0\n }\n } catch {\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 // Dial through the client rather than caching a promise on the connection.\n // A `conn.connectPromise` used to be assigned here with `||=` and then left\n // in place after a SUCCESSFUL connect, so it doubled as \"this connection has\n // been dialled at some point\": a consumer that acquired a still-cached\n // connection whose socket had since died joined that long-settled promise\n // instead of dialling. No connect, no failure, so nothing scheduled a retry,\n // and no status event was coming to arm one either — the connection stayed\n // dead for as long as anything held a reference to it. `NatsClient.connect()`\n // already short-circuits on a live connection and joins its own in-flight\n // dial, so the second copy of that bookkeeping bought nothing.\n void (async () => {\n try {\n await conn.client.connect()\n } catch {\n if (closed) return\n\n emitSynthetic('disconnected')\n scheduleRetry()\n }\n })()\n\n return {\n stop() {\n closed = true\n unsubStatus()\n conn.retrySchedulers.delete(scheduleRetry)\n\n if (conn.retryOwner !== scheduleRetry) {\n // Not ours to cancel. `retryTimer` lives on the CONNECTION, and\n // clearing it unconditionally cancelled the owner's armed retry — so\n // any consumer merely re-running its effect (an `enabled` toggle, a\n // dialog closing, a route change) during a backoff killed reconnect\n // for everyone. Nothing rescheduled it either, for the reason\n // handOffRetry exists. The connection stayed down for good.\n return\n }\n\n conn.retryOwner = null\n const wasRetrying = conn.retryTimer !== null\n if (conn.retryTimer) {\n clearTimeout(conn.retryTimer)\n conn.retryTimer = null\n }\n\n // A healthy connection with nothing armed needs no successor: the next\n // disconnect lets a survivor claim opportunistically. `wasRetrying` is\n // read before the clearTimeout above, and the remaining case — the timer\n // already fired and its callback is mid-dial — is caught by the\n // connection not being connected. The successor backs off on its OWN\n // attempt counter, which is zero unless it has driven the loop before.\n if (!wasRetrying && conn.client.isConnected()) return\n handOffRetry(conn)\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"]}