@braze/web-sdk
Version:
Braze SDK for web sites and other JS platforms.
289 lines (288 loc) • 8.36 kB
JavaScript
import t from "../common/base-provider.js";
import r from "../managers/braze-instance.js";
import { STORAGE_KEYS as s } from "../managers/storage-manager.js";
import u from "../managers/subscription-manager.js";
import a from "../util/net.js";
import h from "../util/request-header-utils.js";
import { logger as E } from "../../shared-lib/index.js";
export default class tr extends t {
constructor(i, t, s) {
super(),
(this.B = i),
(this.j = t),
(this.B = i),
(this.j = t),
(this.mite = null),
(this.yi = null),
(this.$i = s || null),
(this.Ri = null),
(this.D = null),
(this.Mi = 0),
(this.ji = 5),
(this.Fi = null),
(this.Ui = !0),
(this.xi = null),
(this.Pi = null),
(this.ki = null),
(this.Bi = !1),
(this.Ji = new Map()),
this.Oi();
}
Oi() {
if (this.j) {
const i = this.j.lt(s.ct.qi),
t = this.j.lt(s.ct.zi);
i && t
? ((this.mite = i),
(this.yi = t),
E.info("Restored DUST configuration from storage"))
: (i || t) &&
(E.warn("Incomplete DUST configuration in storage, clearing"),
this.j.Nt(s.ct.qi),
this.j.Nt(s.ct.zi));
}
}
Hi() {
this.xi ||
this.Pi ||
((this.ki = () => {
(this.Bi = !0), (this.Ui = !1);
}),
window.addEventListener("beforeunload", this.ki),
(this.xi = () => {
(this.Bi = !0),
this.Ri &&
(E.info("Page unloading, closing real-time connection gracefully"),
(this.Ui = !1),
this.Li());
}),
window.addEventListener("pagehide", this.xi),
(this.Pi = (i) => {
i.persisted &&
this.Wi() &&
!this.Ri &&
(E.info("Page restored from bfcache, reconnecting"),
(this.Bi = !1),
(this.Ui = !0),
(this.Mi = 0),
this.Gi());
}),
window.addEventListener("pageshow", this.Pi));
}
jt() {
return this.D;
}
yt(i) {
this.D = i;
}
Ki(i, t) {
if ("function" != typeof t) return null;
let s = this.Ji.get(i);
return s || ((s = new u()), this.Ji.set(i, s), r.q(s)), s.wt(t);
}
Qi(i, t) {
const s = this.Ji.get(i);
s && s.removeSubscription(t);
}
Wi() {
return Boolean(this.mite && this.yi);
}
Vi(i, t) {
const e = () => {
"function" == typeof t && t();
},
n = this.B,
o = this.j;
if (!n || !o)
return (
E.error("NetworkManager or StorageManager not available"), void e()
);
const l = r.l();
if (l && !l.Xi())
return (
E.info("Real-time messaging is not enabled, skipping refresh"), void e()
);
this.Wi()
? E.info("Refreshing real-time messaging configuration")
: E.info("Fetching initial real-time messaging configuration");
const g = n.$({}, !0),
c = n.A(g, h.H.Yi, !1),
u = new Date().valueOf();
h.K(o, h.H.Yi, u),
a.O({
url: `${n.V()}/dust/config`,
headers: c,
data: g,
W: (t) => {
if (!n.Y(g, t, c))
return (
E.error(
"Failed to validate server response for real-time messaging configuration",
),
void e()
);
n.Z(),
t.mite && t.host
? ((this.mite = t.mite),
(this.yi = t.host),
E.info(
"Received real-time messaging configuration from server",
),
o.ft(s.ct.qi, t.mite),
o.ft(s.ct.zi, t.host),
this.Gi(),
"function" == typeof i && i())
: (t.mite ||
E.error("Missing messaging identifier in server response"),
t.host || E.error("Missing messaging host in server response"),
e());
},
error: (i) => {
n._(i, "retrieving DUST config"), e();
},
});
}
Gi() {
const i = r.l();
if (i && !i.Xi())
return void E.info(
"Real-time messaging is not enabled, skipping connection",
);
if (!this.Wi())
return void E.error(
"Cannot start real-time subscription without configuration",
);
this.Ri &&
(E.info(
"Real-time connection already exists, closing before starting new subscription",
),
this.Li());
const t = this.mite,
s = this.$i || this.yi;
if (!t || !s) return;
const e = `${
/^https?:\/\//i.test(s) ? s : `https://${s}`
}/sse?mite=${encodeURIComponent(t)}&attempts=${this.Mi}`;
this.$i && E.info(`Using custom real-time messaging host: ${this.$i}`);
try {
(this.Ri = new EventSource(e)),
(this.Ri.onopen = () => {
E.info("Real-time messaging connection established"),
(this.Mi = 0),
(this.Ui = !0);
}),
this.Ri.addEventListener("msg", (i) => {
this.Zi(i.data);
}),
(this.Ri.onerror = (i) => {
var t;
const s =
null === (t = this.Ri) || void 0 === t ? void 0 : t.readyState;
this.Bi ||
E.error(
`Real-time messaging connection error (readyState: ${s}, eventPhase: ${
null == i ? void 0 : i.eventPhase
})`,
),
this.Li(),
this.Ui && this.Mi < this.ji
? this._i()
: (this.Mi >= this.ji &&
E.error(
`Max retry attempts (${this.ji}) reached for real-time messaging, giving up for current session`,
),
(this.Ui = !1));
});
} catch (i) {
E.error(
`Failed to create real-time messaging connection: ${
i instanceof Error ? i.message : String(i)
}`,
);
}
}
_i() {
this.Mi++;
const i = Math.min(1e3 * Math.pow(2, this.Mi), 6e4);
E.info(
`Retrying real-time messaging connection in ${i}ms (attempt ${this.Mi}/${this.ji})`,
),
(this.Fi = window.setTimeout(() => {
(this.Fi = null), this.Gi();
}, i));
}
Li() {
null !== this.Fi && (window.clearTimeout(this.Fi), (this.Fi = null)),
this.Ri &&
(this.Ri.close(),
(this.Ri = null),
E.info("Real-time messaging connection closed"));
}
Zi(i) {
try {
const t = JSON.parse(i);
if (!t.type)
return void E.error(
`Received real-time message without type: ${JSON.stringify(t)}`,
);
E.info(`Received real-time message of type '${t.type}'`);
const s = this.Ji.get(t.type);
s && s.he() > 0
? s.L(t)
: E.info(`No subscribers for real-time message type '${t.type}'`);
} catch (i) {
E.error(
`Failed to parse real-time message: ${
i instanceof Error ? i.message : String(i)
}`,
);
}
}
changeUser(i = !1) {
this.Li(),
!i && this.j
? (this.Wi() &&
E.info(
"Clearing cached real-time messaging configuration for user change",
),
this.j.Nt(s.ct.qi),
this.j.Nt(s.ct.zi),
(this.mite = null),
(this.yi = null))
: i &&
this.Wi() &&
E.info(
"Preserving cached real-time messaging configuration for anonymous->identified user transition",
),
(this.Mi = 0),
(this.Ui = !0);
}
clearData(i = !1) {
(this.Ui = !1),
this.Li(),
i &&
this.j &&
(this.Wi() &&
E.info(
"Clearing cached real-time messaging configuration (wipeData)",
),
this.j.Nt(s.ct.qi),
this.j.Nt(s.ct.zi),
(this.mite = null),
(this.yi = null)),
(this.Mi = 0);
}
destroy() {
(this.Ui = !1),
this.Li(),
(this.mite = null),
(this.Mi = 0),
this.ki &&
(window.removeEventListener("beforeunload", this.ki), (this.ki = null)),
this.xi &&
(window.removeEventListener("pagehide", this.xi), (this.xi = null)),
this.Pi &&
(window.removeEventListener("pageshow", this.Pi), (this.Pi = null)),
this.D && (r.removeSubscription(this.D), (this.D = null));
}
}