@braze/web-sdk 6.7.1 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/index.d.ts +74 -27
  2. package/package.json +1 -1
  3. package/shared-lib/encoding-utils.js +2 -2
  4. package/shared-lib/event-types.js +13 -13
  5. package/shared-lib/indexed-db-adapter.js +3 -3
  6. package/shared-lib/logger.js +2 -2
  7. package/shared-lib/supported-options.js +8 -8
  8. package/src/Banner/log-banner-impressions.js +20 -11
  9. package/src/ContentCards/content-cards-provider-factory.js +13 -11
  10. package/src/ContentCards/content-cards-provider.js +4 -4
  11. package/src/ContentCards/get-cached-content-cards.js +2 -2
  12. package/src/ContentCards/request-content-cards-refresh.js +2 -2
  13. package/src/ContentCards/subscribe-to-content-cards-updates.js +10 -10
  14. package/src/ContentCards/ui/show-content-cards.js +3 -3
  15. package/src/Core/change-user.js +2 -2
  16. package/src/Core/index.js +1 -0
  17. package/src/Core/log-ecommerce-event.js +29 -0
  18. package/src/Core/log-purchase.js +17 -192
  19. package/src/Core/open-session.js +6 -6
  20. package/src/Core/set-sdk-authentication-signature.js +1 -1
  21. package/src/Core/subscribe-to-sdk-authentication-failures.js +1 -1
  22. package/src/DUST/dust-connection-core.js +1 -0
  23. package/src/DUST/dust-provider-factory.js +10 -10
  24. package/src/DUST/dust-provider.js +181 -158
  25. package/src/DUST/dust-shared-worker-code.js +1 -1
  26. package/src/DUST/dust-shared-worker-impl.js +93 -100
  27. package/src/DUST/dust-worker-bridge.js +27 -27
  28. package/src/DUST/subscribe-to-dust.js +7 -7
  29. package/src/InAppMessage/defer-in-app-message.js +1 -1
  30. package/src/InAppMessage/display/in-app-message-to-html.js +16 -16
  31. package/src/InAppMessage/display/modal-utils.js +2 -2
  32. package/src/InAppMessage/in-app-message-manager.js +64 -64
  33. package/src/InAppMessage/log-in-app-message-button-click.js +1 -1
  34. package/src/InAppMessage/log-in-app-message-click.js +1 -1
  35. package/src/InAppMessage/log-in-app-message-html-click.js +2 -2
  36. package/src/InAppMessage/log-in-app-message-impression.js +1 -1
  37. package/src/InAppMessage/models/full-screen-message.js +7 -7
  38. package/src/InAppMessage/models/html-message.js +1 -1
  39. package/src/InAppMessage/models/in-app-message-button.js +4 -4
  40. package/src/InAppMessage/models/in-app-message.js +22 -22
  41. package/src/InAppMessage/models/modal-message.js +5 -5
  42. package/src/InAppMessage/models/slide-up-message.js +6 -6
  43. package/src/InAppMessage/models/templated-in-app-message.js +8 -8
  44. package/src/InAppMessage/subscribe-to-in-app-message.js +1 -1
  45. package/src/InAppMessage/ui/automatically-show-in-app-messages.js +3 -3
  46. package/src/InAppMessage/ui/show-in-app-message.js +20 -20
  47. package/src/Push/push-manager-factory.js +2 -2
  48. package/src/Push/push-manager.js +8 -8
  49. package/src/Push/utils/push-utils.js +6 -6
  50. package/src/User/user-manager.js +5 -5
  51. package/src/User/user.js +8 -8
  52. package/src/common/constants.js +1 -0
  53. package/src/common/content-cards-display.js +2 -2
  54. package/src/l10n/l10n-manager-factory.js +1 -1
  55. package/src/l10n/l10n-manager.js +1 -1
  56. package/src/managers/braze-instance.js +21 -21
  57. package/src/managers/device-manager.js +6 -6
  58. package/src/managers/network-manager.js +93 -92
  59. package/src/managers/server-config-manager.js +68 -68
  60. package/src/managers/session-manager.js +2 -2
  61. package/src/managers/storage-manager-factory.js +4 -4
  62. package/src/managers/storage-manager.js +120 -120
  63. package/src/managers/subscription-manager.js +1 -1
  64. package/src/managers/utils.js +1 -1
  65. package/src/models/backend-errors.js +5 -5
  66. package/src/models/braze-event.js +3 -3
  67. package/src/models/device.js +1 -1
  68. package/src/models/identifier.js +3 -3
  69. package/src/models/push-token.js +5 -5
  70. package/src/models/server-config.js +27 -27
  71. package/src/request-controller.js +99 -99
  72. package/src/triggers/models/custom-event-data.js +4 -4
  73. package/src/triggers/models/custom-event-property-data.js +5 -5
  74. package/src/triggers/models/filter-set.js +2 -2
  75. package/src/triggers/models/filter.js +1 -1
  76. package/src/triggers/models/in-app-message-click-data.js +5 -5
  77. package/src/triggers/models/purchase-data.js +1 -1
  78. package/src/triggers/models/purchase-property-data.js +2 -2
  79. package/src/triggers/models/push-click-data.js +5 -5
  80. package/src/triggers/models/trigger-condition.js +42 -42
  81. package/src/triggers/models/trigger-events.js +2 -2
  82. package/src/triggers/models/trigger.js +10 -10
  83. package/src/triggers/triggers-provider-factory.js +1 -1
  84. package/src/triggers/triggers-provider.js +30 -30
  85. package/src/types.js +5 -1
  86. package/src/ui/js/attach-css.js +1 -1
  87. package/src/util/braze-actions.js +5 -5
  88. package/src/util/browser-detector.js +13 -13
  89. package/src/util/client-hints-parser.js +2 -2
  90. package/src/util/dom-utils.js +2 -2
  91. package/src/util/ecommerce-event-validation.js +326 -0
  92. package/src/util/iso-4217-currency-codes.js +176 -0
  93. package/src/util/net.js +3 -3
  94. package/src/util/request-header-utils.js +18 -18
  95. package/src/util/user-agent-parser.js +14 -14
  96. package/src/util/validation-utils.js +2 -2
  97. package/src/util/window-utils.js +2 -2
@@ -12,14 +12,15 @@ import {
12
12
  } from "../common/constants.js";
13
13
  import { randomInclusive as a } from "../util/math.js";
14
14
  import {
15
- MAX_RETRIES as ii,
16
- buildSseUrl as ti,
15
+ CONNECTION_VALID_THRESHOLD_MS as ii,
16
+ MAX_RETRIES as ti,
17
+ buildSseUrl as si,
17
18
  } from "./dust-connection-core.js";
18
19
  import {
19
- DustWorkerBridge as si,
20
- isSharedWorkerSupported as ei,
20
+ DustWorkerBridge as ei,
21
+ isSharedWorkerSupported as ni,
21
22
  } from "./dust-worker-bridge.js";
22
- export default class nr extends t {
23
+ export default class sr extends t {
23
24
  constructor(i, t, s, e, n = !0) {
24
25
  super(),
25
26
  (this.B = i),
@@ -34,30 +35,31 @@ export default class nr extends t {
34
35
  (this.Hi = null),
35
36
  (this.Oi = e || null),
36
37
  (this.Ki = null),
37
- (this.Qi = null),
38
+ (this.Vi = null),
38
39
  (this.T = null),
39
- (this.Vi = 0),
40
- (this.Xi = ii),
40
+ (this._i = 0),
41
+ (this.Qi = ti),
42
+ (this.Xi = null),
41
43
  (this.Yi = null),
42
- (this.Zi = null),
43
- (this._i = !0),
44
+ (this.Zi = !0),
44
45
  (this.we = null),
45
46
  (this.Se = null),
46
47
  (this.ke = null),
47
48
  (this.ye = null),
48
49
  (this.Re = !1),
49
- (this.Me = new Map()),
50
+ (this.De = new Map()),
51
+ (this.Me = null),
50
52
  (this.xe = null),
51
- (this.De = null),
52
53
  (this.We = null),
53
54
  (this.Fe = this.Ue(n)),
54
55
  (this.Ne = !1),
55
- (this.Te = 0),
56
+ (this.Be = 0),
57
+ (this.Te = null),
56
58
  this.ze();
57
59
  }
58
60
  Ue(i) {
59
61
  return i
60
- ? ei()
62
+ ? ni()
61
63
  ? "sharedworker"
62
64
  : (E.info(
63
65
  "SharedWorker not supported, using direct EventSource (multi-tab will gracefully degrade)",
@@ -67,15 +69,17 @@ export default class nr extends t {
67
69
  "direct");
68
70
  }
69
71
  Ae() {
70
- this.We = new si({
71
- Be: (i) => {
72
- this.Ge(i);
72
+ this.We = new ei({
73
+ Ge: (i) => {
74
+ this.Pe(i);
73
75
  },
74
- Pe: () => {
75
- this.qe();
76
+ qe: () => {
77
+ this.He(), (this.Te = new Date().valueOf());
76
78
  },
77
- He: () => {},
78
- Je: (i) => {
79
+ Je: () => {
80
+ this.Te = null;
81
+ },
82
+ Le: (i) => {
79
83
  E.error(`Real-time messaging SharedWorker error: ${i}`);
80
84
  },
81
85
  });
@@ -86,22 +90,32 @@ export default class nr extends t {
86
90
  (this.Fe = "direct"),
87
91
  (this.We = null));
88
92
  }
89
- qe() {
90
- (this.Vi = 0),
93
+ He() {
94
+ (this._i = 0),
91
95
  (this.we = null),
92
- (this._i = !0),
96
+ (this.Zi = !0),
93
97
  (this.Ne = !1),
94
- (this.Te = 0);
98
+ (this.Be = 0);
99
+ }
100
+ Oe() {
101
+ var i;
102
+ return (
103
+ !(
104
+ !("sharedworker" === this.Fe
105
+ ? Boolean(null === (i = this.We) || void 0 === i ? void 0 : i.Ie())
106
+ : Boolean(this.Vi)) || null === this.Te
107
+ ) && new Date().valueOf() - this.Te >= ii
108
+ );
95
109
  }
96
- Ge(i) {
110
+ Pe(i) {
97
111
  if (!i.type)
98
112
  return void E.warn(
99
113
  `Received real-time message without type: ${JSON.stringify(i)}`,
100
114
  );
101
115
  const t = "sharedworker" === this.Fe ? "SharedWorker" : "Direct";
102
116
  E.info(`Received real-time message of type '${i.type}' via ${t}`);
103
- const s = this.Me.get(i.type);
104
- if (s && s.Le() > 0)
117
+ const s = this.De.get(i.type);
118
+ if (s && s.Ke() > 0)
105
119
  try {
106
120
  s.A(i);
107
121
  } catch (t) {
@@ -111,18 +125,18 @@ export default class nr extends t {
111
125
  }
112
126
  else E.info(`No subscribers for real-time message type '${i.type}'`);
113
127
  }
114
- Oe() {
128
+ Ve() {
115
129
  return "sharedworker" === this.Fe;
116
130
  }
117
- Ie() {
131
+ _e() {
118
132
  return this.Fe;
119
133
  }
120
134
  ze() {
121
135
  if (this.C) {
122
- const i = this.C.Rt(s.Tt.Ke),
123
- t = this.C.Rt(s.Tt.Qe),
124
- e = this.C.Rt(s.Tt.Ve),
125
- n = this.C.Rt(s.Tt.Xe);
136
+ const i = this.C.Rt(s.Tt.Qe),
137
+ t = this.C.Rt(s.Tt.Xe),
138
+ e = this.C.Rt(s.Tt.Ye),
139
+ n = this.C.Rt(s.Tt.Ze);
126
140
  i && t
127
141
  ? ((this.mite = i),
128
142
  (this.Wi = t),
@@ -133,63 +147,70 @@ export default class nr extends t {
133
147
  (E.warn(
134
148
  "Incomplete real-time messaging configuration in storage, clearing",
135
149
  ),
136
- this.Ye());
150
+ this.sn());
137
151
  }
138
152
  }
139
153
  Hr() {
140
- this.xe ||
141
- this.De ||
142
- ((this.xe = this.Ze("ddr", (i) => {
143
- if (!i.body || "number" != typeof i.body._e) return;
144
- const t = Math.random() * i.body._e * 0.3,
145
- s = Math.round(i.body._e + t),
146
- e = i.body.e ? ` (${i.body.e})` : "";
147
- E.info(`Admin requested disconnect${e}, reconnecting in ${s}ms`),
148
- this.sn(),
149
- setTimeout(() => this.en(), s);
150
- })),
151
- (this.De = this.Ze("ttl", (i) => {
154
+ this.Me ||
155
+ this.xe ||
156
+ ((this.Me = this.en("ddr", (i) => {
157
+ var t, s, e;
152
158
  if (!i.body || "number" != typeof i.body.on) return;
153
- const t = i.body.on;
159
+ const n =
160
+ (null === (t = this.h) || void 0 === t ? void 0 : t.vt()) || d,
161
+ r = (null === (s = this.h) || void 0 === s ? void 0 : s.gt()) || m,
162
+ o = (null === (e = this.h) || void 0 === e ? void 0 : e.bt()) || c,
163
+ h = Math.min(o, a(n, n * r)),
164
+ l = Math.round(i.body.on + h),
165
+ u = i.body.e ? ` (${i.body.e})` : "";
166
+ E.info(
167
+ `Admin requested disconnect${u}, reconnecting in ${l}ms (r_ms=${i.body.on} + backoff=${h})`,
168
+ ),
169
+ this.hn(),
170
+ setTimeout(() => this.an(), l);
171
+ })),
172
+ (this.xe = this.en("ttl", (i) => {
173
+ if (!i.body || "number" != typeof i.body.ln) return;
174
+ const t = i.body.ln;
154
175
  E.info(`Time to live set to ${t}ms, will reconnect when expired`),
155
176
  "sharedworker" !== this.Fe &&
156
- ("string" == typeof i.body.hn && (this.Ki = i.body.hn),
157
- null !== this.Zi && window.clearTimeout(this.Zi),
158
- (this.Zi = window.setTimeout(() => {
159
- (this.Zi = null),
177
+ ("string" == typeof i.body.cn && (this.Ki = i.body.cn),
178
+ null !== this.Yi && window.clearTimeout(this.Yi),
179
+ (this.Yi = window.setTimeout(() => {
180
+ (this.Yi = null),
160
181
  E.info("Time to live expired, performing gapless reconnection"),
161
- this.an();
182
+ this.un();
162
183
  }, t)));
163
184
  })));
164
185
  }
165
- ln() {
186
+ dn() {
166
187
  this.Se ||
167
188
  this.ke ||
168
189
  ((this.ye = () => {
169
- (this.Re = !0), (this._i = !1);
190
+ (this.Re = !0), (this.Zi = !1);
170
191
  }),
171
192
  window.addEventListener("beforeunload", this.ye),
172
193
  (this.Se = () => {
173
194
  var i;
174
195
  this.Re = !0;
175
- (this.Qi ||
176
- (null === (i = this.We) || void 0 === i ? void 0 : i.cn())) &&
196
+ (this.Vi ||
197
+ (null === (i = this.We) || void 0 === i ? void 0 : i.Ie())) &&
177
198
  (E.info("Page unloading, closing real-time connection gracefully"),
178
- (this._i = !1),
179
- this.sn());
199
+ (this.Zi = !1),
200
+ this.hn());
180
201
  }),
181
202
  window.addEventListener("pagehide", this.Se),
182
203
  (this.ke = (i) => {
183
204
  var t;
184
205
  const s =
185
- this.Qi || (null === (t = this.We) || void 0 === t ? void 0 : t.cn());
206
+ this.Vi || (null === (t = this.We) || void 0 === t ? void 0 : t.Ie());
186
207
  i.persisted &&
187
- this.un() &&
208
+ this.gn() &&
188
209
  !s &&
189
210
  (E.info("Page restored from bfcache, reconnecting"),
190
211
  (this.Re = !1),
191
- this.qe(),
192
- this.en());
212
+ this.He(),
213
+ this.an());
193
214
  }),
194
215
  window.addEventListener("pageshow", this.ke));
195
216
  }
@@ -199,35 +220,35 @@ export default class nr extends t {
199
220
  $t(i) {
200
221
  this.T = i;
201
222
  }
202
- Ze(i, t) {
223
+ en(i, t) {
203
224
  if ("function" != typeof t) return null;
204
- let s = this.Me.get(i);
205
- return s || ((s = new f()), this.Me.set(i, s), r.S(s)), s.Kt(t);
225
+ let s = this.De.get(i);
226
+ return s || ((s = new f()), this.De.set(i, s), r.S(s)), s.Kt(t);
206
227
  }
207
- dn(i, t) {
208
- const s = this.Me.get(i);
228
+ mn(i, t) {
229
+ const s = this.De.get(i);
209
230
  s && s.removeSubscription(t);
210
231
  }
211
- un() {
232
+ gn() {
212
233
  return Boolean(this.mite && this.Wi);
213
234
  }
214
- gn() {
235
+ fn() {
215
236
  if (!this.Hi) return !1;
216
237
  return Math.floor(new Date().valueOf() / 1e3) >= this.Hi;
217
238
  }
218
- Ye() {
239
+ sn() {
219
240
  (this.mite = null),
220
241
  (this.Wi = null),
221
242
  (this.Gi = null),
222
243
  (this.Hi = null),
223
244
  (this.Ki = null),
224
245
  this.C &&
225
- (this.C.Qt(s.Tt.Ke),
226
- this.C.Qt(s.Tt.Qe),
227
- this.C.Qt(s.Tt.Ve),
228
- this.C.Qt(s.Tt.Xe));
246
+ (this.C.Qt(s.Tt.Qe),
247
+ this.C.Qt(s.Tt.Xe),
248
+ this.C.Qt(s.Tt.Ye),
249
+ this.C.Qt(s.Tt.Ze));
229
250
  }
230
- mn(i, t) {
251
+ pn(i, t) {
231
252
  const e = () => {
232
253
  "function" == typeof t && t();
233
254
  },
@@ -237,17 +258,17 @@ export default class nr extends t {
237
258
  return (
238
259
  E.error("NetworkManager or StorageManager not available"), void e()
239
260
  );
240
- if (!this.h || !this.h.fn())
261
+ if (!this.h || !this.h.vn())
241
262
  return (
242
263
  E.info("Real-time messaging is not enabled, skipping refresh"), void e()
243
264
  );
244
- this.un()
265
+ this.gn()
245
266
  ? E.info("Refreshing real-time messaging configuration")
246
267
  : E.info("Fetching initial real-time messaging configuration");
247
268
  const o = n.Z({}, !0),
248
- a = n.tt(o, h.it.pn, !1),
269
+ a = n.tt(o, h.it.wn),
249
270
  c = new Date().valueOf();
250
- h.nt(r, h.it.pn, c),
271
+ h.nt(r, h.it.wn, c),
251
272
  l.ot({
252
273
  url: `${n.ht()}/dust/config`,
253
274
  headers: a,
@@ -269,16 +290,16 @@ export default class nr extends t {
269
290
  E.info(
270
291
  "Received real-time messaging configuration from server",
271
292
  ),
272
- r.It(s.Tt.Ke, t.mite),
273
- r.It(s.Tt.Qe, t.host),
274
- t.auth ? r.It(s.Tt.Ve, t.auth) : r.Qt(s.Tt.Ve),
275
- t.expiration ? r.It(s.Tt.Xe, t.expiration) : r.Qt(s.Tt.Xe),
276
- this.en(),
293
+ r.It(s.Tt.Qe, t.mite),
294
+ r.It(s.Tt.Xe, t.host),
295
+ t.auth ? r.It(s.Tt.Ye, t.auth) : r.Qt(s.Tt.Ye),
296
+ t.expiration ? r.It(s.Tt.Ze, t.expiration) : r.Qt(s.Tt.Ze),
297
+ this.an(),
277
298
  "function" == typeof i && i())
278
299
  : (E.info(
279
300
  "Real-time messaging configuration not available - this SDK version may not be supported",
280
301
  ),
281
- this.Ye(),
302
+ this.sn(),
282
303
  e());
283
304
  },
284
305
  error: (i) => {
@@ -286,20 +307,20 @@ export default class nr extends t {
286
307
  },
287
308
  });
288
309
  }
289
- en() {
290
- if (!this.h || !this.h.fn()) return;
291
- if (!this.un())
310
+ an() {
311
+ if (!this.h || !this.h.vn()) return;
312
+ if (!this.gn())
292
313
  return void E.error(
293
314
  "Cannot start real-time subscription without configuration",
294
315
  );
295
- if (this.gn())
316
+ if (this.fn())
296
317
  return (
297
318
  E.info(
298
319
  "Real-time messaging auth token has expired, refreshing configuration",
299
320
  ),
300
- void this.mn(
321
+ void this.pn(
301
322
  () => {
302
- this.en();
323
+ this.an();
303
324
  },
304
325
  () => {
305
326
  E.error(
@@ -313,25 +334,25 @@ export default class nr extends t {
313
334
  if (i && t)
314
335
  switch (this.Fe) {
315
336
  case "sharedworker":
316
- this.vn(i, t);
337
+ this.bn(i, t);
317
338
  break;
318
339
  case "direct":
319
- this.Qi &&
340
+ this.Vi &&
320
341
  (E.info(
321
342
  "Real-time connection already exists, closing before starting new subscription",
322
343
  ),
323
- this.sn()),
324
- this.wn();
344
+ this.hn()),
345
+ this.Sn();
325
346
  }
326
347
  }
327
- bn() {
348
+ kn() {
328
349
  const i = this.mite,
329
350
  t = this.Oi || this.Wi;
330
351
  return i && t
331
- ? ti(t, i, this.Vi, this.Gi || void 0, this.Ki || void 0)
352
+ ? si(t, i, this._i, this.Gi || void 0, this.Ki || void 0)
332
353
  : null;
333
354
  }
334
- vn(i, t) {
355
+ bn(i, t) {
335
356
  var s, e, n;
336
357
  if ((this.We || this.Ae(), !this.We))
337
358
  return (
@@ -339,7 +360,7 @@ export default class nr extends t {
339
360
  "SharedWorker initialization failed, falling back to direct EventSource",
340
361
  ),
341
362
  (this.Fe = "direct"),
342
- void this.Sn()
363
+ void this.yn()
343
364
  );
344
365
  this.Oi && E.info(`Using custom real-time messaging host: ${this.Oi}`),
345
366
  E.info("Starting real-time subscription via SharedWorker");
@@ -350,19 +371,19 @@ export default class nr extends t {
350
371
  mite: i,
351
372
  Wi: t,
352
373
  auth: this.Gi || void 0,
353
- kn: { yn: r, $n: o, Rn: h },
374
+ $n: { Rn: r, Cn: o, Dn: h },
354
375
  });
355
376
  }
356
- Sn() {
357
- this.Qi &&
377
+ yn() {
378
+ this.Vi &&
358
379
  (E.info(
359
380
  "Real-time connection already exists, closing before starting new subscription",
360
381
  ),
361
- this.Cn()),
362
- this.wn();
382
+ this.Mn()),
383
+ this.Sn();
363
384
  }
364
- wn(i) {
365
- const t = this.bn();
385
+ Sn(i) {
386
+ const t = this.kn();
366
387
  if (t) {
367
388
  this.Oi && E.info(`Using custom real-time messaging host: ${this.Oi}`);
368
389
  try {
@@ -374,14 +395,15 @@ export default class nr extends t {
374
395
  ),
375
396
  i.close())
376
397
  : E.info("Real-time messaging connection established"),
377
- (this.Qi = s),
378
- (this.Vi = 0),
398
+ (this.Vi = s),
399
+ (this._i = 0),
379
400
  (this.we = null),
380
- (this._i = !0),
381
- (this.Ne = !0);
401
+ (this.Zi = !0),
402
+ (this.Ne = !0),
403
+ (this.Te = new Date().valueOf());
382
404
  }),
383
405
  s.addEventListener("msg", (i) => {
384
- this.Mn(i.data);
406
+ this.xn(i.data);
385
407
  }),
386
408
  (s.onerror = () => {
387
409
  const t = s.readyState;
@@ -390,25 +412,25 @@ export default class nr extends t {
390
412
  (0 === t
391
413
  ? E.info("Real-time messaging failed to connect")
392
414
  : E.info("Real-time messaging connection lost")),
393
- i && this.Qi !== s
415
+ i && this.Vi !== s
394
416
  ? (E.info(
395
417
  "Gapless reconnection failed, keeping old connection",
396
418
  ),
397
419
  s.close(),
398
- void (this._i && this.Vi < this.Xi && this.xn()))
399
- : (this.Cn(),
400
- this.Ne && (this.Te++, this.Te > 1)
420
+ void (this.Zi && this._i < this.Qi && this.jn()))
421
+ : (this.Mn(),
422
+ this.Ne && (this.Be++, this.Be > 1)
401
423
  ? (E.info(
402
424
  "Real-time messaging connection lost twice after successful connect (likely multi-tab conflict), yielding to other tab",
403
425
  ),
404
- void (this._i = !1))
405
- : void (this._i && this.Vi < this.Xi
406
- ? this.xn()
407
- : (this.Vi >= this.Xi &&
426
+ void (this.Zi = !1))
427
+ : void (this.Zi && this._i < this.Qi
428
+ ? this.jn()
429
+ : (this._i >= this.Qi &&
408
430
  E.error(
409
- `Max retry attempts (${this.Xi}) reached for real-time messaging, giving up for current session`,
431
+ `Max retry attempts (${this.Qi}) reached for real-time messaging, giving up for current session`,
410
432
  ),
411
- (this._i = !1))))
433
+ (this.Zi = !1))))
412
434
  );
413
435
  });
414
436
  } catch (i) {
@@ -420,9 +442,9 @@ export default class nr extends t {
420
442
  }
421
443
  }
422
444
  }
423
- xn() {
445
+ jn() {
424
446
  var i, t, s;
425
- this.Vi++;
447
+ this._i++;
426
448
  const e = (null === (i = this.h) || void 0 === i ? void 0 : i.vt()) || d,
427
449
  n = (null === (t = this.h) || void 0 === t ? void 0 : t.gt()) || m,
428
450
  r = (null === (s = this.h) || void 0 === s ? void 0 : s.bt()) || c;
@@ -431,48 +453,49 @@ export default class nr extends t {
431
453
  const h = Math.min(r, a(e, o * n));
432
454
  (this.we = h),
433
455
  E.info(
434
- `Retrying real-time messaging connection in ${h}ms (attempt ${this.Vi}/${this.Xi})`,
456
+ `Retrying real-time messaging connection in ${h}ms (attempt ${this._i}/${this.Qi})`,
435
457
  ),
436
- (this.Yi = window.setTimeout(() => {
437
- (this.Yi = null), this.en();
458
+ (this.Xi = window.setTimeout(() => {
459
+ (this.Xi = null), this.an();
438
460
  }, h));
439
461
  }
440
- an() {
441
- if (this.un()) {
442
- if (this.gn())
462
+ un() {
463
+ if (this.gn()) {
464
+ if (this.fn())
443
465
  return (
444
466
  E.info(
445
467
  "Auth token expired during gapless reconnection, falling back to regular reconnection",
446
468
  ),
447
- this.sn(),
448
- void this.en()
469
+ this.hn(),
470
+ void this.an()
449
471
  );
450
- null !== this.Zi && (window.clearTimeout(this.Zi), (this.Zi = null)),
451
- this.wn(this.Qi || void 0);
472
+ null !== this.Yi && (window.clearTimeout(this.Yi), (this.Yi = null)),
473
+ this.Sn(this.Vi || void 0);
452
474
  } else E.error("Cannot perform gapless reconnection without configuration");
453
475
  }
454
- sn() {
476
+ hn() {
455
477
  var i;
456
- switch (this.Fe) {
478
+ switch (((this.Te = null), this.Fe)) {
457
479
  case "sharedworker":
458
480
  null === (i = this.We) || void 0 === i || i.disconnect();
459
481
  break;
460
482
  case "direct":
461
- this.Cn();
483
+ this.Mn();
462
484
  }
463
485
  }
464
- Cn() {
465
- null !== this.Yi && (window.clearTimeout(this.Yi), (this.Yi = null)),
466
- null !== this.Zi && (window.clearTimeout(this.Zi), (this.Zi = null)),
467
- this.Qi &&
468
- (this.Qi.close(),
469
- (this.Qi = null),
486
+ Mn() {
487
+ null !== this.Xi && (window.clearTimeout(this.Xi), (this.Xi = null)),
488
+ null !== this.Yi && (window.clearTimeout(this.Yi), (this.Yi = null)),
489
+ (this.Te = null),
490
+ this.Vi &&
491
+ (this.Vi.close(),
492
+ (this.Vi = null),
470
493
  E.info("Real-time messaging connection closed"));
471
494
  }
472
- Mn(i) {
495
+ xn(i) {
473
496
  try {
474
497
  const t = JSON.parse(i);
475
- this.Ge(t);
498
+ this.Pe(t);
476
499
  } catch (i) {
477
500
  E.warn(
478
501
  `Failed to parse real-time message: ${
@@ -482,33 +505,33 @@ export default class nr extends t {
482
505
  }
483
506
  }
484
507
  changeUser(i = !1) {
485
- this.sn(),
508
+ this.hn(),
486
509
  i ||
487
- (this.un() &&
510
+ (this.gn() &&
488
511
  E.info(
489
512
  "Clearing cached real-time messaging configuration for user change",
490
513
  ),
491
- this.Ye()),
492
- this.qe();
514
+ this.sn()),
515
+ this.He();
493
516
  }
494
517
  clearData(i = !1) {
495
- (this._i = !1),
496
- this.sn(),
518
+ (this.Zi = !1),
519
+ this.hn(),
497
520
  i &&
498
- (this.un() &&
521
+ (this.gn() &&
499
522
  E.info(
500
523
  "Clearing cached real-time messaging configuration (wipeData)",
501
524
  ),
502
- this.Ye()),
503
- this.qe();
525
+ this.sn()),
526
+ this.He();
504
527
  }
505
528
  destroy() {
506
- (this._i = !1),
529
+ (this.Zi = !1),
530
+ this.hn(),
507
531
  this.sn(),
508
- this.Ye(),
509
532
  this.We && (this.We.destroy(), (this.We = null)),
510
- this.xe && (this.dn("ddr", this.xe), (this.xe = null)),
511
- this.De && (this.dn("ttl", this.De), (this.De = null)),
533
+ this.Me && (this.mn("ddr", this.Me), (this.Me = null)),
534
+ this.xe && (this.mn("ttl", this.xe), (this.xe = null)),
512
535
  this.ye &&
513
536
  (window.removeEventListener("beforeunload", this.ye), (this.ye = null)),
514
537
  this.Se &&
@@ -1,2 +1,2 @@
1
1
  export const DUST_SHARED_WORKER_CODE =
2
- '\n"use strict";\nconst workerSelf = self;\nlet eventSource = null;\nlet currentConfig = null;\nlet retryCount = 0;\nlet retryTimeoutId = null;\nlet connectionInProgress = false;\nconst maxRetries = 5;\nconst connectedPorts = new Map();\nlet lastSleepMs = null;\nlet currentRcs = null;\nlet ttlTimeoutId = null;\nlet leaderPortId = null;\nlet ddrTimeoutId = null;\nconst fn = {\n startConnection: null,\n handleMessage: null,\n};\nfunction broadcast(message) {\n connectedPorts.forEach((portInfo, portId) => {\n try {\n portInfo.port.postMessage(message);\n }\n catch (_a) {\n connectedPorts.delete(portId);\n }\n });\n}\nfunction randomInclusive(min, max) {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\nfunction electLeader() {\n if (leaderPortId && connectedPorts.has(leaderPortId)) {\n return;\n }\n const firstPortId = connectedPorts.keys().next().value;\n leaderPortId = firstPortId || null;\n if (leaderPortId) {\n console.log("[Braze Real-Time] Elected leader port:", leaderPortId);\n }\n}\nfunction promoteToLeader(portId) {\n if (connectedPorts.has(portId) && leaderPortId !== portId) {\n leaderPortId = portId;\n console.log("[Braze Real-Time] Promoted to leader (tab became active):", portId);\n }\n}\nfunction sendToLeader(message) {\n electLeader();\n if (!leaderPortId) {\n console.warn("[Braze Real-Time] No leader to send message to");\n return;\n }\n const leader = connectedPorts.get(leaderPortId);\n if (leader) {\n try {\n leader.port.postMessage(message);\n }\n catch (_a) {\n connectedPorts.delete(leaderPortId);\n leaderPortId = null;\n sendToLeader(message);\n }\n }\n}\nfunction closeConnection() {\n connectionInProgress = false;\n if (retryTimeoutId !== null) {\n clearTimeout(retryTimeoutId);\n retryTimeoutId = null;\n }\n if (ttlTimeoutId !== null) {\n clearTimeout(ttlTimeoutId);\n ttlTimeoutId = null;\n }\n if (ddrTimeoutId !== null) {\n clearTimeout(ddrTimeoutId);\n ddrTimeoutId = null;\n }\n if (eventSource) {\n eventSource.close();\n eventSource = null;\n broadcast({ type: "disconnected" });\n }\n}\nfunction retryWithBackoff() {\n if (!currentConfig) {\n return;\n }\n retryCount++;\n const { minSleepMs, maxSleepMs, scaleFactor } = currentConfig.backoff;\n let previousSleepMs = lastSleepMs;\n if (previousSleepMs == null || previousSleepMs < minSleepMs) {\n previousSleepMs = minSleepMs;\n }\n const backoffMs = Math.min(maxSleepMs, randomInclusive(minSleepMs, previousSleepMs * scaleFactor));\n lastSleepMs = backoffMs;\n console.log("[Braze Real-Time] Retrying in " + backoffMs + "ms (attempt " + retryCount + "/" + maxRetries + ")");\n retryTimeoutId = setTimeout(function () {\n retryTimeoutId = null;\n fn.startConnection();\n }, backoffMs);\n}\nfunction startConnection(oldEventSourceToClose) {\n if (!currentConfig) {\n return;\n }\n if (eventSource && !oldEventSourceToClose) {\n console.warn("[Braze Real-Time] Connection already exists");\n return;\n }\n if (connectionInProgress && !oldEventSourceToClose) {\n console.warn("[Braze Real-Time] Connection attempt already in progress");\n return;\n }\n connectionInProgress = true;\n const { dustHost, mite, auth } = currentConfig;\n const dustHostWithScheme = /^https?:\\/\\//i.test(dustHost) ? dustHost : "https://" + dustHost;\n let queryString = "mite=" + encodeURIComponent(mite) + "&attempts=" + retryCount;\n if (auth) {\n queryString += "&auth=" + encodeURIComponent(auth);\n }\n if (currentRcs) {\n queryString += "&rcs=" + encodeURIComponent(currentRcs);\n }\n const subscribeUrl = dustHostWithScheme + "/sse?" + queryString;\n try {\n const newEventSource = new EventSource(subscribeUrl);\n newEventSource.onopen = function () {\n if (oldEventSourceToClose) {\n console.log("[Braze Real-Time] Gapless reconnection: new connection established, closing old connection");\n oldEventSourceToClose.close();\n }\n else {\n console.log("[Braze Real-Time] Connection established");\n }\n eventSource = newEventSource;\n connectionInProgress = false;\n retryCount = 0;\n lastSleepMs = null;\n broadcast({ type: "connected" });\n };\n newEventSource.addEventListener("msg", function (event) {\n fn.handleMessage(event.data);\n });\n newEventSource.onerror = function () {\n const readyState = newEventSource ? newEventSource.readyState : -1;\n if (readyState === 0) {\n console.log("[Braze Real-Time] Failed to connect");\n }\n else {\n console.log("[Braze Real-Time] Connection lost");\n }\n if (oldEventSourceToClose && eventSource !== newEventSource) {\n console.log("[Braze Real-Time] Gapless reconnection failed, keeping old connection");\n newEventSource.close();\n connectionInProgress = false;\n if (retryCount < maxRetries) {\n retryWithBackoff();\n }\n return;\n }\n closeConnection();\n if (retryCount < maxRetries) {\n retryWithBackoff();\n }\n else {\n console.error("[Braze Real-Time] Max retries reached");\n broadcast({ type: "error", error: "Max retry attempts reached" });\n }\n };\n }\n catch (error) {\n connectionInProgress = false;\n console.error("[Braze Real-Time] Failed to create EventSource:", error);\n broadcast({ type: "error", error: String(error) });\n }\n}\nfunction handleTtlMessage(tMs, rcs) {\n if (typeof tMs !== "number") {\n return;\n }\n if (typeof rcs === "string") {\n currentRcs = rcs;\n }\n console.log("[Braze Real-Time] TTL set to " + tMs + "ms, will perform gapless reconnection when expired");\n if (ttlTimeoutId !== null) {\n clearTimeout(ttlTimeoutId);\n }\n ttlTimeoutId = setTimeout(function () {\n ttlTimeoutId = null;\n console.log("[Braze Real-Time] TTL expired, performing gapless reconnection");\n startConnection(eventSource || undefined);\n }, tMs);\n}\nfunction handleDdrMessage(rMs, reason) {\n if (typeof rMs !== "number") {\n return;\n }\n const jitter = Math.random() * rMs * 0.3;\n const waitMs = Math.round(rMs + jitter);\n const reasonStr = reason ? " (" + reason + ")" : "";\n console.log("[Braze Real-Time] Admin requested disconnect" + reasonStr + ", reconnecting in " + waitMs + "ms");\n if (ddrTimeoutId !== null) {\n clearTimeout(ddrTimeoutId);\n }\n closeConnection();\n ddrTimeoutId = setTimeout(function () {\n ddrTimeoutId = null;\n fn.startConnection();\n }, waitMs);\n}\nfunction handleMessage(data) {\n try {\n const message = JSON.parse(data);\n if (!message.type) {\n console.warn("[Braze Real-Time] Message without type:", message);\n return;\n }\n if (message.type === "ttl" && message.body) {\n handleTtlMessage(message.body.t_ms, message.body.rcs);\n return;\n }\n if (message.type === "ddr" && message.body) {\n handleDdrMessage(message.body.r_ms, message.body.e);\n return;\n }\n console.log("[Braze Real-Time] Routing \'" + message.type + "\' message to leader");\n sendToLeader({ type: "message", data: message });\n }\n catch (error) {\n console.warn("[Braze Real-Time] Failed to parse message:", error);\n }\n}\nfn.startConnection = startConnection;\nfn.handleMessage = handleMessage;\nfunction handlePortMessage(port, portId, message) {\n switch (message.type) {\n case "connect":\n if (currentConfig &&\n (currentConfig.mite !== message.config.mite || currentConfig.dustHost !== message.config.dustHost)) {\n console.log("[Braze Real-Time] Config changed, reconnecting");\n closeConnection();\n retryCount = 0;\n lastSleepMs = null;\n currentRcs = null;\n }\n currentConfig = message.config;\n electLeader();\n if (!eventSource && !connectionInProgress) {\n startConnection();\n }\n else if (eventSource) {\n port.postMessage({ type: "connected" });\n }\n break;\n case "disconnect":\n connectedPorts.delete(portId);\n if (portId === leaderPortId) {\n leaderPortId = null;\n electLeader();\n }\n if (connectedPorts.size === 0) {\n console.log("[Braze Real-Time] No more ports, closing connection");\n closeConnection();\n currentConfig = null;\n currentRcs = null;\n leaderPortId = null;\n }\n break;\n case "tab_active":\n promoteToLeader(portId);\n break;\n case "ping":\n port.postMessage({ type: "pong" });\n break;\n default:\n console.warn("[Braze Real-Time] Unknown message type:", message.type);\n }\n}\nworkerSelf.onconnect = function (event) {\n const port = event.ports[0];\n const portId = "port-" + Date.now() + "-" + Math.random().toString(36).substr(2, 9);\n connectedPorts.set(portId, { port: port });\n port.onmessage = function (messageEvent) {\n try {\n handlePortMessage(port, portId, messageEvent.data);\n }\n catch (error) {\n console.error("[Braze Real-Time] Error handling message:", error);\n }\n };\n port.onmessageerror = function () {\n console.warn("[Braze Real-Time] Message error from port:", portId);\n connectedPorts.delete(portId);\n };\n port.start();\n};\n';
2
+ '\n"use strict";\nconst workerSelf = self;\nlet eventSource = null;\nlet currentConfig = null;\nlet retryCount = 0;\nlet retryTimeoutId = null;\nlet connectionInProgress = false;\nconst maxRetries = 5;\nconst connectedPorts = new Map();\nlet lastSleepMs = null;\nlet currentRcs = null;\nlet ttlTimeoutId = null;\nlet leaderPortId = null;\nlet ddrTimeoutId = null;\nconst fn = {\n startConnection: null,\n handleMessage: null,\n};\nfunction broadcast(message) {\n connectedPorts.forEach((portInfo, portId) => {\n try {\n portInfo.port.postMessage(message);\n }\n catch (_a) {\n connectedPorts.delete(portId);\n }\n });\n}\nfunction randomInclusive(min, max) {\n return Math.floor(Math.random() * (max - min + 1)) + min;\n}\nfunction electLeader() {\n if (leaderPortId && connectedPorts.has(leaderPortId)) {\n return;\n }\n const firstPortId = connectedPorts.keys().next().value;\n leaderPortId = firstPortId || null;\n if (leaderPortId) {\n console.log("[Braze Real-Time] Elected leader port:", leaderPortId);\n }\n}\nfunction promoteToLeader(portId) {\n if (connectedPorts.has(portId) && leaderPortId !== portId) {\n leaderPortId = portId;\n console.log("[Braze Real-Time] Promoted to leader (tab became active):", portId);\n }\n}\nfunction sendToLeader(message) {\n electLeader();\n if (!leaderPortId) {\n console.warn("[Braze Real-Time] No leader to send message to");\n return;\n }\n const leader = connectedPorts.get(leaderPortId);\n if (leader) {\n try {\n leader.port.postMessage(message);\n }\n catch (_a) {\n connectedPorts.delete(leaderPortId);\n leaderPortId = null;\n sendToLeader(message);\n }\n }\n}\nfunction closeConnection() {\n connectionInProgress = false;\n if (retryTimeoutId !== null) {\n clearTimeout(retryTimeoutId);\n retryTimeoutId = null;\n }\n if (ttlTimeoutId !== null) {\n clearTimeout(ttlTimeoutId);\n ttlTimeoutId = null;\n }\n if (ddrTimeoutId !== null) {\n clearTimeout(ddrTimeoutId);\n ddrTimeoutId = null;\n }\n if (eventSource) {\n eventSource.close();\n eventSource = null;\n broadcast({ type: "disconnected" });\n }\n}\nfunction retryWithBackoff() {\n if (!currentConfig) {\n return;\n }\n retryCount++;\n const { minSleepMs, maxSleepMs, scaleFactor } = currentConfig.backoff;\n let previousSleepMs = lastSleepMs;\n if (previousSleepMs == null || previousSleepMs < minSleepMs) {\n previousSleepMs = minSleepMs;\n }\n const backoffMs = Math.min(maxSleepMs, randomInclusive(minSleepMs, previousSleepMs * scaleFactor));\n lastSleepMs = backoffMs;\n console.log(`[Braze Real-Time] Retrying in ${backoffMs}ms (attempt ${retryCount}/${maxRetries})`);\n retryTimeoutId = setTimeout(function () {\n retryTimeoutId = null;\n fn.startConnection();\n }, backoffMs);\n}\nfunction startConnection(oldEventSourceToClose) {\n if (!currentConfig) {\n return;\n }\n if (eventSource && !oldEventSourceToClose) {\n console.warn("[Braze Real-Time] Connection already exists");\n return;\n }\n if (connectionInProgress && !oldEventSourceToClose) {\n console.warn("[Braze Real-Time] Connection attempt already in progress");\n return;\n }\n connectionInProgress = true;\n const { dustHost, mite, auth } = currentConfig;\n const dustHostWithScheme = /^https?:\\/\\//i.test(dustHost) ? dustHost : `https://${dustHost}`;\n let queryString = `mite=${encodeURIComponent(mite)}&attempts=${retryCount}`;\n if (auth) {\n queryString += `&auth=${encodeURIComponent(auth)}`;\n }\n if (currentRcs) {\n queryString += `&rcs=${encodeURIComponent(currentRcs)}`;\n }\n const subscribeUrl = `${dustHostWithScheme}/sse?${queryString}`;\n try {\n const newEventSource = new EventSource(subscribeUrl);\n newEventSource.onopen = function () {\n if (oldEventSourceToClose) {\n console.log("[Braze Real-Time] Gapless reconnection: new connection established, closing old connection");\n oldEventSourceToClose.close();\n }\n else {\n console.log("[Braze Real-Time] Connection established");\n }\n eventSource = newEventSource;\n connectionInProgress = false;\n retryCount = 0;\n lastSleepMs = null;\n broadcast({ type: "connected" });\n };\n newEventSource.addEventListener("msg", function (event) {\n fn.handleMessage(event.data);\n });\n newEventSource.onerror = function () {\n const readyState = newEventSource ? newEventSource.readyState : -1;\n if (readyState === 0) {\n console.log("[Braze Real-Time] Failed to connect");\n }\n else {\n console.log("[Braze Real-Time] Connection lost");\n }\n if (oldEventSourceToClose && eventSource !== newEventSource) {\n console.log("[Braze Real-Time] Gapless reconnection failed, keeping old connection");\n newEventSource.close();\n connectionInProgress = false;\n if (retryCount < maxRetries) {\n retryWithBackoff();\n }\n return;\n }\n closeConnection();\n if (retryCount < maxRetries) {\n retryWithBackoff();\n }\n else {\n console.error("[Braze Real-Time] Max retries reached");\n broadcast({ type: "error", error: "Max retry attempts reached" });\n }\n };\n }\n catch (error) {\n connectionInProgress = false;\n console.error("[Braze Real-Time] Failed to create EventSource:", error);\n broadcast({ type: "error", error: String(error) });\n }\n}\nfunction handleTtlMessage(tMs, rcs) {\n if (typeof tMs !== "number") {\n return;\n }\n if (typeof rcs === "string") {\n currentRcs = rcs;\n }\n console.log(`[Braze Real-Time] TTL set to ${tMs}ms, will perform gapless reconnection when expired`);\n if (ttlTimeoutId !== null) {\n clearTimeout(ttlTimeoutId);\n }\n ttlTimeoutId = setTimeout(function () {\n ttlTimeoutId = null;\n console.log("[Braze Real-Time] TTL expired, performing gapless reconnection");\n startConnection(eventSource || undefined);\n }, tMs);\n}\nfunction handleDdrMessage(rMs, reason) {\n if (typeof rMs !== "number") {\n return;\n }\n const backoffConfig = currentConfig === null || currentConfig === void 0 ? void 0 : currentConfig.backoff;\n const minSleepMs = (backoffConfig === null || backoffConfig === void 0 ? void 0 : backoffConfig.minSleepMs) || 10000;\n const scaleFactor = (backoffConfig === null || backoffConfig === void 0 ? void 0 : backoffConfig.scaleFactor) || 3;\n const maxSleepMs = (backoffConfig === null || backoffConfig === void 0 ? void 0 : backoffConfig.maxSleepMs) || 300000;\n const backoffMs = Math.min(maxSleepMs, randomInclusive(minSleepMs, minSleepMs * scaleFactor));\n const waitMs = Math.round(rMs + backoffMs);\n const reasonStr = reason ? ` (${reason})` : "";\n console.log(`[Braze Real-Time] Admin requested disconnect${reasonStr}, reconnecting in ${waitMs}ms (r_ms=${rMs} + backoff=${backoffMs})`);\n if (ddrTimeoutId !== null) {\n clearTimeout(ddrTimeoutId);\n }\n closeConnection();\n ddrTimeoutId = setTimeout(function () {\n ddrTimeoutId = null;\n fn.startConnection();\n }, waitMs);\n}\nfunction handleMessage(data) {\n try {\n const message = JSON.parse(data);\n if (!message.type) {\n console.warn("[Braze Real-Time] Message without type:", message);\n return;\n }\n if (message.type === "ttl" && message.body) {\n handleTtlMessage(message.body.t_ms, message.body.rcs);\n return;\n }\n if (message.type === "ddr" && message.body) {\n handleDdrMessage(message.body.r_ms, message.body.e);\n return;\n }\n console.log(`[Braze Real-Time] Routing \'${message.type}\' message to leader`);\n sendToLeader({ type: "message", data: message });\n }\n catch (error) {\n console.warn("[Braze Real-Time] Failed to parse message:", error);\n }\n}\nfn.startConnection = startConnection;\nfn.handleMessage = handleMessage;\nfunction handlePortMessage(port, portId, message) {\n switch (message.type) {\n case "connect":\n if (currentConfig &&\n (currentConfig.mite !== message.config.mite || currentConfig.dustHost !== message.config.dustHost)) {\n console.log("[Braze Real-Time] Config changed, reconnecting");\n closeConnection();\n retryCount = 0;\n lastSleepMs = null;\n currentRcs = null;\n }\n currentConfig = message.config;\n electLeader();\n if (!eventSource && !connectionInProgress) {\n startConnection();\n }\n else if (eventSource) {\n port.postMessage({ type: "connected" });\n }\n break;\n case "disconnect":\n connectedPorts.delete(portId);\n if (portId === leaderPortId) {\n leaderPortId = null;\n electLeader();\n }\n if (connectedPorts.size === 0) {\n console.log("[Braze Real-Time] No more ports, closing connection");\n closeConnection();\n currentConfig = null;\n currentRcs = null;\n leaderPortId = null;\n }\n break;\n case "tab_active":\n promoteToLeader(portId);\n break;\n case "ping":\n port.postMessage({ type: "pong" });\n break;\n default:\n console.warn("[Braze Real-Time] Unknown message type:", message.type);\n }\n}\nworkerSelf.onconnect = function (event) {\n const port = event.ports[0];\n const portId = `port-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n connectedPorts.set(portId, { port: port });\n port.onmessage = function (messageEvent) {\n try {\n handlePortMessage(port, portId, messageEvent.data);\n }\n catch (error) {\n console.error("[Braze Real-Time] Error handling message:", error);\n }\n };\n port.onmessageerror = function () {\n console.warn("[Braze Real-Time] Message error from port:", portId);\n connectedPorts.delete(portId);\n };\n port.start();\n};\n';