@arsel.sa/web-sdk 1.0.0 → 1.2.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.
package/dist/arsel.js CHANGED
@@ -1,51 +1,62 @@
1
- const G = "arsel";
2
- const w = "events";
3
- let I = null;
4
- function Q() {
5
- return I || (I = new Promise((e, t) => {
6
- const n = indexedDB.open(G, 1);
1
+ const Ye = "arsel";
2
+ const S = {
3
+ events: "events",
4
+ inAppBeacons: "inapp_beacons"
5
+ };
6
+ let H = null;
7
+ function Je() {
8
+ return H || (H = new Promise((e, t) => {
9
+ const n = indexedDB.open(Ye, 2);
7
10
  n.onupgradeneeded = () => {
8
11
  const i = n.result;
9
- i.objectStoreNames.contains("kv") || i.createObjectStore("kv"), i.objectStoreNames.contains(w) || i.createObjectStore(w, { keyPath: "id", autoIncrement: !0 });
12
+ i.objectStoreNames.contains("kv") || i.createObjectStore("kv");
13
+ for (const r of Object.values(S))
14
+ i.objectStoreNames.contains(r) || i.createObjectStore(r, { keyPath: "id", autoIncrement: !0 });
10
15
  }, n.onsuccess = () => e(n.result), n.onerror = () => t(n.error);
11
- }), I);
16
+ }), H);
12
17
  }
13
- function y(e, t, n) {
14
- return Q().then(
15
- (i) => new Promise((s, a) => {
16
- const o = n(i.transaction(e, t).objectStore(e));
17
- o.onsuccess = () => s(o.result), o.onerror = () => a(o.error);
18
+ function P(e, t, n) {
19
+ return Je().then(
20
+ (i) => new Promise((r, s) => {
21
+ const a = n(i.transaction(e, t).objectStore(e));
22
+ a.onsuccess = () => r(a.result), a.onerror = () => s(a.error);
18
23
  })
19
24
  );
20
25
  }
21
26
  function c(e) {
22
- return y("kv", "readonly", (t) => t.get(e)).then(
27
+ return P("kv", "readonly", (t) => t.get(e)).then(
23
28
  (t) => t ?? null
24
29
  );
25
30
  }
26
31
  function u(e, t) {
27
- return y("kv", "readwrite", (n) => n.put(t, e));
32
+ return P("kv", "readwrite", (n) => n.put(t, e));
28
33
  }
29
- function A(e) {
30
- return y("kv", "readwrite", (t) => t.delete(e));
34
+ function L(e) {
35
+ return P("kv", "readwrite", (t) => t.delete(e));
31
36
  }
32
- function ee(e, t) {
33
- return y(
34
- w,
37
+ function De(e, t, n = "") {
38
+ return P(
39
+ e,
35
40
  "readwrite",
36
- (n) => n.add({ body: e, idempotencyKey: t, createdAtMs: Date.now() })
41
+ (i) => i.add({ body: t, idempotencyKey: n, createdAtMs: Date.now() })
37
42
  );
38
43
  }
39
- function te() {
40
- return y(w, "readonly", (e) => e.getAll());
44
+ function le(e) {
45
+ return P(e, "readonly", (t) => t.getAll());
41
46
  }
42
- function ne(e) {
43
- return y(w, "readwrite", (t) => t.delete(e));
47
+ function ue(e, t) {
48
+ return P(e, "readwrite", (n) => n.delete(t));
44
49
  }
45
- function re() {
46
- return y(w, "readonly", (e) => e.count());
50
+ function Oe(e) {
51
+ return P(e, "readonly", (t) => t.count());
47
52
  }
48
- const r = {
53
+ async function Ge(e, t) {
54
+ const n = await le(e);
55
+ if (!(n.length <= t))
56
+ for (const i of n.slice(0, n.length - t))
57
+ i.id !== void 0 && await ue(e, i.id);
58
+ }
59
+ const o = {
49
60
  clientKey: "client_key",
50
61
  baseUrl: "base_url",
51
62
  anonymousId: "anonymous_id",
@@ -56,47 +67,59 @@ const r = {
56
67
  deviceSecret: "device_secret",
57
68
  vapidKeyVersion: "vapid_key_version",
58
69
  endpoint: "endpoint",
70
+ subscriptionStatus: "subscription_status",
71
+ installReported: "install_reported",
59
72
  sessionStartedAt: "session_started_at",
60
73
  backgroundedAt: "backgrounded_at",
61
74
  lastResponseCode: "last_response_code",
62
75
  lastResponsePath: "last_response_path",
63
- lastResponseAtMs: "last_response_at"
76
+ lastResponseAtMs: "last_response_at",
77
+ /** `{ bundleVersion, ttlSeconds, fetchedAtMs, messages }` — the cached bundle. */
78
+ inAppBundle: "inapp_bundle",
79
+ /** messageId -> lifetime counters. Survives `reset()`: it describes the device. */
80
+ inAppState: "inapp_state",
81
+ /**
82
+ * `{ startedAt, counts }`. Persisted rather than held in memory so
83
+ * `maxPerSession` survives a navigation — on a multi-page site an in-memory
84
+ * counter makes the cap per-page, which is not a cap at all.
85
+ */
86
+ inAppSession: "inapp_session"
64
87
  };
65
- async function T() {
66
- const e = await c(r.anonymousId);
88
+ async function X() {
89
+ const e = await c(o.anonymousId);
67
90
  if (e) return e;
68
91
  const t = crypto.randomUUID();
69
- return await u(r.anonymousId, t), t;
92
+ return await u(o.anonymousId, t), t;
70
93
  }
71
- async function ie() {
94
+ async function Ze() {
72
95
  const e = crypto.randomUUID();
73
- return await u(r.anonymousId, e), e;
96
+ return await u(o.anonymousId, e), e;
74
97
  }
75
- const K = "1.0.0", W = { "X-Arsel-SDK": `web/${K}` }, f = {
98
+ const j = "1.2.0", Re = { "X-Arsel-SDK": `web/${j}` }, y = {
76
99
  success: "success",
77
100
  retryable: "retryable",
78
101
  permanent: "permanent",
79
102
  reauth: "reauth"
80
- }, O = -1;
81
- function se(e, t) {
82
- return e >= 200 && e <= 299 ? f.success : e === 408 || e === 429 || e >= 500 && e <= 599 ? f.retryable : t && (e === 401 || e === 403 || e === 404) ? f.reauth : e === 404 ? f.retryable : f.permanent;
103
+ }, J = -1;
104
+ function Me(e, t) {
105
+ return e >= 200 && e <= 299 ? y.success : e === 408 || e === 429 || e >= 500 && e <= 599 ? y.retryable : t && (e === 401 || e === 403 || e === 404) ? y.reauth : e === 404 ? y.retryable : y.permanent;
83
106
  }
84
- async function N(e, t) {
107
+ async function G(e, t) {
85
108
  try {
86
109
  await Promise.all([
87
- u(r.lastResponseCode, t),
88
- u(r.lastResponsePath, e),
89
- u(r.lastResponseAtMs, Date.now())
110
+ u(o.lastResponseCode, t),
111
+ u(o.lastResponsePath, e),
112
+ u(o.lastResponseAtMs, Date.now())
90
113
  ]);
91
114
  } catch {
92
115
  }
93
116
  }
94
- async function R(e, t, n, i = {}, s = !1) {
95
- let a;
117
+ async function z(e, t, n, i = {}, r = !1) {
118
+ let s;
96
119
  try {
97
- a = await fetch(e + t, {
120
+ s = await fetch(e + t, {
98
121
  method: "POST",
99
- headers: { "Content-Type": "application/json", ...W, ...i },
122
+ headers: { "Content-Type": "application/json", ...Re, ...i },
100
123
  body: JSON.stringify(n),
101
124
  // The SDK never relies on cookies, and sending them would attach a
102
125
  // first-party session to a cross-origin call for no reason.
@@ -104,30 +127,42 @@ async function R(e, t, n, i = {}, s = !1) {
104
127
  keepalive: !0
105
128
  });
106
129
  } catch {
107
- return await N(t, O), { result: f.retryable, code: O, body: null };
130
+ return await G(t, J), { result: y.retryable, code: J, body: null };
108
131
  }
109
- await N(t, a.status);
110
- const o = se(a.status, s);
132
+ await G(t, s.status);
133
+ const a = Me(s.status, r);
111
134
  let l = null;
112
135
  try {
113
- l = await a.json();
136
+ l = await s.json();
114
137
  } catch {
115
138
  }
116
- return { result: o, code: a.status, body: l };
139
+ return { result: a, code: s.status, body: l };
117
140
  }
118
- async function B(e, t) {
141
+ async function de(e, t, n = {}, i = !1, r = {}) {
142
+ let s;
119
143
  try {
120
- const n = await fetch(e + t, {
144
+ s = await fetch(e + t, {
145
+ ...r,
121
146
  credentials: "omit",
122
- headers: W
147
+ headers: { ...Re, ...n }
123
148
  });
124
- return await N(t, n.status), n.ok ? await n.json() : null;
125
149
  } catch {
126
- return await N(t, O), null;
150
+ return await G(t, J), { result: y.retryable, code: J, body: null };
151
+ }
152
+ await G(t, s.status);
153
+ let a = null;
154
+ try {
155
+ a = await s.json();
156
+ } catch {
127
157
  }
158
+ return {
159
+ result: Me(s.status, i),
160
+ code: s.status,
161
+ body: a
162
+ };
128
163
  }
129
- const ae = "/v1/events/send", v = "arsel.", oe = `${v}session_start`, ce = `${v}session_end`, le = `${v}identify`, ue = 80, de = 128, fe = 255, U = 50, pe = 8, V = 64 * 1024;
130
- function D(e, t) {
164
+ const Qe = "/v1/events/send", x = "arsel.", et = `${x}session_start`, tt = `${x}session_end`, nt = `${x}identify`, it = `${x}app_installed`, rt = `${x}screen_view`, ot = 80, st = 128, at = 255, ge = 50, ct = 8, ve = 64 * 1024;
165
+ function se(e, t) {
131
166
  if (e === null) return null;
132
167
  const n = typeof e;
133
168
  if (n === "string" || n === "boolean") return e;
@@ -135,466 +170,1172 @@ function D(e, t) {
135
170
  if (n === "bigint") return String(e);
136
171
  if (e instanceof Date)
137
172
  return Number.isNaN(e.getTime()) ? void 0 : e.toISOString();
138
- if (n !== "object" || t >= pe) return;
173
+ if (n !== "object" || t >= ct) return;
139
174
  if (Array.isArray(e))
140
- return e.map((s) => D(s, t + 1) ?? null);
175
+ return e.map((r) => se(r, t + 1) ?? null);
141
176
  const i = {};
142
- for (const [s, a] of Object.entries(e)) {
143
- if (!s) continue;
144
- const o = D(a, t + 1);
145
- o !== void 0 && (i[s] = o);
177
+ for (const [r, s] of Object.entries(e)) {
178
+ if (!r) continue;
179
+ const a = se(s, t + 1);
180
+ a !== void 0 && (i[r] = a);
146
181
  }
147
182
  return i;
148
183
  }
149
- function ye(e) {
184
+ function lt(e) {
150
185
  const t = {};
151
186
  let n = 2, i = !1;
152
- for (const [s, a] of Object.entries(e ?? {})) {
153
- if (!s) continue;
154
- const o = D(a, 0);
155
- if (o === void 0) continue;
156
- const l = s.length + JSON.stringify(o).length + 6;
157
- if (n + l > V) {
187
+ for (const [r, s] of Object.entries(e ?? {})) {
188
+ if (!r) continue;
189
+ const a = se(s, 0);
190
+ if (a === void 0) continue;
191
+ const l = r.length + JSON.stringify(a).length + 6;
192
+ if (n + l > ve) {
158
193
  i = !0;
159
194
  continue;
160
195
  }
161
- n += l, t[s] = o;
196
+ n += l, t[r] = a;
162
197
  }
163
198
  return i && console.warn(
164
- `[arsel] event data exceeded ${V} serialized bytes — oversized properties were dropped`
199
+ `[arsel] event data exceeded ${ve} serialized bytes — oversized properties were dropped`
165
200
  ), t;
166
201
  }
167
- async function we(e, t, n) {
168
- const [i, s, a, o] = await Promise.all([
169
- T(),
170
- c(r.externalId),
171
- c(r.email),
172
- c(r.phoneNumber)
202
+ async function ut(e, t, n) {
203
+ const [i, r, s, a] = await Promise.all([
204
+ X(),
205
+ c(o.externalId),
206
+ c(o.email),
207
+ c(o.phoneNumber)
173
208
  ]), l = {
174
- event: e.slice(0, ue),
209
+ event: e.slice(0, ot),
175
210
  // Always sent, even once identified: it is what the backend merges FROM.
176
- anonymous_id: i.slice(0, de),
177
- data: ye(t),
211
+ anonymous_id: i.slice(0, st),
212
+ data: lt(t),
178
213
  timestamp: new Date(n).toISOString()
179
214
  };
180
- return s && (l.external_id = s.slice(0, fe)), a && (l.email = a), o && (l.phone_number = o), JSON.stringify(l);
215
+ return r && (l.external_id = r.slice(0, at)), s && (l.email = s), a && (l.phone_number = a), JSON.stringify(l);
181
216
  }
182
- async function g(e, t, n = Date.now()) {
183
- await ee(await we(e, t, n), crypto.randomUUID()), S().catch(() => {
217
+ async function k(e, t, n = Date.now()) {
218
+ await De(S.events, await ut(e, t, n), crypto.randomUUID()), V().catch(() => {
184
219
  });
185
220
  }
186
- let h = null;
187
- function S() {
188
- return h || (h = he().finally(() => {
189
- h = null;
190
- }), h);
221
+ let $ = null;
222
+ function V() {
223
+ return $ || ($ = pt().finally(() => {
224
+ $ = null;
225
+ }), $);
191
226
  }
192
- function me(e) {
227
+ function dt(e) {
193
228
  const t = e[0].idempotencyKey;
194
229
  return e.length === 1 ? t : `${t}:${e[e.length - 1].idempotencyKey}:${e.length}`;
195
230
  }
196
- function be(e, t, n) {
231
+ function ft(e, t, n) {
197
232
  e.some(
198
- (s) => s.external_id || s.email || s.phone_number
233
+ (r) => r.external_id || r.email || r.phone_number
199
234
  ) && console.error(
200
235
  `[arsel] ${e.length} event(s) carrying identifiers were rejected permanently (HTTP ${t}) and dropped. Response: ${JSON.stringify(n)}`
201
236
  );
202
237
  }
203
- async function he() {
238
+ async function pt() {
204
239
  const [e, t] = await Promise.all([
205
- c(r.clientKey),
206
- c(r.baseUrl)
240
+ c(o.clientKey),
241
+ c(o.baseUrl)
207
242
  ]);
208
243
  if (!(!e || !t))
209
244
  for (; ; ) {
210
- const n = await te();
245
+ const n = await le(S.events);
211
246
  if (n.length === 0) return;
212
- for (let i = 0; i < n.length; i += U) {
213
- const s = n.slice(i, i + U), a = s.map(
247
+ for (let i = 0; i < n.length; i += ge) {
248
+ const r = n.slice(i, i + ge), s = r.map(
214
249
  (l) => JSON.parse(l.body)
215
- ), o = await R(
250
+ ), a = await z(
216
251
  t,
217
- ae,
218
- s.length === 1 ? a[0] : { events: a },
252
+ Qe,
253
+ r.length === 1 ? s[0] : { events: s },
219
254
  {
220
255
  Authorization: `Bearer ${e}`,
221
- "Idempotency-Key": me(s)
256
+ "Idempotency-Key": dt(r)
222
257
  }
223
258
  );
224
- if (o.result === f.retryable) return;
225
- o.result !== f.success && be(a, o.code, o.body);
226
- for (const l of s)
227
- l.id !== void 0 && await ne(l.id);
259
+ if (a.result === y.retryable) return;
260
+ a.result !== y.success && ft(s, a.code, a.body);
261
+ for (const l of r)
262
+ l.id !== void 0 && await ue(S.events, l.id);
228
263
  }
229
264
  }
230
265
  }
231
- function C(e) {
266
+ const ee = 1e3, mt = 720 * 60 * 60 * ee, yt = 500, Se = 50, bt = 86400, ht = 900, q = {
267
+ maxPerSession: 1,
268
+ maxLifetime: 3,
269
+ minSecondsBetween: 86400,
270
+ delaySeconds: 0
271
+ }, wt = [
272
+ "MODAL",
273
+ "BANNER_TOP",
274
+ "BANNER_BOTTOM",
275
+ "IMAGE_ONLY"
276
+ ];
277
+ let fe = !1, d = null, h = {}, v = { startedAt: 0, counts: {} }, F = null, K = null, Be = !1, ie = null, _ = null, Ue = !1, Ae = !1;
278
+ function N(e) {
279
+ Ue && console.info(`[arsel] in-app: ${e}`);
280
+ }
281
+ async function gt(e) {
282
+ fe = !0, Ue = e;
283
+ const [t, n, i] = await Promise.all([
284
+ c(o.inAppBundle),
285
+ c(o.inAppState),
286
+ c(o.inAppSession)
287
+ ]);
288
+ d = t, h = n ?? {}, v = i ?? { startedAt: 0, counts: {} }, await Ke(), await te();
289
+ }
290
+ function vt(e) {
291
+ _ = e;
292
+ }
293
+ function St(e) {
294
+ Be = e, e && ae();
295
+ }
296
+ function At() {
297
+ return {
298
+ messages: (d == null ? void 0 : d.messages.length) ?? 0,
299
+ bundleVersion: (d == null ? void 0 : d.bundleVersion) ?? null,
300
+ fetchedAtMs: (d == null ? void 0 : d.fetchedAtMs) ?? null
301
+ };
302
+ }
303
+ function Et() {
304
+ return Oe(S.inAppBeacons);
305
+ }
306
+ async function Ce() {
307
+ d = null, await L(o.inAppBundle), fe && await te(!0);
308
+ }
309
+ function It() {
310
+ te(!0);
311
+ }
312
+ function te(e = !1) {
313
+ return ie ?? (ie = _t(e).finally(() => {
314
+ ie = null;
315
+ })), ie;
316
+ }
317
+ async function _t(e) {
318
+ if (!fe) return;
319
+ const t = Date.now();
320
+ if (!e && d && t - d.fetchedAtMs < d.ttlSeconds * ee)
321
+ return;
322
+ const n = await $e();
323
+ if (!n) return;
324
+ const i = Nt(n.clientKey, n.installationId), r = {
325
+ "X-Arsel-Device-Auth": n.deviceSecret
326
+ }, s = !!(d != null && d.bundleVersion);
327
+ s && (r["If-None-Match"] = `"${(d == null ? void 0 : d.bundleVersion) ?? ""}"`);
328
+ let a = await Ee(n.baseUrl, i, r);
329
+ if (a.code === Lt && s && Ae && (N("conditional request failed; retrying without If-None-Match"), delete r["If-None-Match"], a = await Ee(n.baseUrl, i, r)), a.code === Kt) {
330
+ d && (d = { ...d, fetchedAtMs: t }, await u(o.inAppBundle, d));
331
+ return;
332
+ }
333
+ if (a.result !== y.success || !a.body)
334
+ return;
335
+ s || (Ae = !0);
336
+ const l = xt(a.body, t);
337
+ l && (d = l, await u(o.inAppBundle, d), await Ct(l, t));
338
+ }
339
+ function Ee(e, t, n) {
340
+ return de(e, t, n, !0, {
341
+ // Without no-store the browser revalidates on its own against the response's
342
+ // ETag + Cache-Control and hands back a synthesized 200; the manually-set
343
+ // If-None-Match never reaches the server and 304 never happens.
344
+ cache: "no-store"
345
+ });
346
+ }
347
+ function Nt(e, t) {
348
+ const n = encodeURIComponent(e), i = encodeURIComponent(t);
349
+ return `/api/v1/orgs/${n}/in-app/bundle?installationId=${i}`;
350
+ }
351
+ async function $e() {
352
+ const [e, t, n, i] = await Promise.all([
353
+ c(o.clientKey),
354
+ c(o.baseUrl),
355
+ c(o.installationId),
356
+ c(o.deviceSecret)
357
+ ]);
358
+ return !e || !t || !n || !i ? null : { clientKey: e, baseUrl: t, installationId: n, deviceSecret: i };
359
+ }
360
+ function xt(e, t) {
361
+ if (typeof (e == null ? void 0 : e.bundleVersion) != "string")
362
+ return N("response carried no bundleVersion"), null;
363
+ e.contractVersion !== void 0 && e.contractVersion !== 1 && N(`unknown contractVersion ${String(e.contractVersion)}; proceeding`);
364
+ const n = Array.isArray(e.messages) ? e.messages : [], i = [];
365
+ for (const s of n) {
366
+ const a = kt(s);
367
+ a && i.push(a);
368
+ }
369
+ n.length !== i.length && N(`dropped ${n.length - i.length} unrenderable message(s)`);
370
+ const r = O(e.ttlSeconds);
371
+ return {
372
+ bundleVersion: e.bundleVersion,
373
+ ttlSeconds: r && r > 0 ? r : ht,
374
+ fetchedAtMs: t,
375
+ messages: i
376
+ };
377
+ }
378
+ function kt(e) {
379
+ if (!D(e)) return null;
380
+ const t = m(e.campaignId), n = m(e.messageId), i = m(e.layout), r = D(e.content) ? e.content : null, s = r ? m(r.headline) : void 0;
381
+ if (!t || !n || !i || !r || !s)
382
+ return N("message missing a required field"), null;
383
+ if (!wt.includes(i))
384
+ return N(`layout ${i} is not renderable on web`), null;
385
+ const a = D(e.trigger) ? e.trigger : {}, l = D(e.displayRules) ? e.displayRules : {};
386
+ return {
387
+ campaignId: t,
388
+ messageId: n,
389
+ variantKey: m(e.variantKey) ?? "default",
390
+ priority: O(e.priority) ?? 0,
391
+ expiresAtMs: $t(e.expiresAt),
392
+ triggerType: m(a.type) ?? "APP_OPEN",
393
+ triggerEventName: m(a.eventName) ?? null,
394
+ triggerProperties: D(a.properties) ? a.properties : null,
395
+ displayRules: {
396
+ maxPerSession: O(l.maxPerSession) ?? q.maxPerSession,
397
+ maxLifetime: O(l.maxLifetime) ?? q.maxLifetime,
398
+ minSecondsBetween: O(l.minSecondsBetween) ?? q.minSecondsBetween,
399
+ delaySeconds: O(l.delaySeconds) ?? q.delaySeconds
400
+ },
401
+ layout: i,
402
+ content: {
403
+ headline: s,
404
+ body: m(r.body) ?? "",
405
+ imageUrl: m(r.imageUrl),
406
+ backgroundColor: m(r.backgroundColor),
407
+ textColor: m(r.textColor),
408
+ // Absent means "not suppressed"; only an explicit false hides it.
409
+ showCloseButton: r.showCloseButton !== !1
410
+ },
411
+ // `buttons` arrives as null, not [], when a campaign has none.
412
+ buttons: Array.isArray(e.buttons) ? e.buttons.map(Pt).filter((w) => w !== null) : []
413
+ };
414
+ }
415
+ function Pt(e) {
416
+ if (!D(e)) return null;
417
+ const t = m(e.buttonId), n = m(e.label), i = m(e.action);
418
+ return !t || !n || !i ? null : {
419
+ buttonId: t,
420
+ label: n,
421
+ action: i,
422
+ value: m(e.value),
423
+ style: m(e.style) === "SECONDARY" ? "SECONDARY" : "PRIMARY"
424
+ };
425
+ }
426
+ function Tt(e, t, n, i) {
427
+ if (Be || F !== null || !d) return null;
428
+ for (const r of d.messages) {
429
+ if (r.triggerType !== t || t !== "APP_OPEN" && r.triggerEventName !== n || !Dt(r.triggerProperties, i)) continue;
430
+ const s = h[r.messageId];
431
+ if (r.expiresAtMs !== null && r.expiresAtMs <= e) {
432
+ Ut(r);
433
+ continue;
434
+ }
435
+ if (!(((s == null ? void 0 : s.shown) ?? 0) >= r.displayRules.maxLifetime) && !((v.counts[r.messageId] ?? 0) >= r.displayRules.maxPerSession) && !(s && e - s.lastShownAtMs < r.displayRules.minSecondsBetween * ee))
436
+ return r;
437
+ }
438
+ return null;
439
+ }
440
+ function Dt(e, t) {
441
+ if (!e) return !0;
442
+ const n = t;
443
+ for (const [i, r] of Object.entries(e))
444
+ if (String(n[i]) !== String(r)) return !1;
445
+ return !0;
446
+ }
447
+ function pe(e, t, n = {}) {
448
+ try {
449
+ const i = Tt(Date.now(), e, t, n);
450
+ if (!i) return;
451
+ F = i.messageId, Le = t;
452
+ const r = i.displayRules.delaySeconds * ee;
453
+ if (r <= 0) {
454
+ _ == null || _(i);
455
+ return;
456
+ }
457
+ K = setTimeout(() => {
458
+ K = null, _ == null || _(i);
459
+ }, r);
460
+ } catch (i) {
461
+ N(`observe failed: ${i instanceof Error ? i.message : "unknown"}`), F = null;
462
+ }
463
+ }
464
+ let Le = null;
465
+ function Ot() {
466
+ return Le;
467
+ }
468
+ function Ie() {
469
+ Ke().then(() => {
470
+ pe("APP_OPEN", null, {});
471
+ });
472
+ }
473
+ function ae() {
474
+ K !== null && (clearTimeout(K), K = null, F = null);
475
+ }
476
+ function _e() {
477
+ F = null;
478
+ }
479
+ async function Rt(e, t) {
480
+ const n = Date.now(), i = h[e.messageId] ?? {
481
+ shown: 0,
482
+ lastShownAtMs: 0,
483
+ lastSeenInBundleAtMs: n
484
+ };
485
+ h = {
486
+ ...h,
487
+ [e.messageId]: { ...i, shown: i.shown + 1, lastShownAtMs: n }
488
+ }, v = {
489
+ ...v,
490
+ counts: {
491
+ ...v.counts,
492
+ [e.messageId]: (v.counts[e.messageId] ?? 0) + 1
493
+ }
494
+ }, await Promise.all([
495
+ u(o.inAppState, h),
496
+ u(o.inAppSession, v)
497
+ ]), await ne(e, "impression", { triggerEventName: t });
498
+ }
499
+ async function Mt(e, t) {
500
+ await ne(e, "clicked", { buttonId: t });
501
+ }
502
+ async function Bt(e, t) {
503
+ await ne(e, "dismissed", {
504
+ visibleSeconds: Math.max(
505
+ 0,
506
+ Math.min(bt, Math.round(t))
507
+ )
508
+ });
509
+ }
510
+ function Ut(e) {
511
+ const t = h[e.messageId];
512
+ t != null && t.expiredReported || (h = {
513
+ ...h,
514
+ [e.messageId]: {
515
+ shown: (t == null ? void 0 : t.shown) ?? 0,
516
+ lastShownAtMs: (t == null ? void 0 : t.lastShownAtMs) ?? 0,
517
+ lastSeenInBundleAtMs: (t == null ? void 0 : t.lastSeenInBundleAtMs) ?? Date.now(),
518
+ expiredReported: !0
519
+ }
520
+ }, u(o.inAppState, h), ne(e, "expired", {}));
521
+ }
522
+ async function ne(e, t, n) {
523
+ const i = {
524
+ messageId: e.messageId,
525
+ campaignId: e.campaignId,
526
+ eventType: t,
527
+ // Stamped when it HAPPENED, not at flush: a beacon that waits out an offline
528
+ // spell would otherwise land in the wrong hour bucket.
529
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
530
+ variantKey: e.variantKey
531
+ };
532
+ n.buttonId && (i.buttonId = n.buttonId), n.visibleSeconds !== void 0 && (i.visibleSeconds = n.visibleSeconds), n.triggerEventName && (i.triggerEventName = n.triggerEventName), await De(S.inAppBeacons, JSON.stringify(i)), await Ge(S.inAppBeacons, yt), Y();
533
+ }
534
+ async function Y() {
535
+ const e = await $e();
536
+ if (!e) return;
537
+ const t = await le(S.inAppBeacons);
538
+ if (t.length === 0) return;
539
+ const n = `/api/v1/orgs/${encodeURIComponent(e.clientKey)}/in-app/events`;
540
+ for (let i = 0; i < t.length; i += Se) {
541
+ const r = t.slice(i, i + Se);
542
+ if ((await z(
543
+ e.baseUrl,
544
+ n,
545
+ {
546
+ installationId: e.installationId,
547
+ events: r.map((a) => JSON.parse(a.body))
548
+ },
549
+ { "X-Arsel-Device-Auth": e.deviceSecret },
550
+ !0
551
+ )).result === y.retryable) return;
552
+ for (const a of r)
553
+ a.id !== void 0 && await ue(S.inAppBeacons, a.id);
554
+ }
555
+ }
556
+ async function Ct(e, t) {
557
+ const n = new Set(e.messages.map((r) => r.messageId)), i = {};
558
+ for (const [r, s] of Object.entries(h)) {
559
+ const a = n.has(r) ? t : s.lastSeenInBundleAtMs;
560
+ t - a > mt || (i[r] = { ...s, lastSeenInBundleAtMs: a });
561
+ }
562
+ h = i, await u(o.inAppState, h);
563
+ }
564
+ async function Ke() {
565
+ const e = await c(o.sessionStartedAt) ?? 0;
566
+ v.startedAt !== e && (v = { startedAt: e, counts: {} }, await u(o.inAppSession, v));
567
+ }
568
+ function D(e) {
569
+ return typeof e == "object" && e !== null && !Array.isArray(e);
570
+ }
571
+ function m(e) {
572
+ return typeof e == "string" && e.length > 0 ? e : void 0;
573
+ }
574
+ function O(e) {
575
+ return typeof e == "number" && Number.isFinite(e) ? e : void 0;
576
+ }
577
+ function $t(e) {
578
+ if (typeof e != "string") return null;
579
+ const t = Date.parse(e);
580
+ return Number.isNaN(t) ? null : t;
581
+ }
582
+ const Lt = -1, Kt = 304, Vt = 2147483e3, re = 44, Ne = /^#[0-9a-fA-F]{3,8}$/, Ft = 0.6;
583
+ let xe = null, Wt = 0;
584
+ function Xt(e, t, n) {
585
+ const i = e.layout === "MODAL", r = Date.now(), s = `arsel-iam-${Wt += 1}`, a = document.createElement("div");
586
+ a.setAttribute("data-arsel-inapp", "");
587
+ const l = a.attachShadow({ mode: "closed" });
588
+ tn(l), a.style.setProperty("z-index", String(t.zIndex || Vt)), a.style.setProperty("isolation", "isolate"), a.setAttribute("data-layout", e.layout), a.setAttribute("dir", Zt());
589
+ const w = document.activeElement instanceof HTMLElement ? document.activeElement : null, f = document.createElement("div");
590
+ f.className = "panel", f.tabIndex = -1, i ? (f.setAttribute("role", "dialog"), f.setAttribute("aria-modal", "true"), f.setAttribute("aria-labelledby", `${s}-h`), e.content.body && f.setAttribute("aria-describedby", `${s}-b`)) : (f.setAttribute("role", "status"), f.setAttribute("aria-live", "polite"));
591
+ const A = Qt(e);
592
+ A.background && f.style.setProperty("background", A.background), A.text && f.style.setProperty("color", A.text);
593
+ let C = !1;
594
+ const E = (p) => {
595
+ var g, we;
596
+ C || (C = !0, document.removeEventListener("keydown", T, !0), a.remove(), w != null && w.isConnected ? w.focus() : i && ((we = (g = document.body).focus) == null || we.call(g)), p === "dismiss" && n.onDismiss((Date.now() - r) / 1e3));
597
+ }, T = (p) => {
598
+ p.key === "Escape" && (p.stopPropagation(), E("dismiss"));
599
+ };
600
+ if (e.layout === "IMAGE_ONLY" && e.content.imageUrl)
601
+ f.append(zt(e, () => qe(e)));
602
+ else {
603
+ e.content.imageUrl && f.append(jt(e.content.imageUrl, e.content.headline));
604
+ const p = document.createElement("h2");
605
+ if (p.id = `${s}-h`, p.className = "headline", p.textContent = e.content.headline, f.append(p), e.content.body) {
606
+ const g = document.createElement("p");
607
+ g.id = `${s}-b`, g.className = "body", g.textContent = e.content.body, f.append(g);
608
+ }
609
+ }
610
+ if (e.buttons.length > 0 && f.append(Ht(e, n, E)), (e.content.showCloseButton || !e.buttons.some((p) => p.action === "DISMISS")) && f.append(Jt(t.closeLabel, () => E("dismiss"))), i) {
611
+ const p = document.createElement("div");
612
+ p.className = "backdrop", e.content.showCloseButton && p.addEventListener("click", () => E("dismiss")), l.append(p);
613
+ }
614
+ l.append(f), document.body.append(a), i ? (f.focus({ preventScroll: !0 }), l.addEventListener("keydown", (p) => {
615
+ p instanceof KeyboardEvent && Gt(p, l);
616
+ })) : (f.textContent, f.setAttribute("aria-busy", "true"), requestAnimationFrame(() => {
617
+ f.removeAttribute("aria-busy");
618
+ })), document.addEventListener("keydown", T, !0), requestAnimationFrame(() => {
619
+ C || !a.isConnected || document.visibilityState === "visible" && n.onImpression();
620
+ });
621
+ function qe(p) {
622
+ const g = p.buttons[0];
623
+ if (!g) {
624
+ E("dismiss");
625
+ return;
626
+ }
627
+ n.onButton(g), E("button");
628
+ }
629
+ return { close: E, root: l };
630
+ }
631
+ function jt(e, t) {
632
+ const n = document.createElement("figure");
633
+ n.className = "figure";
634
+ const i = document.createElement("img");
635
+ return i.src = e, i.alt = t, i.loading = "eager", i.decoding = "async", i.referrerPolicy = "no-referrer", i.addEventListener("error", () => n.remove()), n.append(i), n;
636
+ }
637
+ function zt(e, t) {
638
+ const n = document.createElement("button");
639
+ n.type = "button", n.className = "image-only";
640
+ const i = document.createElement("img");
641
+ return i.src = e.content.imageUrl ?? "", i.alt = e.content.headline, i.loading = "eager", i.decoding = "async", i.referrerPolicy = "no-referrer", n.append(i), n.addEventListener("click", t), n;
642
+ }
643
+ function Ht(e, t, n) {
644
+ const i = document.createElement("div");
645
+ i.className = "buttons";
646
+ for (const r of e.buttons) {
647
+ const s = r.action === "URL" && r.value ? qt(r) : Yt(r);
648
+ s.addEventListener("click", () => {
649
+ r.action !== "DISMISS" && t.onButton(r), n(r.action === "DISMISS" ? "dismiss" : "button"), r.action === "DEEP_LINK" && r.value && location.assign(r.value);
650
+ }), i.append(s);
651
+ }
652
+ return i;
653
+ }
654
+ function qt(e) {
655
+ const t = document.createElement("a");
656
+ return t.href = e.value ?? "#", t.target = "_blank", t.rel = "noopener noreferrer", t.className = `cta ${e.style.toLowerCase()}`, t.textContent = e.label, t;
657
+ }
658
+ function Yt(e) {
659
+ const t = document.createElement("button");
660
+ return t.type = "button", t.className = `cta ${e.style.toLowerCase()}`, t.textContent = e.label, t;
661
+ }
662
+ function Jt(e, t) {
663
+ const n = document.createElement("button");
664
+ return n.type = "button", n.className = "close", n.setAttribute("aria-label", e), n.textContent = "×", n.addEventListener("click", t), n;
665
+ }
666
+ function Gt(e, t) {
667
+ if (e.key !== "Tab") return;
668
+ const n = [
669
+ ...t.querySelectorAll("button, a[href]")
670
+ ].filter((a) => !a.hasAttribute("disabled"));
671
+ if (n.length === 0) return;
672
+ const i = n[0], r = n[n.length - 1];
673
+ if (!i || !r) return;
674
+ const s = t.activeElement;
675
+ if (e.shiftKey && s === i) {
676
+ e.preventDefault(), r.focus();
677
+ return;
678
+ }
679
+ !e.shiftKey && s === r && (e.preventDefault(), i.focus());
680
+ }
681
+ function Zt() {
682
+ const e = document.documentElement.getAttribute("dir");
683
+ if (e) return e;
684
+ try {
685
+ return getComputedStyle(document.documentElement).direction || "ltr";
686
+ } catch {
687
+ return "ltr";
688
+ }
689
+ }
690
+ function Qt(e) {
691
+ const t = Ne.test(e.content.backgroundColor ?? "") ? e.content.backgroundColor : void 0, n = Ne.test(e.content.textColor ?? "") ? e.content.textColor : void 0;
692
+ return t && !n ? { background: t, text: en(t) > Ft ? "#101010" : "#FFFFFF" } : { background: t, text: n };
693
+ }
694
+ function en(e) {
695
+ const t = e.slice(1), n = t.length === 3 ? t.split("").map((a) => a + a).join("") : t.slice(0, 6), i = parseInt(n.slice(0, 2), 16) / 255, r = parseInt(n.slice(2, 4), 16) / 255, s = parseInt(n.slice(4, 6), 16) / 255;
696
+ return 0.2126 * i + 0.7152 * r + 0.0722 * s;
697
+ }
698
+ function tn(e) {
699
+ if ("adoptedStyleSheets" in Document.prototype && typeof CSSStyleSheet == "function")
700
+ try {
701
+ xe ?? (xe = nn()), e.adoptedStyleSheets = [xe];
702
+ return;
703
+ } catch {
704
+ }
705
+ const t = document.createElement("style");
706
+ t.textContent = Ve, e.append(t);
707
+ }
708
+ function nn() {
709
+ const e = new CSSStyleSheet();
710
+ return e.replaceSync(Ve), e;
711
+ }
712
+ const Ve = `
713
+ :host {
714
+ all: initial;
715
+ display: block;
716
+ position: fixed;
717
+ inset: 0;
718
+ pointer-events: none;
719
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
720
+ line-height: 1.5;
721
+ color: #101010;
722
+ }
723
+ .backdrop {
724
+ position: fixed;
725
+ inset: 0;
726
+ background: rgba(0, 0, 0, 0.45);
727
+ pointer-events: auto;
728
+ }
729
+ .panel {
730
+ position: fixed;
731
+ box-sizing: border-box;
732
+ pointer-events: auto;
733
+ background: #ffffff;
734
+ border-radius: 12px;
735
+ padding: 20px;
736
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
737
+ max-height: calc(100vh - 32px);
738
+ overflow-y: auto;
739
+ }
740
+ :host([data-layout="MODAL"]) .panel {
741
+ inset-inline-start: 50%;
742
+ top: 50%;
743
+ transform: translate(-50%, -50%);
744
+ width: min(calc(100vw - 32px), 420px);
745
+ }
746
+ :host([dir="rtl"][data-layout="MODAL"]) .panel {
747
+ transform: translate(50%, -50%);
748
+ }
749
+ :host([data-layout="BANNER_TOP"]) .panel,
750
+ :host([data-layout="BANNER_BOTTOM"]) .panel {
751
+ inset-inline: 16px;
752
+ margin-inline: auto;
753
+ max-width: min(calc(100vw - 32px), 560px);
754
+ }
755
+ :host([data-layout="BANNER_TOP"]) .panel {
756
+ top: 16px;
757
+ padding-top: calc(20px + env(safe-area-inset-top));
758
+ }
759
+ :host([data-layout="BANNER_BOTTOM"]) .panel {
760
+ bottom: 16px;
761
+ padding-bottom: calc(20px + env(safe-area-inset-bottom));
762
+ }
763
+ :host([data-layout="IMAGE_ONLY"]) .panel {
764
+ inset-inline-start: 50%;
765
+ top: 50%;
766
+ transform: translate(-50%, -50%);
767
+ padding: 0;
768
+ background: transparent;
769
+ box-shadow: none;
770
+ }
771
+ .figure { margin: 0 0 12px; }
772
+ .figure img, .image-only img {
773
+ display: block;
774
+ width: 100%;
775
+ height: auto;
776
+ border-radius: 8px;
777
+ }
778
+ .image-only {
779
+ display: block;
780
+ padding: 0;
781
+ border: 0;
782
+ background: none;
783
+ cursor: pointer;
784
+ width: min(calc(100vw - 32px), 420px);
785
+ }
786
+ .headline { margin: 0 0 8px; font-size: 18px; font-weight: 600; }
787
+ .body { margin: 0 0 16px; font-size: 15px; }
788
+ .buttons { display: flex; gap: 8px; flex-wrap: wrap; }
789
+ .cta {
790
+ flex: 1 1 auto;
791
+ min-height: ${re}px;
792
+ padding: 10px 16px;
793
+ border-radius: 8px;
794
+ border: 1px solid currentColor;
795
+ font: inherit;
796
+ cursor: pointer;
797
+ text-align: center;
798
+ text-decoration: none;
799
+ }
800
+ .cta.primary { background: #101010; color: #ffffff; border-color: #101010; }
801
+ .cta.secondary { background: transparent; color: inherit; }
802
+ .close {
803
+ position: absolute;
804
+ top: 4px;
805
+ inset-inline-end: 4px;
806
+ min-width: ${re}px;
807
+ min-height: ${re}px;
808
+ border: 0;
809
+ background: none;
810
+ color: inherit;
811
+ font-size: 22px;
812
+ line-height: 1;
813
+ cursor: pointer;
814
+ }
815
+ @media (prefers-reduced-motion: reduce) {
816
+ .panel { animation: none; transition: none; }
817
+ }
818
+ `;
819
+ async function rn(e) {
820
+ await c(o.installReported) || (await u(o.installReported, !0), !e && await k(it, {
821
+ sdk_version: j,
822
+ platform: "web"
823
+ }));
824
+ }
825
+ async function on() {
826
+ const [e, t] = await Promise.all([
827
+ c(o.anonymousId),
828
+ c(o.installationId)
829
+ ]);
830
+ return !!e || !!t;
831
+ }
832
+ const me = "REVOKED";
833
+ function sn(e) {
232
834
  const t = e.replace(/-/g, "+").replace(/_/g, "/"), n = atob(t + "=".repeat((4 - t.length % 4) % 4)), i = new Uint8Array(n.length);
233
- for (let s = 0; s < n.length; s += 1) i[s] = n.charCodeAt(s);
835
+ for (let r = 0; r < n.length; r += 1) i[r] = n.charCodeAt(r);
234
836
  return i.buffer;
235
837
  }
236
- function b() {
838
+ function B() {
237
839
  return typeof navigator < "u" && "serviceWorker" in navigator && typeof PushManager < "u" && typeof Notification < "u";
238
840
  }
239
- function F(e) {
841
+ function Fe(e) {
240
842
  return `/api/v1/orgs/${e}/push/web/config`;
241
843
  }
242
- function X(e) {
844
+ function ye(e) {
243
845
  return `/api/v1/orgs/${e}/push/subscriptions`;
244
846
  }
245
- const H = 1e4;
246
- let m = "none", E = null, k = null;
247
- function ve() {
248
- m = "external";
847
+ const We = 1e4;
848
+ let M = "none", W = null, Z = null;
849
+ function an() {
850
+ M = "external";
249
851
  }
250
- async function ge(e) {
251
- if (b()) {
252
- m = "managed";
852
+ async function cn(e) {
853
+ if (B()) {
854
+ M = "managed";
253
855
  try {
254
- E = await navigator.serviceWorker.register(e), k = null;
856
+ W = await navigator.serviceWorker.register(e), Z = null;
255
857
  } catch (t) {
256
- E = null, k = `service worker registration failed for '${e}': ${String(t)}. Check that the file is deployed and served with a JavaScript content type.`;
858
+ W = null, Z = `service worker registration failed for '${e}': ${String(t)}. Check that the file is deployed and served with a JavaScript content type.`;
257
859
  }
258
860
  }
259
861
  }
260
- function Se(e, t) {
862
+ function ln(e, t) {
261
863
  return e.active ? Promise.resolve(e) : new Promise((n, i) => {
262
- const s = setTimeout(() => {
864
+ const r = setTimeout(() => {
263
865
  i(
264
866
  new Error(
265
867
  `service worker did not activate within ${t}ms — check the worker script for load errors in DevTools → Application → Service Workers`
266
868
  )
267
869
  );
268
- }, t), a = (l) => {
870
+ }, t), s = (l) => {
269
871
  l.addEventListener("statechange", () => {
270
- l.state === "activated" ? (clearTimeout(s), n(e)) : l.state === "redundant" && (clearTimeout(s), i(
872
+ l.state === "activated" ? (clearTimeout(r), n(e)) : l.state === "redundant" && (clearTimeout(r), i(
271
873
  new Error(
272
874
  "service worker became redundant before activating — its script likely failed to parse"
273
875
  )
274
876
  ));
275
877
  });
276
- }, o = e.installing ?? e.waiting;
277
- o ? a(o) : e.addEventListener("updatefound", () => {
278
- e.installing && a(e.installing);
878
+ }, a = e.installing ?? e.waiting;
879
+ a ? s(a) : e.addEventListener("updatefound", () => {
880
+ e.installing && s(e.installing);
279
881
  });
280
882
  });
281
883
  }
282
- function Ee(e, t, n) {
884
+ function un(e, t, n) {
283
885
  return Promise.race([
284
886
  e,
285
- new Promise((i, s) => {
286
- setTimeout(() => s(new Error(n)), t);
887
+ new Promise((i, r) => {
888
+ setTimeout(() => r(new Error(n)), t);
287
889
  })
288
890
  ]);
289
891
  }
290
- async function $(e = H) {
291
- if (m === "none")
892
+ async function be(e = We) {
893
+ if (M === "none")
292
894
  throw new Error(
293
895
  "push is not configured — pass serviceWorkerPath to init(), or serviceWorker: 'external' if your own service worker imports arsel-sw.js"
294
896
  );
295
- if (m === "external")
296
- return Ee(
897
+ if (M === "external")
898
+ return un(
297
899
  navigator.serviceWorker.ready,
298
900
  e,
299
901
  `no service worker became active within ${e}ms — with serviceWorker: 'external', your own worker must be registered and must importScripts arsel-sw.js`
300
902
  );
301
- if (k) throw new Error(k);
302
- if (!E)
903
+ if (Z) throw new Error(Z);
904
+ if (!W)
303
905
  throw new Error("service worker registration has not completed");
304
- return Se(E, e);
906
+ return ln(W, e);
305
907
  }
306
- async function _e(e = H) {
908
+ async function dn(e = We) {
307
909
  try {
308
- return await $(e), null;
910
+ return await be(e), null;
309
911
  } catch (t) {
310
912
  return t instanceof Error ? t.message : String(t);
311
913
  }
312
914
  }
313
- async function Ie() {
915
+ async function Xe(e, t) {
916
+ try {
917
+ return await e.pushManager.subscribe({
918
+ // Non-negotiable: the Push API only accepts a userVisibleOnly subscription
919
+ // in Chrome, and silent push is what the restriction exists to prevent.
920
+ userVisibleOnly: !0,
921
+ applicationServerKey: sn(t)
922
+ });
923
+ } catch (n) {
924
+ return console.error(
925
+ `[arsel] push subscribe failed: ${n instanceof Error ? n.message : n}`
926
+ ), null;
927
+ }
928
+ }
929
+ async function fn() {
314
930
  const [e, t] = await Promise.all([
315
- c(r.clientKey),
316
- c(r.baseUrl)
931
+ c(o.clientKey),
932
+ c(o.baseUrl)
317
933
  ]);
318
- if (!e || !t || !b()) return !1;
319
- const n = await B(t, F(e));
934
+ if (!e || !t || !B()) return !1;
935
+ const { body: n } = await de(
936
+ t,
937
+ Fe(e)
938
+ );
320
939
  if (!(n != null && n.vapidPublicKey)) return !1;
321
940
  let i;
322
941
  try {
323
- i = await $();
324
- } catch (a) {
325
- return console.error(`[arsel] ${a instanceof Error ? a.message : a}`), !1;
326
- }
327
- const s = await i.pushManager.subscribe({
328
- // Non-negotiable: the Push API only accepts a userVisibleOnly subscription
329
- // in Chrome, and silent push is what the restriction exists to prevent.
330
- userVisibleOnly: !0,
331
- applicationServerKey: C(n.vapidPublicKey)
332
- });
333
- return L(s, n.keyVersion);
334
- }
335
- async function L(e, t) {
336
- var _;
337
- const [n, i, s] = await Promise.all([
338
- c(r.clientKey),
339
- c(r.baseUrl),
340
- T()
942
+ i = await be();
943
+ } catch (s) {
944
+ return console.error(`[arsel] ${s instanceof Error ? s.message : s}`), !1;
945
+ }
946
+ const r = await Xe(i, n.vapidPublicKey);
947
+ return r ? je(r, n.keyVersion) : !1;
948
+ }
949
+ async function je(e, t) {
950
+ var f, A;
951
+ const [n, i, r] = await Promise.all([
952
+ c(o.clientKey),
953
+ c(o.baseUrl),
954
+ X()
341
955
  ]);
342
956
  if (!n || !i) return !1;
343
- let a = await c(r.installationId);
344
- a || (a = crypto.randomUUID(), await u(r.installationId, a));
345
- const o = e.toJSON(), l = await R(
957
+ let s = await c(o.installationId);
958
+ s || (s = crypto.randomUUID(), await u(o.installationId, s));
959
+ const a = e.toJSON(), l = await z(
346
960
  i,
347
- X(n),
961
+ ye(n),
348
962
  {
349
- installationId: a,
963
+ installationId: s,
350
964
  platform: "web",
351
- endpoint: o.endpoint,
352
- keys: o.keys,
965
+ endpoint: a.endpoint,
966
+ keys: a.keys,
353
967
  // The same id the events API sends — it is what binds this subscription
354
968
  // to the contact the browser's events resolve to.
355
- anonymousId: s,
969
+ anonymousId: r,
356
970
  enablementStatus: Notification.permission === "granted" ? "AUTHORIZED" : "DENIED",
357
971
  deviceTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
358
972
  deviceLocale: navigator.language
359
973
  }
360
974
  );
361
- return l.result !== f.success ? !1 : ((_ = l.body) != null && _.deviceSecret && await u(r.deviceSecret, l.body.deviceSecret), await u(r.vapidKeyVersion, t), await u(r.endpoint, o.endpoint ?? null), !0);
975
+ if (l.result !== y.success) return !1;
976
+ (f = l.body) != null && f.deviceSecret && await u(o.deviceSecret, l.body.deviceSecret), await u(o.vapidKeyVersion, t), await u(o.endpoint, a.endpoint ?? null);
977
+ const w = ((A = l.body) == null ? void 0 : A.status) ?? null;
978
+ return await u(o.subscriptionStatus, w), w !== me;
362
979
  }
363
- async function Ae() {
980
+ async function pn() {
364
981
  const [e, t, n, i] = await Promise.all([
365
- c(r.clientKey),
366
- c(r.baseUrl),
367
- c(r.installationId),
368
- c(r.deviceSecret)
982
+ c(o.clientKey),
983
+ c(o.baseUrl),
984
+ c(o.installationId),
985
+ c(o.deviceSecret)
369
986
  ]);
370
- return !e || !t || !n || !i ? !1 : (await R(
987
+ return !e || !t || !n || !i || (await z(
371
988
  t,
372
- `${X(e)}/unsubscribe`,
989
+ `${ye(e)}/unsubscribe`,
373
990
  { installationId: n },
374
991
  { "X-Arsel-Device-Auth": i },
375
992
  !0
376
- )).result === f.success;
993
+ )).result !== y.success ? !1 : (await u(o.subscriptionStatus, me), !0);
377
994
  }
378
- async function Ne() {
379
- if (m === "none" || !b() || Notification.permission !== "granted") return;
995
+ async function mn() {
996
+ if (M === "none" || !B() || Notification.permission !== "granted") return;
380
997
  const [e, t] = await Promise.all([
381
- c(r.clientKey),
382
- c(r.baseUrl)
998
+ c(o.clientKey),
999
+ c(o.baseUrl)
383
1000
  ]);
384
1001
  if (!e || !t) return;
385
- const n = await B(t, F(e));
1002
+ const { body: n } = await de(
1003
+ t,
1004
+ Fe(e)
1005
+ );
386
1006
  if (!(n != null && n.vapidPublicKey)) return;
387
1007
  let i;
388
1008
  try {
389
- i = await $();
1009
+ i = await be();
390
1010
  } catch {
391
1011
  return;
392
1012
  }
393
- const s = await i.pushManager.getSubscription(), a = await c(r.vapidKeyVersion);
394
- if (s && a === n.keyVersion) return;
395
- s && await s.unsubscribe().catch(() => !1);
396
- const o = await i.pushManager.subscribe({
397
- userVisibleOnly: !0,
398
- applicationServerKey: C(n.vapidPublicKey)
399
- });
400
- await L(o, n.keyVersion);
1013
+ const r = await i.pushManager.getSubscription(), s = await c(o.vapidKeyVersion);
1014
+ if (r && s === n.keyVersion) return;
1015
+ r && await r.unsubscribe().catch(() => !1);
1016
+ const a = await Xe(i, n.vapidPublicKey);
1017
+ a && await je(a, n.keyVersion);
401
1018
  }
402
- async function ke() {
403
- if (!b()) return !1;
1019
+ async function yn() {
1020
+ if (!B() || await c(o.subscriptionStatus) === me)
1021
+ return !1;
404
1022
  try {
405
- const e = m === "managed" ? E : await navigator.serviceWorker.getRegistration();
1023
+ const e = M === "managed" ? W : await navigator.serviceWorker.getRegistration();
406
1024
  return e ? await e.pushManager.getSubscription() !== null : !1;
407
1025
  } catch {
408
1026
  return !1;
409
1027
  }
410
1028
  }
411
- const Pe = 30 * 6e4;
412
- async function z(e = Date.now()) {
413
- const t = await c(r.sessionStartedAt) ?? 0, n = await c(r.backgroundedAt) ?? 0;
1029
+ function bn() {
1030
+ return oe ?? (oe = hn().finally(() => {
1031
+ oe = null;
1032
+ })), oe;
1033
+ }
1034
+ let oe = null;
1035
+ async function hn() {
1036
+ var s;
1037
+ const [e, t, n] = await Promise.all([
1038
+ c(o.clientKey),
1039
+ c(o.baseUrl),
1040
+ X()
1041
+ ]);
1042
+ if (!e || !t) return !1;
1043
+ if (await c(o.deviceSecret)) return !0;
1044
+ let i = await c(o.installationId);
1045
+ i || (i = crypto.randomUUID(), await u(o.installationId, i));
1046
+ const r = await z(
1047
+ t,
1048
+ ye(e),
1049
+ {
1050
+ installationId: i,
1051
+ platform: "web",
1052
+ anonymousId: n,
1053
+ // Truthful: no prompt has been shown. Reporting DENIED would mark the
1054
+ // device unreachable for push it was never asked about.
1055
+ enablementStatus: "NOT_DETERMINED",
1056
+ deviceTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
1057
+ deviceLocale: navigator.language
1058
+ }
1059
+ );
1060
+ return r.code === 403 ? (console.error(
1061
+ "[arsel] this origin is not on the organization's allowed origins list — in-app messaging is disabled"
1062
+ ), !1) : r.result !== y.success ? !1 : ((s = r.body) != null && s.deviceSecret && await u(o.deviceSecret, r.body.deviceSecret), !0);
1063
+ }
1064
+ const wn = 30 * 6e4;
1065
+ async function ze(e = Date.now()) {
1066
+ const t = await c(o.sessionStartedAt) ?? 0, n = await c(o.backgroundedAt) ?? 0;
414
1067
  if (t !== 0) {
415
- if (n === 0 || e - n < Pe) return;
416
- await g(
417
- ce,
1068
+ if (n === 0 || e - n < wn) return;
1069
+ await k(
1070
+ tt,
418
1071
  { duration_seconds: Math.round((n - t) / 1e3) },
419
1072
  n
420
1073
  );
421
1074
  }
422
- await u(r.sessionStartedAt, e), await u(r.backgroundedAt, 0), await g(oe, {}, e);
1075
+ await u(o.sessionStartedAt, e), await u(o.backgroundedAt, 0), await k(et, {}, e);
423
1076
  }
424
- async function x(e = Date.now()) {
425
- (await c(r.sessionStartedAt) ?? 0) !== 0 && await u(r.backgroundedAt, e);
1077
+ async function ke(e = Date.now()) {
1078
+ (await c(o.sessionStartedAt) ?? 0) !== 0 && await u(o.backgroundedAt, e);
426
1079
  }
427
- function Te() {
1080
+ function gn(e) {
428
1081
  typeof document > "u" || (document.addEventListener("visibilitychange", () => {
429
- document.visibilityState === "visible" ? z() : x();
430
- }), window.addEventListener("pagehide", () => void x()));
1082
+ document.visibilityState === "visible" ? ze().then(() => e == null ? void 0 : e()) : ke();
1083
+ }), window.addEventListener("pagehide", () => void ke()));
431
1084
  }
432
- let d = null, J = !1;
433
- function p(e) {
434
- J && console.info(`[arsel] ${e}`);
1085
+ let b = null, ce = !1, R = null;
1086
+ function I(e) {
1087
+ ce && console.info(`[arsel] ${e}`);
435
1088
  }
436
- const Ke = /^\+[1-9]\d{6,14}$/, Oe = /^[^\s@]+@[^\s@]+\.[^\s@]+$/, M = 100;
437
- let P = [], j = !1;
438
- function De() {
439
- typeof window < "u" && window.addEventListener("online", () => void S().catch(() => {
1089
+ const vn = /^\+[1-9]\d{6,14}$/, Sn = /^[^\s@]+@[^\s@]+\.[^\s@]+$/, Pe = 100;
1090
+ let Q = [], Te = !1;
1091
+ function An() {
1092
+ typeof navigator < "u" && navigator.serviceWorker && navigator.serviceWorker.addEventListener("message", (e) => {
1093
+ const t = e.data;
1094
+ (t == null ? void 0 : t.type) === "arsel_iam_sync" && It();
1095
+ }), typeof window < "u" && (window.addEventListener("online", () => {
1096
+ V().catch(() => {
1097
+ }), Y().catch(() => {
1098
+ });
1099
+ }), window.addEventListener("pagehide", () => {
1100
+ ae(), Y().catch(() => {
1101
+ });
440
1102
  })), typeof document < "u" && document.addEventListener("visibilitychange", () => {
441
- document.visibilityState === "visible" && S().catch(() => {
1103
+ if (document.visibilityState === "visible") {
1104
+ V().catch(() => {
1105
+ }), te().catch(() => {
1106
+ });
1107
+ return;
1108
+ }
1109
+ ae(), Y().catch(() => {
442
1110
  });
443
1111
  });
444
1112
  }
445
- function Re(e) {
446
- return d || (d = (async () => {
447
- var a;
448
- if (!e.clientKey) throw new Error("Arsel: clientKey is required");
449
- const t = /^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(
450
- e.baseUrl ?? ""
451
- );
452
- if (!((a = e.baseUrl) != null && a.startsWith("https://")) && !t)
453
- throw new Error("Arsel: baseUrl must be HTTPS (http is allowed for localhost only)");
454
- J = e.debug ?? !1;
455
- const n = e.baseUrl.replace(/\/+$/, "");
1113
+ function U() {
1114
+ return R === null;
1115
+ }
1116
+ function En(e) {
1117
+ var n, i;
1118
+ if (!((n = e.clientKey) != null && n.trim()))
1119
+ return "clientKey is required (the org's publishable pub_… key)";
1120
+ if (!e.clientKey.startsWith("pub_"))
1121
+ return "clientKey should be the publishable pub_… key never a secret API key";
1122
+ const t = /^http:\/\/(localhost|127\.0\.0\.1)(:\d+)?(\/|$)/.test(
1123
+ e.baseUrl ?? ""
1124
+ );
1125
+ if (!((i = e.baseUrl) != null && i.startsWith("https://")) && !t)
1126
+ return "baseUrl must be HTTPS (http is allowed for localhost only)";
1127
+ try {
1128
+ new URL(e.baseUrl);
1129
+ } catch {
1130
+ return "baseUrl is not a valid URL";
1131
+ }
1132
+ return null;
1133
+ }
1134
+ function In(e) {
1135
+ return b || (b = (async () => {
1136
+ if (ce = e.debug ?? !1, R = En(e), R) {
1137
+ console.error(`[arsel] not started — ${R}`);
1138
+ return;
1139
+ }
1140
+ const t = e.baseUrl.replace(/\/+$/, "");
456
1141
  await Promise.all([
457
- u(r.clientKey, e.clientKey),
458
- u(r.baseUrl, n)
459
- ]), await T();
460
- const i = P;
461
- P = [];
462
- for (const o of i)
463
- await g(o.name, o.properties, o.timestampMs);
464
- e.serviceWorker === "external" ? ve() : e.serviceWorkerPath && await ge(e.serviceWorkerPath), Te(), De();
465
- const s = typeof document < "u" ? document : void 0;
466
- (s == null ? void 0 : s.visibilityState) === "visible" && s.prerendering !== !0 && await z(), S(), Ne(), p(`initialized (sdk ${K})`);
467
- })(), d);
468
- }
469
- async function $e(e, t = {}) {
1142
+ u(o.clientKey, e.clientKey),
1143
+ u(o.baseUrl, t)
1144
+ ]);
1145
+ const n = await on();
1146
+ await X(), await rn(n);
1147
+ const i = Q;
1148
+ Q = [];
1149
+ for (const l of i)
1150
+ await k(l.name, l.properties, l.timestampMs);
1151
+ e.serviceWorker === "external" ? an() : e.serviceWorkerPath && await cn(e.serviceWorkerPath);
1152
+ const r = Mn(e.inApp);
1153
+ gn(r ? Ie : void 0), An();
1154
+ const s = typeof document < "u" ? document : void 0, a = (s == null ? void 0 : s.visibilityState) === "visible" && s.prerendering !== !0;
1155
+ a && await ze(), r && (vt(Bn(r)), await bn() && (await gt(ce), a && Ie())), V().catch(() => {
1156
+ }), mn().catch(() => {
1157
+ }), I(`initialized (sdk ${j})`);
1158
+ })(), b);
1159
+ }
1160
+ async function He(e, t = {}) {
470
1161
  const n = e == null ? void 0 : e.trim();
471
1162
  if (!n) {
472
- p("track() called with a blank event name — ignoring");
1163
+ I("track() called with a blank event name — ignoring");
473
1164
  return;
474
1165
  }
475
- if (n.startsWith(v)) {
476
- p(`'${v}' is reserved for the SDK — ignoring '${n}'`);
1166
+ if (n.startsWith(x)) {
1167
+ I(`'${x}' is reserved for the SDK — ignoring '${n}'`);
477
1168
  return;
478
1169
  }
479
- if (!d) {
480
- if (P.length >= M) {
481
- j || (j = !0, console.warn(
482
- `[arsel] more than ${M} events tracked before init() — dropping the rest. Call init() earlier.`
1170
+ if (!b) {
1171
+ if (Q.length >= Pe) {
1172
+ Te || (Te = !0, console.warn(
1173
+ `[arsel] more than ${Pe} events tracked before init() — dropping the rest. Call init() earlier.`
483
1174
  ));
484
1175
  return;
485
1176
  }
486
- P.push({ name: n, properties: t, timestampMs: Date.now() });
1177
+ Q.push({ name: n, properties: t, timestampMs: Date.now() });
487
1178
  return;
488
1179
  }
489
- await d, await g(n, t);
1180
+ await b, U() && (await k(n, t), pe("CUSTOM_EVENT", n, t));
1181
+ }
1182
+ async function _n(e, t = {}) {
1183
+ const n = e == null ? void 0 : e.trim();
1184
+ if (!n) {
1185
+ I("screen() called with a blank name — ignoring");
1186
+ return;
1187
+ }
1188
+ await b, await k(rt, { ...t, screen_name: n }), pe("SCREEN_VIEW", n, t);
1189
+ }
1190
+ function Nn(e) {
1191
+ St(e);
490
1192
  }
491
- async function Ue(e) {
492
- await d;
1193
+ async function xn(e) {
1194
+ if (await b, !U()) return;
493
1195
  let { email: t, phoneNumber: n } = e ?? {};
494
1196
  const i = e == null ? void 0 : e.externalId;
495
- if (t && !Oe.test(t) && (console.error(`[arsel] identify(): '${t}' is not a valid email address — ignored`), t = void 0), n && !Ke.test(n) && (console.error(
1197
+ if (t && !Sn.test(t) && (console.error(`[arsel] identify(): '${t}' is not a valid email address — ignored`), t = void 0), n && !vn.test(n) && (console.error(
496
1198
  `[arsel] identify(): '${n}' is not E.164 (e.g. +966501234567) — ignored`
497
1199
  ), n = void 0), !i && !t && !n) {
498
- p("identify() needs at least one of externalId, email or phoneNumber");
1200
+ I("identify() needs at least one of externalId, email or phoneNumber");
499
1201
  return;
500
1202
  }
501
- i && await u(r.externalId, i), t && await u(r.email, t), n && await u(r.phoneNumber, n), await g(le, {});
1203
+ i && await u(o.externalId, i), t && await u(o.email, t), n && await u(o.phoneNumber, n), await k(nt, {}), await Ce();
502
1204
  }
503
- async function Ve() {
504
- await d, await Promise.all([
505
- A(r.externalId),
506
- A(r.email),
507
- A(r.phoneNumber),
508
- A(r.sessionStartedAt)
509
- ]), await ie(), p("identity reset");
1205
+ async function kn() {
1206
+ await b, U() && (await Promise.all([
1207
+ L(o.externalId),
1208
+ L(o.email),
1209
+ L(o.phoneNumber),
1210
+ L(o.sessionStartedAt)
1211
+ ]), await Ze(), await Ce(), I("identity reset"));
510
1212
  }
511
- async function xe() {
512
- await d;
513
- const e = await Ae();
514
- return p(e ? "push opt-out recorded" : "push opt-out not sent — no registration to revoke"), e;
1213
+ async function Pn() {
1214
+ if (await b, !U()) return !1;
1215
+ const e = await pn();
1216
+ return I(e ? "push opt-out recorded" : "push opt-out not sent — no registration to revoke"), e;
515
1217
  }
516
- async function Me() {
517
- if (await d, !b()) return !1;
518
- const e = await _e();
1218
+ async function Tn() {
1219
+ if (await b, !U() || !B()) return !1;
1220
+ const e = await dn();
519
1221
  if (e)
520
1222
  return console.error(`[arsel] promptForPush(): ${e}`), !1;
521
1223
  const t = await Notification.requestPermission();
522
- return t !== "granted" ? (p(`notification permission: ${t}`), !1) : Ie();
1224
+ return t !== "granted" ? (I(`notification permission: ${t}`), !1) : fn();
523
1225
  }
524
- async function je() {
525
- return await d, T();
1226
+ async function Dn() {
1227
+ return await b, X();
526
1228
  }
527
- async function We() {
1229
+ async function On() {
528
1230
  const [
529
1231
  e,
530
1232
  t,
531
1233
  n,
532
1234
  i,
1235
+ r,
533
1236
  s,
534
1237
  a,
535
- o,
536
1238
  l,
537
- _,
538
- q,
539
- Y,
540
- Z
1239
+ w,
1240
+ f,
1241
+ A,
1242
+ C,
1243
+ E
541
1244
  ] = await Promise.all([
542
- c(r.anonymousId),
543
- c(r.externalId),
544
- c(r.email),
545
- c(r.phoneNumber),
546
- c(r.installationId),
547
- c(r.deviceSecret),
548
- c(r.vapidKeyVersion),
549
- re(),
550
- ke(),
551
- c(r.lastResponseCode),
552
- c(r.lastResponsePath),
553
- c(r.lastResponseAtMs)
554
- ]);
1245
+ c(o.anonymousId),
1246
+ c(o.externalId),
1247
+ c(o.email),
1248
+ c(o.phoneNumber),
1249
+ c(o.installationId),
1250
+ c(o.deviceSecret),
1251
+ c(o.vapidKeyVersion),
1252
+ Oe(S.events),
1253
+ yn(),
1254
+ c(o.subscriptionStatus),
1255
+ c(o.lastResponseCode),
1256
+ c(o.lastResponsePath),
1257
+ c(o.lastResponseAtMs)
1258
+ ]), T = At(), he = await Et().catch(() => 0);
555
1259
  return {
556
- sdkVersion: K,
557
- initialized: d !== null,
1260
+ sdkVersion: j,
1261
+ initialized: b !== null && R === null,
1262
+ configError: R,
1263
+ inAppMessages: T.messages,
1264
+ inAppBundleVersion: T.bundleVersion,
1265
+ inAppFetchedAtMs: T.fetchedAtMs,
1266
+ pendingInAppBeacons: he,
558
1267
  anonymousId: e,
559
1268
  hasAssertedIdentity: !!(t || n || i),
560
- installationId: s,
561
- hasDeviceSecret: !!a,
1269
+ installationId: r,
1270
+ hasDeviceSecret: !!s,
562
1271
  pendingEvents: l,
563
- permission: b() ? Notification.permission : "unsupported",
564
- isSubscribed: _,
565
- vapidKeyVersion: o,
566
- lastResponseCode: q,
567
- lastResponsePath: Y,
568
- lastResponseAtMs: Z
1272
+ permission: B() ? Notification.permission : "unsupported",
1273
+ isSubscribed: w,
1274
+ subscriptionStatus: f,
1275
+ vapidKeyVersion: a,
1276
+ lastResponseCode: A,
1277
+ lastResponsePath: C,
1278
+ lastResponseAtMs: E
569
1279
  };
570
1280
  }
571
- async function Be() {
572
- await d, await S();
573
- }
574
- const Ce = {
575
- init: Re,
576
- track: $e,
577
- identify: Ue,
578
- reset: Ve,
579
- optOut: xe,
580
- promptForPush: Me,
581
- getAnonymousId: je,
582
- diagnostics: We,
583
- flushNow: Be,
584
- SDK_VERSION: K
1281
+ async function Rn() {
1282
+ await b, U() && await V();
1283
+ }
1284
+ const Cn = {
1285
+ init: In,
1286
+ track: He,
1287
+ screen: _n,
1288
+ suppressInAppMessages: Nn,
1289
+ identify: xn,
1290
+ reset: kn,
1291
+ optOut: Pn,
1292
+ promptForPush: Tn,
1293
+ getAnonymousId: Dn,
1294
+ diagnostics: On,
1295
+ flushNow: Rn,
1296
+ SDK_VERSION: j
585
1297
  };
1298
+ function Mn(e) {
1299
+ if (e === !1) return;
1300
+ const t = typeof e == "object" && e !== null ? e : {};
1301
+ return {
1302
+ zIndex: t.zIndex ?? Un,
1303
+ closeLabel: t.closeLabel ?? "Close"
1304
+ };
1305
+ }
1306
+ function Bn(e) {
1307
+ return (t) => {
1308
+ const n = Ot();
1309
+ try {
1310
+ Xt(t, e, {
1311
+ onImpression: () => void Rt(t, n),
1312
+ onButton: (i) => {
1313
+ Mt(t, i.buttonId), i.action === "CUSTOM_EVENT" && i.value && He(i.value);
1314
+ },
1315
+ onDismiss: (i) => {
1316
+ Bt(t, i), _e();
1317
+ }
1318
+ });
1319
+ } catch (i) {
1320
+ I(`in-app render failed: ${i instanceof Error ? i.message : ""}`), _e();
1321
+ }
1322
+ };
1323
+ }
1324
+ const Un = 2147483e3;
586
1325
  export {
587
- Ce as Arsel,
588
- K as SDK_VERSION,
589
- Ce as default,
590
- We as diagnostics,
591
- Be as flushNow,
592
- je as getAnonymousId,
593
- Ue as identify,
594
- Re as init,
595
- xe as optOut,
596
- Me as promptForPush,
597
- Ve as reset,
598
- $e as track
1326
+ Cn as Arsel,
1327
+ j as SDK_VERSION,
1328
+ Cn as default,
1329
+ On as diagnostics,
1330
+ Rn as flushNow,
1331
+ Dn as getAnonymousId,
1332
+ xn as identify,
1333
+ In as init,
1334
+ Pn as optOut,
1335
+ Tn as promptForPush,
1336
+ kn as reset,
1337
+ _n as screen,
1338
+ Nn as suppressInAppMessages,
1339
+ He as track
599
1340
  };
600
1341
  //# sourceMappingURL=arsel.js.map