@braze/web-sdk
Version:
Braze SDK for web sites and other JS platforms.
235 lines (234 loc) • 6.88 kB
JavaScript
const oe = self;
let te = null,
le = null,
ce = 0,
ae = null,
ie = !1;
const se = new Map();
let ue = null,
me = null,
de = null,
fe = null,
pe = null;
const Te = { Tn: null, qe: null };
function Re(e) {
se.forEach((n, o) => {
try {
n.port.postMessage(e);
} catch (e) {
se.delete(o);
}
});
}
function a(e, n) {
return Math.floor(Math.random() * (n - e + 1)) + e;
}
function ge() {
if (fe && se.has(fe)) return;
const e = se.keys().next().value;
(fe = e || null),
fe && console.log("[Braze Real-Time] Elected leader port:", fe);
}
function he(e) {
if ((ge(), !fe))
return void console.warn("[Braze Real-Time] No leader to send message to");
const n = se.get(fe);
if (n)
try {
n.port.postMessage(e);
} catch (n) {
se.delete(fe), (fe = null), he(e);
}
}
function ze() {
(ie = !1),
null !== ae && (clearTimeout(ae), (ae = null)),
null !== de && (clearTimeout(de), (de = null)),
null !== pe && (clearTimeout(pe), (pe = null)),
te && (te.close(), (te = null), Re({ type: "disconnected" }));
}
function Be() {
if (!le) return;
ce++;
const { Rn: e, zn: n, Bn: o } = le.Mn;
let t = ue;
(null == t || t < e) && (t = e);
const l = Math.min(n, a(e, t * o));
(ue = l),
console.log(`[Braze Real-Time] Retrying in ${l}ms (attempt ${ce}/5)`),
(ae = setTimeout(function () {
(ae = null), Te.Tn();
}, l));
}
function ye(e) {
if (!le) return;
if (te && !e)
return void console.warn("[Braze Real-Time] Connection already exists");
if (ie && !e)
return void console.warn(
"[Braze Real-Time] Connection attempt already in progress",
);
ie = !0;
const { Wi: n, mite: o, auth: t } = le,
l = /^https?:\/\//i.test(n) ? n : `https://${n}`;
let c = `mite=${encodeURIComponent(o)}&attempts=${ce}`;
t && (c += `&auth=${encodeURIComponent(t)}`),
me && (c += `&rcs=${encodeURIComponent(me)}`);
const r = `${l}/sse?${c}`;
try {
const n = new EventSource(r);
(n.onopen = function () {
e
? (console.log(
"[Braze Real-Time] Gapless reconnection: new connection established, closing old connection",
),
e.close())
: console.log("[Braze Real-Time] Connection established"),
(te = n),
(ie = !1),
(ce = 0),
(ue = null),
Re({ type: "connected" });
}),
n.addEventListener("msg", function (e) {
Te.qe(e.data);
}),
(n.onerror = function () {
if (
(0 === (n ? n.readyState : -1)
? console.log("[Braze Real-Time] Failed to connect")
: console.log("[Braze Real-Time] Connection lost"),
e && te !== n)
)
return (
console.log(
"[Braze Real-Time] Gapless reconnection failed, keeping old connection",
),
n.close(),
(ie = !1),
void (ce < 5 && Be())
);
ze(),
ce < 5
? Be()
: (console.error("[Braze Real-Time] Max retries reached"),
Re({ type: "error", error: "Max retry attempts reached" }));
});
} catch (e) {
(ie = !1),
console.error("[Braze Real-Time] Failed to create EventSource:", e),
Re({ type: "error", error: String(e) });
}
}
function $e(e, n, o) {
switch (o.type) {
case "connect":
!le ||
(le.mite === o.Cn.mite && le.Wi === o.Cn.Wi) ||
(console.log("[Braze Real-Time] Config changed, reconnecting"),
ze(),
(ce = 0),
(ue = null),
(me = null)),
(le = o.Cn),
se.has(n) || se.set(n, { port: e }),
ge(),
te || ie ? te && e.postMessage({ type: "connected" }) : ye();
break;
case "disconnect":
se.delete(n),
n === fe && ((fe = null), ge()),
0 === se.size &&
(console.log("[Braze Real-Time] No more ports, closing connection"),
ze(),
(le = null),
(me = null),
(fe = null));
break;
case "tab_active":
!(function (e) {
se.has(e) &&
fe !== e &&
((fe = e),
console.log(
"[Braze Real-Time] Promoted to leader (tab became active):",
e,
));
})(n);
break;
case "ping":
e.postMessage({ type: "pong" });
break;
default:
console.warn("[Braze Real-Time] Unknown message type:", o.type);
}
}
(Te.Tn = ye),
(Te.qe = function (e) {
try {
const t = JSON.parse(e);
if (!t.type)
return void console.warn("[Braze Real-Time] Message without type:", t);
if ("ttl" === t.type && t.body)
return (
(n = t.body.t_ms),
(o = t.body.rcs),
void (
"number" == typeof n &&
("string" == typeof o && (me = o),
console.log(
`[Braze Real-Time] TTL set to ${n}ms, will perform gapless reconnection when expired`,
),
null !== de && clearTimeout(de),
(de = setTimeout(function () {
(de = null),
console.log(
"[Braze Real-Time] TTL expired, performing gapless reconnection",
),
ye(te || void 0);
}, n)))
)
);
if ("ddr" === t.type && t.body)
return void (function (e, n) {
if ("number" != typeof e) return;
const o = null == le ? void 0 : le.Mn,
t = (null == o ? void 0 : o.Rn) || 1e4,
l = (null == o ? void 0 : o.Bn) || 3,
c = (null == o ? void 0 : o.zn) || 3e5,
r = Math.min(c, a(t, t * l)),
i = Math.round(e + r),
s = n ? ` (${n})` : "";
console.log(
`[Braze Real-Time] Admin requested disconnect${s}, reconnecting in ${i}ms (r_ms=${e} + backoff=${r})`,
),
null !== pe && clearTimeout(pe),
ze(),
(pe = setTimeout(function () {
(pe = null), Te.Tn();
}, i));
})(t.body.r_ms, t.body.e);
console.log(`[Braze Real-Time] Routing '${t.type}' message to leader`),
he({ type: "message", data: t });
} catch (e) {
console.warn("[Braze Real-Time] Failed to parse message:", e);
}
var n, o;
}),
(oe.onconnect = function (e) {
const n = e.ports[0],
o = `port-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
se.set(o, { port: n }),
(n.onmessage = function (e) {
try {
$e(n, o, e.data);
} catch (e) {
console.error("[Braze Real-Time] Error handling message:", e);
}
}),
(n.onmessageerror = function () {
console.warn("[Braze Real-Time] Message error from port:", o),
se.delete(o);
}),
n.start();
});