@suprsend/web-sdk 1.5.1 → 2.0.1

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.
@@ -0,0 +1,1111 @@
1
+ var k = Object.defineProperty;
2
+ var x = (i, e, t) => e in i ? k(i, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : i[e] = t;
3
+ var d = (i, e, t) => x(i, typeof e != "symbol" ? e + "" : e, t);
4
+ var p = /* @__PURE__ */ ((i) => (i.OPT_IN = "opt_in", i.OPT_OUT = "opt_out", i))(p || {}), v = /* @__PURE__ */ ((i) => (i.ALL = "all", i.REQUIRED = "required", i))(v || {}), o = /* @__PURE__ */ ((i) => (i.VALIDATION_ERROR = "VALIDATION_ERROR", i.NETWORK_ERROR = "NETWORK_ERROR", i.UNKNOWN_ERROR = "UNKNOWN_ERROR", i.PERMISSION_DENIED = "PERMISSION_DENIED", i.UNSUPPORTED_ACTION = "UNSUPPORTED_ACTION", i))(o || {}), n = /* @__PURE__ */ ((i) => (i.SUCCESS = "success", i.ERROR = "error", i))(n || {});
5
+ function b() {
6
+ let i = (/* @__PURE__ */ new Date()).getTime();
7
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(
8
+ /[xy]/g,
9
+ function(t) {
10
+ const r = (i + Math.random() * 16) % 16 | 0;
11
+ return i = Math.floor(i / 16), (t == "x" ? r : r & 3 | 8).toString(16);
12
+ }
13
+ );
14
+ }
15
+ function C() {
16
+ return Math.round(Date.now());
17
+ }
18
+ function f(i) {
19
+ return Object.keys(i).length === 0;
20
+ }
21
+ function $(i) {
22
+ return (i == null ? void 0 : i.length) <= 0;
23
+ }
24
+ function j(i) {
25
+ const e = "=".repeat((4 - i.length % 4) % 4), t = (i + e).replace(/-/g, "+").replace(/_/g, "/"), r = atob(t), s = new Uint8Array(r.length);
26
+ for (let u = 0; u < r.length; ++u)
27
+ s[u] = r.charCodeAt(u);
28
+ return s;
29
+ }
30
+ function K(i, e) {
31
+ let t;
32
+ return (...r) => (clearTimeout(t), new Promise((s) => {
33
+ t = setTimeout(() => s(i(...r)), e);
34
+ }));
35
+ }
36
+ function P(i, e) {
37
+ const t = {};
38
+ return (...r) => {
39
+ const [s] = r, u = r.slice(1);
40
+ return typeof t[s] == "function" ? t[s](...u) : (t[s] = K(i, e), t[s](...u));
41
+ };
42
+ }
43
+ function a(i) {
44
+ const e = { status: i.status };
45
+ return i.statusCode && (e.statusCode = i.statusCode), i.body && (e.body = i.body), i.status === n.ERROR && (e.error = {
46
+ type: i.errorType,
47
+ message: i.errorMessage
48
+ }), e;
49
+ }
50
+ function g() {
51
+ return typeof window < "u";
52
+ }
53
+ function M(i, e) {
54
+ g() && (typeof e == "object" && (e = JSON.stringify(e)), localStorage.setItem(i, e));
55
+ }
56
+ function V(i) {
57
+ if (!g()) return;
58
+ const e = localStorage.getItem(i);
59
+ if (e)
60
+ try {
61
+ return JSON.parse(e);
62
+ } catch {
63
+ return e;
64
+ }
65
+ }
66
+ function W(i) {
67
+ g() && localStorage.removeItem(i);
68
+ }
69
+ function N(i) {
70
+ this.message = i;
71
+ }
72
+ N.prototype = new Error(), N.prototype.name = "InvalidCharacterError";
73
+ var D = typeof window < "u" && window.atob && window.atob.bind(window) || function(i) {
74
+ var e = String(i).replace(/=+$/, "");
75
+ if (e.length % 4 == 1) throw new N("'atob' failed: The string to be decoded is not correctly encoded.");
76
+ for (var t, r, s = 0, u = 0, c = ""; r = e.charAt(u++); ~r && (t = s % 4 ? 64 * t + r : r, s++ % 4) ? c += String.fromCharCode(255 & t >> (-2 * s & 6)) : 0) r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(r);
77
+ return c;
78
+ };
79
+ function F(i) {
80
+ var e = i.replace(/-/g, "+").replace(/_/g, "/");
81
+ switch (e.length % 4) {
82
+ case 0:
83
+ break;
84
+ case 2:
85
+ e += "==";
86
+ break;
87
+ case 3:
88
+ e += "=";
89
+ break;
90
+ default:
91
+ throw "Illegal base64url string!";
92
+ }
93
+ try {
94
+ return function(t) {
95
+ return decodeURIComponent(D(t).replace(/(.)/g, function(r, s) {
96
+ var u = s.charCodeAt(0).toString(16).toUpperCase();
97
+ return u.length < 2 && (u = "0" + u), "%" + u;
98
+ }));
99
+ }(e);
100
+ } catch {
101
+ return D(e);
102
+ }
103
+ }
104
+ function _(i) {
105
+ this.message = i;
106
+ }
107
+ function L(i, e) {
108
+ if (typeof i != "string") throw new _("Invalid token specified");
109
+ var t = (e = e || {}).header === !0 ? 0 : 1;
110
+ try {
111
+ return JSON.parse(F(i.split(".")[t]));
112
+ } catch (r) {
113
+ throw new _("Invalid token specified: " + r.message);
114
+ }
115
+ }
116
+ _.prototype = new Error(), _.prototype.name = "InvalidTokenError";
117
+ class S {
118
+ constructor(e) {
119
+ d(this, "config");
120
+ this.config = e;
121
+ }
122
+ getUrl(e) {
123
+ return `${this.config.host}/${e}`;
124
+ }
125
+ getHeaders() {
126
+ const e = {
127
+ "Content-Type": "application/json",
128
+ Authorization: this.config.publicApiKey
129
+ };
130
+ return this.config.userToken && (e["x-ss-signature"] = this.config.userToken), e;
131
+ }
132
+ requestApiInstance(e) {
133
+ switch (e.type) {
134
+ case "get":
135
+ return this.get(e.path);
136
+ case "post":
137
+ return this.post(e.path, (e == null ? void 0 : e.payload) || {});
138
+ case "patch":
139
+ return this.patch(e.path, (e == null ? void 0 : e.payload) || {});
140
+ default:
141
+ return this.get(e.path);
142
+ }
143
+ }
144
+ get(e) {
145
+ const t = this.getUrl(e);
146
+ return fetch(t, {
147
+ method: "GET",
148
+ headers: this.getHeaders()
149
+ });
150
+ }
151
+ post(e, t) {
152
+ const r = this.getUrl(e);
153
+ return fetch(r, {
154
+ method: "POST",
155
+ body: JSON.stringify(t),
156
+ headers: this.getHeaders()
157
+ });
158
+ }
159
+ patch(e, t) {
160
+ const r = this.getUrl(e);
161
+ return fetch(r, {
162
+ method: "PATCH",
163
+ body: JSON.stringify(t),
164
+ headers: this.getHeaders()
165
+ });
166
+ }
167
+ async request(e) {
168
+ var t, r, s;
169
+ if (!this.config.distinctId)
170
+ return a({
171
+ status: n.ERROR,
172
+ errorType: o.VALIDATION_ERROR,
173
+ errorMessage: "User isn't authenticated. Call identify method before performing any action"
174
+ });
175
+ if ((t = this.config.authenticateOptions) != null && t.refreshUserToken && this.config.userToken) {
176
+ const u = L(this.config.userToken), c = (u.exp || 0) * 1e3, h = Date.now();
177
+ if (c <= h)
178
+ try {
179
+ const R = await this.config.authenticateOptions.refreshUserToken(
180
+ this.config.userToken,
181
+ u
182
+ );
183
+ R && typeof R == "string" && this.config.identify(
184
+ this.config.distinctId,
185
+ R,
186
+ this.config.authenticateOptions
187
+ );
188
+ } catch {
189
+ }
190
+ }
191
+ try {
192
+ const u = await this.requestApiInstance(e), c = await u.json(), h = (c == null ? void 0 : c.status) || (u.ok ? n.SUCCESS : n.ERROR);
193
+ return a({
194
+ status: h,
195
+ body: c,
196
+ statusCode: u.status,
197
+ errorMessage: (r = c == null ? void 0 : c.error) == null ? void 0 : r.message,
198
+ errorType: (s = c == null ? void 0 : c.error) == null ? void 0 : s.type
199
+ });
200
+ } catch (u) {
201
+ return console.error(u), a({
202
+ status: n.ERROR,
203
+ statusCode: 500,
204
+ errorMessage: (u == null ? void 0 : u.message) || "network error",
205
+ errorType: o.NETWORK_ERROR
206
+ });
207
+ }
208
+ }
209
+ }
210
+ class H {
211
+ constructor(e) {
212
+ d(this, "config");
213
+ d(this, "preferenceData");
214
+ d(this, "preferenceArgs");
215
+ d(this, "debouncedUpdateCategoryPreferences");
216
+ d(this, "debouncedUpdateChannelPreferences");
217
+ d(this, "debounceTime", 1e3);
218
+ this.config = e, this.debouncedUpdateCategoryPreferences = P(
219
+ this._updateCategoryPreferences.bind(this),
220
+ this.debounceTime
221
+ ), this.debouncedUpdateChannelPreferences = P(
222
+ this._updateChannelPreferences.bind(this),
223
+ this.debounceTime
224
+ );
225
+ }
226
+ validateQueryParams(e = {}) {
227
+ const t = {};
228
+ for (const r in e)
229
+ e[r] && (t[r] = String(e[r]));
230
+ return t;
231
+ }
232
+ set data(e) {
233
+ this.preferenceData = e;
234
+ }
235
+ get data() {
236
+ return this.preferenceData;
237
+ }
238
+ getUrlPath(e, t) {
239
+ const r = `v2/subscriber/${this.config.distinctId}/${e}`, s = this.validateQueryParams(t), u = new URLSearchParams(
240
+ s
241
+ ).toString();
242
+ return u ? `${r}/?${u}` : r;
243
+ }
244
+ /**
245
+ * Used to get user's whole preferences data.
246
+ */
247
+ async getPreferences(e) {
248
+ const t = {
249
+ tenant_id: e == null ? void 0 : e.tenantId,
250
+ show_opt_out_channels: (e == null ? void 0 : e.showOptOutChannels) !== !1
251
+ };
252
+ this.preferenceArgs = {
253
+ tenantId: t == null ? void 0 : t.tenant_id,
254
+ showOptOutChannels: t == null ? void 0 : t.show_opt_out_channels
255
+ };
256
+ const r = this.getUrlPath("full_preference", t), s = await this.config.client().request({ type: "get", path: r });
257
+ return s.error || (this.data = s.body), s;
258
+ }
259
+ /**
260
+ * Used to get user's preference of all categories.
261
+ */
262
+ async getCategories(e) {
263
+ const t = {
264
+ tenant_id: e == null ? void 0 : e.tenantId,
265
+ show_opt_out_channels: (e == null ? void 0 : e.showOptOutChannels) !== !1,
266
+ limit: e == null ? void 0 : e.limit,
267
+ offset: e == null ? void 0 : e.offset
268
+ }, r = this.getUrlPath("category", t);
269
+ return await this.config.client().request({ type: "get", path: r });
270
+ }
271
+ /**
272
+ * Used to get user's preference of specific category.
273
+ */
274
+ async getCategory(e, t) {
275
+ if (!e)
276
+ return a({
277
+ status: n.ERROR,
278
+ errorType: o.VALIDATION_ERROR,
279
+ errorMessage: "Category parameter is missing"
280
+ });
281
+ const r = {
282
+ tenant_id: t == null ? void 0 : t.tenantId,
283
+ show_opt_out_channels: (t == null ? void 0 : t.showOptOutChannels) !== !1
284
+ }, s = this.getUrlPath(`category/${e}`, r);
285
+ return await this.config.client().request({ type: "get", path: s });
286
+ }
287
+ /**
288
+ * Used to get user's all channel level preference.
289
+ */
290
+ async getOverallChannelPreferences() {
291
+ const e = this.getUrlPath("channel_preference");
292
+ return await this.config.client().request({ type: "get", path: e });
293
+ }
294
+ async _updateCategoryPreferences(e, t, r, s) {
295
+ const u = this.getUrlPath(`category/${e}`, s), c = await this.config.client().request({ type: "patch", path: u, payload: t });
296
+ return c != null && c.error ? this.config.emitter.emit("preferences_error", c) : (Object.assign(r, c.body), this.config.emitter.emit("preferences_updated", {
297
+ status: n.SUCCESS,
298
+ statusCode: 200,
299
+ body: this.data
300
+ })), c;
301
+ }
302
+ async _updateChannelPreferences(e) {
303
+ const t = this.getUrlPath("channel_preference"), r = await this.config.client().request({ type: "patch", path: t, payload: e });
304
+ return r != null && r.error ? this.config.emitter.emit("preferences_error", r) : (await this.getPreferences(this.preferenceArgs), this.config.emitter.emit("preferences_updated", {
305
+ status: n.SUCCESS,
306
+ statusCode: 200,
307
+ body: this.data
308
+ })), r;
309
+ }
310
+ /**
311
+ * Used to update user's category level preference.
312
+ */
313
+ async updateCategoryPreference(e, t, r) {
314
+ var R;
315
+ if (!e || ![p.OPT_IN, p.OPT_OUT].includes(
316
+ t
317
+ ))
318
+ return a({
319
+ status: n.ERROR,
320
+ errorType: o.VALIDATION_ERROR,
321
+ errorMessage: e ? "Preference parameter is invalid" : "Category parameter is missing"
322
+ });
323
+ if (!this.data)
324
+ return a({
325
+ status: n.ERROR,
326
+ errorType: o.VALIDATION_ERROR,
327
+ errorMessage: "Call getPreferences method before performing action"
328
+ });
329
+ if (!this.data.sections)
330
+ return a({
331
+ status: n.ERROR,
332
+ errorType: o.VALIDATION_ERROR,
333
+ errorMessage: "Sections doesn't exist"
334
+ });
335
+ let s = null, u = !1;
336
+ for (const y of this.data.sections) {
337
+ let E = !1;
338
+ if (y.subcategories) {
339
+ for (const l of y.subcategories)
340
+ if (l.category === e)
341
+ if (s = l, l.is_editable) {
342
+ if (l.preference !== t) {
343
+ l.preference = t, u = !0, E = !0;
344
+ break;
345
+ }
346
+ } else
347
+ return a({
348
+ status: n.ERROR,
349
+ errorType: o.VALIDATION_ERROR,
350
+ errorMessage: "Category preference is not editable"
351
+ });
352
+ if (E) break;
353
+ }
354
+ }
355
+ if (!s)
356
+ return a({
357
+ status: n.ERROR,
358
+ errorType: o.VALIDATION_ERROR,
359
+ errorMessage: "Category not found"
360
+ });
361
+ if (!u)
362
+ return a({
363
+ status: n.SUCCESS,
364
+ body: this.data
365
+ });
366
+ const c = [];
367
+ (R = s == null ? void 0 : s.channels) == null || R.forEach((y) => {
368
+ y.preference === p.OPT_OUT && c.push(y.channel);
369
+ });
370
+ const h = (r == null ? void 0 : r.showOptOutChannels) !== !1, O = {
371
+ preference: s.preference,
372
+ opt_out_channels: h && t === p.OPT_IN ? null : c
373
+ };
374
+ return this.debouncedUpdateCategoryPreferences(
375
+ e,
376
+ e,
377
+ O,
378
+ s,
379
+ { tenant_id: r == null ? void 0 : r.tenantId, show_opt_out_channels: h }
380
+ ), a({
381
+ status: n.SUCCESS,
382
+ body: this.data
383
+ });
384
+ }
385
+ /**
386
+ * Used to update user's category level channel preference.
387
+ */
388
+ async updateChannelPreferenceInCategory(e, t, r, s) {
389
+ var l;
390
+ if (!e || !r)
391
+ return a({
392
+ status: n.ERROR,
393
+ errorType: o.VALIDATION_ERROR,
394
+ errorMessage: e ? "Category parameter is missing" : "Channel parameter is missing"
395
+ });
396
+ if (![p.OPT_IN, p.OPT_OUT].includes(
397
+ t
398
+ ))
399
+ return a({
400
+ status: n.ERROR,
401
+ errorType: o.VALIDATION_ERROR,
402
+ errorMessage: "Preference parameter is invalid"
403
+ });
404
+ if (!this.data)
405
+ return a({
406
+ status: n.ERROR,
407
+ errorType: o.VALIDATION_ERROR,
408
+ errorMessage: "Call getPreferences method before performing action"
409
+ });
410
+ if (!this.data.sections)
411
+ return a({
412
+ status: n.ERROR,
413
+ errorType: o.VALIDATION_ERROR,
414
+ errorMessage: "Sections doesn't exist"
415
+ });
416
+ let u = null, c = null, h = !1;
417
+ for (const T of this.data.sections) {
418
+ let A = !1;
419
+ if (T.subcategories) {
420
+ for (const m of T.subcategories) {
421
+ if (m.category === r) {
422
+ if (u = m, !m.channels) continue;
423
+ for (const I of m.channels)
424
+ if (I.channel === e)
425
+ if (c = I, I.is_editable) {
426
+ if (I.preference !== t) {
427
+ I.preference = t, t === p.OPT_IN && (m.preference = p.OPT_IN), h = !0, A = !0;
428
+ break;
429
+ }
430
+ } else
431
+ return a({
432
+ status: n.ERROR,
433
+ errorType: o.VALIDATION_ERROR,
434
+ errorMessage: "Channel preference is not editable"
435
+ });
436
+ }
437
+ if (A) break;
438
+ }
439
+ if (A) break;
440
+ }
441
+ }
442
+ if (!u)
443
+ return a({
444
+ status: n.ERROR,
445
+ errorType: o.VALIDATION_ERROR,
446
+ errorMessage: "Category not found"
447
+ });
448
+ if (!c)
449
+ return a({
450
+ status: n.ERROR,
451
+ errorType: o.VALIDATION_ERROR,
452
+ errorMessage: "Category's channel not found"
453
+ });
454
+ if (!h)
455
+ return a({
456
+ status: n.SUCCESS,
457
+ body: this.data
458
+ });
459
+ const O = [];
460
+ (l = u == null ? void 0 : u.channels) == null || l.forEach((T) => {
461
+ T.preference === p.OPT_OUT && O.push(T.channel);
462
+ });
463
+ const R = (s == null ? void 0 : s.showOptOutChannels) !== !1, E = {
464
+ preference: R && u.preference === p.OPT_OUT && t === p.OPT_IN ? p.OPT_IN : u.preference,
465
+ opt_out_channels: O
466
+ };
467
+ return this.debouncedUpdateCategoryPreferences(
468
+ r,
469
+ r,
470
+ E,
471
+ u,
472
+ { tenant_id: s == null ? void 0 : s.tenantId, show_opt_out_channels: R }
473
+ ), a({
474
+ status: n.SUCCESS,
475
+ body: this.data
476
+ });
477
+ }
478
+ /**
479
+ * Used to update user's channel level preference.
480
+ */
481
+ async updateOverallChannelPreference(e, t) {
482
+ if (!e || ![
483
+ v.ALL,
484
+ v.REQUIRED
485
+ ].includes(t))
486
+ return a({
487
+ status: n.ERROR,
488
+ errorType: o.VALIDATION_ERROR,
489
+ errorMessage: e ? "Preference parameter is invalid" : "Channel parameter is missing"
490
+ });
491
+ if (!this.data)
492
+ return a({
493
+ status: n.ERROR,
494
+ errorType: o.VALIDATION_ERROR,
495
+ errorMessage: "Call getPreferences method before performing action"
496
+ });
497
+ if (!this.data.channel_preferences)
498
+ return a({
499
+ status: n.ERROR,
500
+ errorType: o.VALIDATION_ERROR,
501
+ errorMessage: "Channel preferences doesn't exist"
502
+ });
503
+ let r = null, s = !1;
504
+ const u = t === v.REQUIRED;
505
+ for (const c of this.data.channel_preferences)
506
+ if (c.channel === e && (r = c, c.is_restricted !== u)) {
507
+ c.is_restricted = u, s = !0;
508
+ break;
509
+ }
510
+ return r ? s ? (this.debouncedUpdateChannelPreferences(r.channel, {
511
+ channel_preferences: [r]
512
+ }), a({
513
+ status: n.SUCCESS,
514
+ body: this.data
515
+ })) : a({
516
+ status: n.SUCCESS,
517
+ body: this.data
518
+ }) : a({
519
+ status: n.ERROR,
520
+ errorType: o.VALIDATION_ERROR,
521
+ errorMessage: "Channel data not found"
522
+ });
523
+ }
524
+ }
525
+ const U = "ss_device_id";
526
+ class Q {
527
+ constructor(e) {
528
+ d(this, "config");
529
+ d(this, "preferences");
530
+ this.config = e, this.preferences = new H(e);
531
+ }
532
+ isReservedKey(e) {
533
+ var t;
534
+ return e.startsWith("$") || ((t = e == null ? void 0 : e.toLowerCase()) == null ? void 0 : t.startsWith("ss_"));
535
+ }
536
+ formatParamsToObj(e, t) {
537
+ let r = null;
538
+ return typeof e == "object" && t === void 0 ? r = e : typeof e == "string" && t !== void 0 ? r = { [e]: t } : console.warn("[SuprSend]: Invalid input parameters"), r;
539
+ }
540
+ formatParamsToArray(e) {
541
+ if (e)
542
+ return Array.isArray(e) ? e : [e];
543
+ }
544
+ validateObjData(e, t) {
545
+ const r = {}, s = (t == null ? void 0 : t.allowReservedKeys) || !1, u = (t == null ? void 0 : t.valueType) || "";
546
+ for (const c in e) {
547
+ let h = e[c];
548
+ if (!(c && h === void 0)) {
549
+ if (!s && this.isReservedKey(c)) {
550
+ console.warn("[SuprSend]: key cannot start with $ or ss_");
551
+ continue;
552
+ }
553
+ u === "number" ? h = Number(h) : u === "boolean" && (h = !!h), r[c] = h;
554
+ }
555
+ }
556
+ return r;
557
+ }
558
+ validateArrayData(e) {
559
+ const t = [];
560
+ for (const r of e)
561
+ if (r != null) {
562
+ if (this.isReservedKey(r)) {
563
+ console.warn("[SuprSend]: key cannot start with $ or ss_");
564
+ continue;
565
+ }
566
+ t.push(String(r));
567
+ }
568
+ return t;
569
+ }
570
+ async triggerUserEvent(e) {
571
+ return this.config.eventApi({
572
+ distinct_id: this.config.distinctId,
573
+ $insert_id: b(),
574
+ $time: C(),
575
+ ...e
576
+ });
577
+ }
578
+ /**
579
+ * Used to set user properties. Keys with $ and ss_ will be removed.
580
+ */
581
+ async set(e, t) {
582
+ const r = this.formatParamsToObj(e, t);
583
+ if (!r)
584
+ return a({
585
+ status: n.ERROR,
586
+ errorType: o.VALIDATION_ERROR,
587
+ errorMessage: "data provided is empty"
588
+ });
589
+ const s = this.validateObjData(r);
590
+ return f(s) ? a({
591
+ status: n.ERROR,
592
+ errorType: o.VALIDATION_ERROR,
593
+ errorMessage: "data provided is empty"
594
+ }) : this.triggerUserEvent({ $set: s });
595
+ }
596
+ /**
597
+ * Used to set user properties only once. Properties once set cannot be changed later.
598
+ * Keys with $ and ss_ will be removed.
599
+ */
600
+ async setOnce(e, t) {
601
+ const r = this.formatParamsToObj(e, t);
602
+ if (!r)
603
+ return a({
604
+ status: n.ERROR,
605
+ errorType: o.VALIDATION_ERROR,
606
+ errorMessage: "data provided is empty"
607
+ });
608
+ const s = this.validateObjData(r);
609
+ return f(s) ? a({
610
+ status: n.ERROR,
611
+ errorType: o.VALIDATION_ERROR,
612
+ errorMessage: "data provided is empty"
613
+ }) : this.triggerUserEvent({ $set_once: s });
614
+ }
615
+ /**
616
+ * Used to increment/decrement user properties whose values are numbers. To decrement use -ve values.
617
+ * Keys with $ and ss_ will be removed.
618
+ */
619
+ async increment(e, t) {
620
+ const r = this.formatParamsToObj(e, t);
621
+ if (!r)
622
+ return a({
623
+ status: n.ERROR,
624
+ errorType: o.VALIDATION_ERROR,
625
+ errorMessage: "data provided is empty"
626
+ });
627
+ const s = this.validateObjData(r, { valueType: "number" });
628
+ return f(s) ? a({
629
+ status: n.ERROR,
630
+ errorType: o.VALIDATION_ERROR,
631
+ errorMessage: "data provided is empty"
632
+ }) : this.triggerUserEvent({ $add: s });
633
+ }
634
+ /**
635
+ * Used to add items to list if user property is list (example: wishlist: [iphone, macbook]).
636
+ * Keys with $ and ss_ will be removed.
637
+ */
638
+ async append(e, t) {
639
+ const r = this.formatParamsToObj(e, t);
640
+ if (!r)
641
+ return a({
642
+ status: n.ERROR,
643
+ errorType: o.VALIDATION_ERROR,
644
+ errorMessage: "data provided is empty"
645
+ });
646
+ const s = this.validateObjData(r);
647
+ return f(s) ? a({
648
+ status: n.ERROR,
649
+ errorType: o.VALIDATION_ERROR,
650
+ errorMessage: "data provided is empty"
651
+ }) : this.triggerUserEvent({ $append: s });
652
+ }
653
+ /**
654
+ * Used to remove items from list if user property is list.
655
+ * Keys with $ and ss_ will be removed.
656
+ */
657
+ async remove(e, t) {
658
+ const r = this.formatParamsToObj(e, t);
659
+ if (!r)
660
+ return a({
661
+ status: n.ERROR,
662
+ errorType: o.VALIDATION_ERROR,
663
+ errorMessage: "data provided is empty"
664
+ });
665
+ const s = this.validateObjData(r);
666
+ return f(s) ? a({
667
+ status: n.ERROR,
668
+ errorType: o.VALIDATION_ERROR,
669
+ errorMessage: "data provided is empty"
670
+ }) : this.triggerUserEvent({ $remove: s });
671
+ }
672
+ /**
673
+ * Used to remove user property. Keys with $ and ss_ will be removed.
674
+ */
675
+ async unset(e) {
676
+ const t = this.formatParamsToArray(e);
677
+ if (!t)
678
+ return a({
679
+ status: n.ERROR,
680
+ errorType: o.VALIDATION_ERROR,
681
+ errorMessage: "data provided is empty"
682
+ });
683
+ const r = this.validateArrayData(t);
684
+ return $(r) ? a({
685
+ status: n.ERROR,
686
+ errorType: o.VALIDATION_ERROR,
687
+ errorMessage: "data provided is empty"
688
+ }) : this.triggerUserEvent({ $unset: r });
689
+ }
690
+ // this append is only used internally since it allows internal events
691
+ appendInternal(e, t) {
692
+ const r = this.formatParamsToObj(e, t);
693
+ if (!r)
694
+ return a({
695
+ status: n.ERROR,
696
+ errorType: o.VALIDATION_ERROR,
697
+ errorMessage: "data provided is empty"
698
+ });
699
+ const s = this.validateObjData(r, {
700
+ allowReservedKeys: !0
701
+ });
702
+ return f(s) ? a({
703
+ status: n.ERROR,
704
+ errorType: o.VALIDATION_ERROR,
705
+ errorMessage: "data provided is empty"
706
+ }) : this.triggerUserEvent({ $append: s });
707
+ }
708
+ // this remove is only used internally since it allows internal events
709
+ removeInternal(e, t) {
710
+ const r = this.formatParamsToObj(e, t);
711
+ if (!r)
712
+ return a({
713
+ status: n.ERROR,
714
+ errorType: o.VALIDATION_ERROR,
715
+ errorMessage: "data provided is empty"
716
+ });
717
+ const s = this.validateObjData(r, {
718
+ allowReservedKeys: !0
719
+ });
720
+ return f(s) ? a({
721
+ status: n.ERROR,
722
+ errorType: o.VALIDATION_ERROR,
723
+ errorMessage: "data provided is empty"
724
+ }) : this.triggerUserEvent({ $remove: s });
725
+ }
726
+ setInternal(e, t) {
727
+ const r = this.formatParamsToObj(e, t);
728
+ if (!r)
729
+ return a({
730
+ status: n.ERROR,
731
+ errorType: o.VALIDATION_ERROR,
732
+ errorMessage: "data provided is empty"
733
+ });
734
+ const s = this.validateObjData(r, {
735
+ allowReservedKeys: !0
736
+ });
737
+ return f(s) ? a({
738
+ status: n.ERROR,
739
+ errorType: o.VALIDATION_ERROR,
740
+ errorMessage: "data provided is empty"
741
+ }) : this.triggerUserEvent({ $set: s });
742
+ }
743
+ validateEmail(e) {
744
+ return /\S+@\S+\.\S+/.test(e);
745
+ }
746
+ validateMobile(e) {
747
+ return /^\+[1-9]\d{1,14}$/.test(e);
748
+ }
749
+ async addEmail(e) {
750
+ return this.validateEmail(e) ? this.appendInternal({ $email: e }) : a({
751
+ status: n.ERROR,
752
+ errorType: o.VALIDATION_ERROR,
753
+ errorMessage: "provided email is invalid"
754
+ });
755
+ }
756
+ async removeEmail(e) {
757
+ return this.validateEmail(e) ? this.removeInternal({ $email: e }) : a({
758
+ status: n.ERROR,
759
+ errorType: o.VALIDATION_ERROR,
760
+ errorMessage: "provided email is invalid"
761
+ });
762
+ }
763
+ /**
764
+ * Mobile number must be as per {@link https://www.twilio.com/docs/glossary/what-e164 E.164 standard}.
765
+ */
766
+ async addSms(e) {
767
+ return this.validateMobile(e) ? this.appendInternal({ $sms: e }) : a({
768
+ status: n.ERROR,
769
+ errorType: o.VALIDATION_ERROR,
770
+ errorMessage: "provided mobile number is invalid, must be as per E.164 standard"
771
+ });
772
+ }
773
+ /**
774
+ * Mobile number must be as per {@link https://www.twilio.com/docs/glossary/what-e164 E.164 standard}.
775
+ */
776
+ async removeSms(e) {
777
+ return this.validateMobile(e) ? this.removeInternal({ $sms: e }) : a({
778
+ status: n.ERROR,
779
+ errorType: o.VALIDATION_ERROR,
780
+ errorMessage: "provided mobile number is invalid, must be as per E.164 standard"
781
+ });
782
+ }
783
+ /**
784
+ * Mobile number must be as per {@link https://www.twilio.com/docs/glossary/what-e164 E.164 standard}.
785
+ */
786
+ async addWhatsapp(e) {
787
+ return this.validateMobile(e) ? this.appendInternal({ $whatsapp: e }) : a({
788
+ status: n.ERROR,
789
+ errorType: o.VALIDATION_ERROR,
790
+ errorMessage: "provided mobile number is invalid, must be as per E.164 standard"
791
+ });
792
+ }
793
+ /**
794
+ * Mobile number must be as per {@link https://www.twilio.com/docs/glossary/what-e164 E.164 standard}.
795
+ */
796
+ async removeWhatsapp(e) {
797
+ return this.validateMobile(e) ? this.removeInternal({ $whatsapp: e }) : a({
798
+ status: n.ERROR,
799
+ errorType: o.VALIDATION_ERROR,
800
+ errorMessage: "provided mobile number is invalid, must be as per E.164 standard"
801
+ });
802
+ }
803
+ getDeviceId() {
804
+ let e = V(U);
805
+ return e || (e = b(), M(U, e)), e;
806
+ }
807
+ async addWebPush(e) {
808
+ if (typeof e != "object")
809
+ return a({
810
+ status: n.ERROR,
811
+ errorType: o.VALIDATION_ERROR,
812
+ errorMessage: "provided push subscription is invalid, must be an object"
813
+ });
814
+ const t = this.getDeviceId();
815
+ return this.appendInternal({
816
+ $webpush: e,
817
+ $id_provider: "vapid",
818
+ $device_id: t
819
+ });
820
+ }
821
+ async removeWebPush(e) {
822
+ if (typeof e != "object")
823
+ return a({
824
+ status: n.ERROR,
825
+ errorType: o.VALIDATION_ERROR,
826
+ errorMessage: "provided push subscription is invalid, must be an object"
827
+ });
828
+ const t = this.getDeviceId();
829
+ return this.removeInternal({
830
+ $webpush: e,
831
+ $id_provider: "vapid",
832
+ $device_id: t
833
+ });
834
+ }
835
+ async addSlack(e) {
836
+ return typeof e != "object" ? a({
837
+ status: n.ERROR,
838
+ errorType: o.VALIDATION_ERROR,
839
+ errorMessage: "provided slack data is invalid, must be an object"
840
+ }) : this.appendInternal({ $slack: e });
841
+ }
842
+ async removeSlack(e) {
843
+ return typeof e != "object" ? a({
844
+ status: n.ERROR,
845
+ errorType: o.VALIDATION_ERROR,
846
+ errorMessage: "provided slack data is invalid, must be an object"
847
+ }) : this.removeInternal({ $slack: e });
848
+ }
849
+ async addMSTeams(e) {
850
+ return typeof e != "object" ? a({
851
+ status: n.ERROR,
852
+ errorType: o.VALIDATION_ERROR,
853
+ errorMessage: "provided ms_teams data is invalid, must be object"
854
+ }) : this.appendInternal({ $ms_teams: e });
855
+ }
856
+ async removeMSTeams(e) {
857
+ return typeof e != "object" ? a({
858
+ status: n.ERROR,
859
+ errorType: o.VALIDATION_ERROR,
860
+ errorMessage: "provided ms_teams data is invalid, must be object"
861
+ }) : this.removeInternal({ $ms_teams: e });
862
+ }
863
+ /**
864
+ * language passed should be 2-letter language code in {@link https://gist.github.com/jrnk/8eb57b065ea0b098d571 ISO 639-1 Alpha-2 format}.
865
+ * e.g. en (for English), es (for Spanish), fr (for French) etc.
866
+ */
867
+ async setPreferredLanguage(e) {
868
+ return typeof e != "string" ? a({
869
+ status: n.ERROR,
870
+ errorType: o.VALIDATION_ERROR,
871
+ errorMessage: "provided language is invalid, must be string"
872
+ }) : this.setInternal({ $preferred_language: e });
873
+ }
874
+ /**
875
+ * Timezone passed should be in {@link https://timeapi.io/documentation/iana-timezones IANA timezone format}.
876
+ */
877
+ async setTimezone(e) {
878
+ return typeof e != "string" ? a({
879
+ status: n.ERROR,
880
+ errorType: o.VALIDATION_ERROR,
881
+ errorMessage: "provided timezone is invalid, must be string"
882
+ }) : this.setInternal({ $timezone: e });
883
+ }
884
+ }
885
+ class J {
886
+ constructor(e) {
887
+ d(this, "config");
888
+ this.config = e;
889
+ }
890
+ async getPushSubscription() {
891
+ if (!g()) return;
892
+ const e = await navigator.serviceWorker.getRegistration();
893
+ if (!e) return;
894
+ const t = e.pushManager.getSubscription();
895
+ if (t)
896
+ return t;
897
+ }
898
+ async handleRegisterPush() {
899
+ try {
900
+ if (await navigator.serviceWorker.register(`/${this.config.swFileName}`), await Notification.requestPermission() !== "granted")
901
+ return console.warn("[SuprSend]: Notification permission isnt granted"), a({
902
+ status: n.ERROR,
903
+ errorType: o.PERMISSION_DENIED,
904
+ errorMessage: "Notification permission isn't granted"
905
+ });
906
+ const t = await navigator.serviceWorker.ready, r = await t.pushManager.getSubscription();
907
+ if (r)
908
+ return this.config.user.addWebPush(r);
909
+ if (!this.config.vapidKey)
910
+ return console.warn(
911
+ "[SuprSend]: Vapid key is missing. Add it while creating SuprSend instance"
912
+ ), a({
913
+ status: n.ERROR,
914
+ errorType: o.VALIDATION_ERROR,
915
+ errorMessage: "Vapid key is missing. Add it while creating SuprSend instance"
916
+ });
917
+ const s = await t.pushManager.subscribe({
918
+ userVisibleOnly: !0,
919
+ applicationServerKey: j(this.config.vapidKey)
920
+ });
921
+ return this.config.user.addWebPush(s);
922
+ } catch (e) {
923
+ return console.warn("SuprSend: Error getting push subscription", e), a({
924
+ status: n.ERROR,
925
+ errorType: o.UNKNOWN_ERROR,
926
+ errorMessage: (e == null ? void 0 : e.message) || "Unknown error occured while registering for push"
927
+ });
928
+ }
929
+ }
930
+ /**
931
+ * Used to register push service. This method will
932
+ * 1. Ask for notification permission.
933
+ * 2. Register push service and generate webpush token.
934
+ * 3. Send webpush token to SuprSend.
935
+ */
936
+ async registerPush() {
937
+ return g() && "serviceWorker" in navigator && "PushManager" in window ? this.handleRegisterPush() : (console.warn("[SuprSend]: Webpush isn't supported"), a({
938
+ status: n.ERROR,
939
+ errorType: o.UNSUPPORTED_ACTION,
940
+ errorMessage: "Webpush isn't supported"
941
+ }));
942
+ }
943
+ async updatePushSubscription() {
944
+ const e = await this.getPushSubscription();
945
+ if (e)
946
+ return this.config.user.addWebPush(e);
947
+ }
948
+ async removePushSubscription() {
949
+ const e = await this.getPushSubscription();
950
+ if (e)
951
+ return this.config.user.removeWebPush(e);
952
+ }
953
+ /**
954
+ * Used to get browser level permission to show notifications.
955
+ */
956
+ notificationPermission() {
957
+ return Notification.permission;
958
+ }
959
+ /**
960
+ * Used to check if push service is already active in browser.
961
+ */
962
+ async pushSubscribed() {
963
+ return !!await this.getPushSubscription();
964
+ }
965
+ }
966
+ function z(i) {
967
+ return { all: i = i || /* @__PURE__ */ new Map(), on: function(e, t) {
968
+ var r = i.get(e);
969
+ r ? r.push(t) : i.set(e, [t]);
970
+ }, off: function(e, t) {
971
+ var r = i.get(e);
972
+ r && (t ? r.splice(r.indexOf(t) >>> 0, 1) : i.set(e, []));
973
+ }, emit: function(e, t) {
974
+ var r = i.get(e);
975
+ r && r.slice().map(function(s) {
976
+ s(t);
977
+ }), (r = i.get("*")) && r.slice().map(function(s) {
978
+ s(e, t);
979
+ });
980
+ } };
981
+ }
982
+ const B = "https://hub.suprsend.com", G = "serviceworker.js", w = "ss_distinct_id";
983
+ class Z {
984
+ constructor(e, t) {
985
+ d(this, "host");
986
+ d(this, "publicApiKey");
987
+ d(this, "distinctId");
988
+ d(this, "userToken");
989
+ d(this, "vapidKey");
990
+ d(this, "swFileName");
991
+ d(this, "apiClient", null);
992
+ d(this, "userTokenExpirationTimer", null);
993
+ d(this, "authenticateOptions");
994
+ d(this, "user", new Q(this));
995
+ d(this, "webpush", new J(this));
996
+ d(this, "emitter", z());
997
+ if (!e)
998
+ throw new Error("[SuprSend]: publicApiKey is missing");
999
+ this.publicApiKey = e, this.host = (t == null ? void 0 : t.host) || B, this.vapidKey = (t == null ? void 0 : t.vapidKey) || "", this.swFileName = (t == null ? void 0 : t.swFileName) || G;
1000
+ }
1001
+ handleRefreshUserToken(e) {
1002
+ if (!this.userToken || !g()) return;
1003
+ const t = L(this.userToken), r = (t.exp || 0) * 1e3, s = Date.now(), u = 1e3 * 30;
1004
+ if (r && r > s) {
1005
+ const c = r - s - u;
1006
+ this.userTokenExpirationTimer && clearTimeout(this.userTokenExpirationTimer), this.userTokenExpirationTimer = setTimeout(async () => {
1007
+ let h = "";
1008
+ try {
1009
+ h = await e(
1010
+ this.userToken,
1011
+ t
1012
+ );
1013
+ } catch {
1014
+ try {
1015
+ h = await e(
1016
+ this.userToken,
1017
+ t
1018
+ );
1019
+ } catch (R) {
1020
+ console.warn("[SuprSend]: Couldn't fetch new userToken", R);
1021
+ }
1022
+ }
1023
+ h && typeof h == "string" && this.identify(this.distinctId, h, this.authenticateOptions);
1024
+ }, c);
1025
+ }
1026
+ }
1027
+ client() {
1028
+ return this.distinctId || console.warn(
1029
+ "[SuprSend]: distinctId is missing. User should be authenticated"
1030
+ ), this.apiClient || (this.apiClient = new S(this)), this.apiClient;
1031
+ }
1032
+ eventApi(e) {
1033
+ return this.client().request({ path: "v2/event", payload: e, type: "post" });
1034
+ }
1035
+ /**
1036
+ * Used to authenticate user. Usually called just after successful login and on reload of loggedin route to re-authenticate loggedin user.
1037
+ * In production env's userToken is mandatory for security purposes.
1038
+ */
1039
+ async identify(e, t, r) {
1040
+ if (!e)
1041
+ return a({
1042
+ status: n.ERROR,
1043
+ errorType: o.VALIDATION_ERROR,
1044
+ errorMessage: "distinctId is missing"
1045
+ });
1046
+ if (this.apiClient && this.distinctId && this.distinctId !== e)
1047
+ return a({
1048
+ status: n.ERROR,
1049
+ errorType: o.VALIDATION_ERROR,
1050
+ errorMessage: "User already loggedin, reset current user to login new user"
1051
+ });
1052
+ if (this.apiClient && this.distinctId === e && this.userToken !== t)
1053
+ return this.userToken = t, this.apiClient = new S(this), r != null && r.refreshUserToken && this.handleRefreshUserToken(r.refreshUserToken), a({ status: n.SUCCESS });
1054
+ if (this.distinctId && this.apiClient)
1055
+ return a({ status: n.SUCCESS });
1056
+ this.distinctId = e, this.userToken = t, this.apiClient = new S(this), this.authenticateOptions = r;
1057
+ const s = V(
1058
+ w
1059
+ );
1060
+ if (r != null && r.refreshUserToken && this.handleRefreshUserToken(r.refreshUserToken), s == this.distinctId)
1061
+ return this.webpush.updatePushSubscription(), a({ status: n.SUCCESS });
1062
+ const u = await this.eventApi({
1063
+ event: "$identify",
1064
+ $insert_id: b(),
1065
+ $time: C(),
1066
+ properties: {
1067
+ $identified_id: e
1068
+ }
1069
+ });
1070
+ return u.status === n.SUCCESS ? (this.webpush.updatePushSubscription(), M(w, this.distinctId)) : this.reset({ unsubscribePush: !1 }), u;
1071
+ }
1072
+ /**
1073
+ * Check's if SuprSend instance is authenticated. To check if userToken is also present pass true.
1074
+ */
1075
+ isIdentified(e) {
1076
+ return e ? !!(this.userToken && this.distinctId) : !!this.distinctId;
1077
+ }
1078
+ /**
1079
+ * Used to trigger events to suprsend.
1080
+ */
1081
+ async track(e, t) {
1082
+ let r = {};
1083
+ return e ? (typeof t == "object" && (r = { ...r, ...t }), this.eventApi({
1084
+ event: String(e),
1085
+ $insert_id: b(),
1086
+ $time: C(),
1087
+ distinct_id: this.distinctId,
1088
+ properties: r
1089
+ })) : a({
1090
+ status: n.ERROR,
1091
+ errorType: o.VALIDATION_ERROR,
1092
+ errorMessage: "event name is missing"
1093
+ });
1094
+ }
1095
+ /**
1096
+ * Clears user related data attached to SuprSend instance. Usually called during logout.
1097
+ */
1098
+ async reset(e) {
1099
+ var r;
1100
+ return (e == null ? void 0 : e.unsubscribePush) !== !1 && await ((r = this.webpush) == null ? void 0 : r.removePushSubscription()), this.apiClient = null, this.distinctId = null, this.userToken = "", W(w), this.userTokenExpirationTimer && clearTimeout(this.userTokenExpirationTimer), a({ status: n.SUCCESS });
1101
+ }
1102
+ }
1103
+ export {
1104
+ v as ChannelLevelPreferenceOptions,
1105
+ o as ERROR_TYPE,
1106
+ p as PreferenceOptions,
1107
+ n as RESPONSE_STATUS,
1108
+ Z as SuprSend,
1109
+ Z as default
1110
+ };
1111
+ //# sourceMappingURL=index.js.map