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