@suprsend/web-sdk 0.1.16 → 0.1.20

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,119 @@
1
+ var suprsend_config = {
2
+ api_url: "https://collector-staging.suprsend.workers.dev",
3
+ imgkit_root: "https://ik.imagekit.io/l0quatz6utm/",
4
+ api_events_route: "event/",
5
+ };
6
+
7
+ var valid_notification_params = [
8
+ "title",
9
+ "body",
10
+ "icon",
11
+ "image",
12
+ "badge",
13
+ "vibrate",
14
+ "sound",
15
+ "dir",
16
+ "tag",
17
+ "data",
18
+ "requireInteraction",
19
+ "renotify",
20
+ "silent",
21
+ "timestamp",
22
+ "actions",
23
+ ];
24
+
25
+ const url_fields = ["image", "icon", "badge"];
26
+
27
+ function safe_get(cb, default_value) {
28
+ let resp;
29
+ try {
30
+ resp = cb();
31
+ } catch (err) {
32
+ resp = default_value;
33
+ }
34
+ return resp;
35
+ }
36
+
37
+ function validate_notification(notification_obj) {
38
+ let validated_notification_obj = {};
39
+ for (var item in notification_obj) {
40
+ if (valid_notification_params.includes(item)) {
41
+ if (url_fields.includes(item)) {
42
+ validated_notification_obj[
43
+ item
44
+ ] = `${suprsend_config.imgkit_root}${notification_obj[item]}`;
45
+ } else {
46
+ validated_notification_obj[item] = notification_obj[item];
47
+ }
48
+ }
49
+ }
50
+ if (!(validated_notification_obj["actions"] instanceof Array)) {
51
+ delete validated_notification_obj["actions"];
52
+ }
53
+ return validated_notification_obj;
54
+ }
55
+
56
+ function track_data(body, method = "post") {
57
+ return fetch(
58
+ `${suprsend_config.api_url}/${suprsend_config.api_events_route}`,
59
+ {
60
+ method: method,
61
+ body: JSON.stringify(body),
62
+ headers: { "Content-Type": "application/json" },
63
+ }
64
+ );
65
+ }
66
+
67
+ self.addEventListener("push", function (e) {
68
+ var notification = e.data.json();
69
+ const validated_notification = validate_notification(notification);
70
+ console.log("Received notification", validated_notification);
71
+ track_data({
72
+ event: "$notification_delivered",
73
+ properties: {
74
+ id: safe_get(() => validated_notification.data.notification_id),
75
+ },
76
+ });
77
+ e.waitUntil(
78
+ self.registration.showNotification(
79
+ validated_notification.title || "",
80
+ validated_notification
81
+ )
82
+ );
83
+ });
84
+
85
+ self.addEventListener("notificationclose", function (e) {
86
+ var notification = e.notification;
87
+ console.log("Closed notification", notification);
88
+ track_data({
89
+ event: "$notification_dismiss",
90
+ properties: {
91
+ id: safe_get(() => notification.data.notification_id),
92
+ },
93
+ });
94
+ });
95
+
96
+ self.addEventListener("notificationclick", function (e) {
97
+ e.notification.close();
98
+ var notification = e.notification;
99
+ console.log("Clicked notification", notification);
100
+ track_data({
101
+ event: "$notification_clicked",
102
+ properties: {
103
+ id: safe_get(() => notification.data.notification_id),
104
+ label_id: e.action,
105
+ },
106
+ });
107
+ var launch_url_obj = safe_get(() => notification.data.launch_urls);
108
+ var redirect_url = "/";
109
+ if (launch_url_obj) {
110
+ if (e.action && launch_url_obj[e.action]) {
111
+ redirect_url = launch_url_obj[e.action];
112
+ } else if (launch_url_obj["default"]) {
113
+ redirect_url = launch_url_obj["default"];
114
+ }
115
+ } else {
116
+ redirect_url = "/";
117
+ }
118
+ clients.openWindow(redirect_url);
119
+ });
@@ -0,0 +1 @@
1
+ var suprsend_config={api_url:"https://collector-staging.suprsend.workers.dev",imgkit_root:"https://ik.imagekit.io/l0quatz6utm/",api_events_route:"event/"},valid_notification_params=["title","body","icon","image","badge","vibrate","sound","dir","tag","data","requireInteraction","renotify","silent","timestamp","actions"];const url_fields=["image","icon","badge"];function safe_get(i,t){let n;try{n=i()}catch(i){n=t}return n}function validate_notification(i){let t={};for(var n in i)valid_notification_params.includes(n)&&(url_fields.includes(n)?t[n]=`${suprsend_config.imgkit_root}${i[n]}`:t[n]=i[n]);return t.actions instanceof Array||delete t.actions,t}function track_data(i,t="post"){return fetch(`${suprsend_config.api_url}/${suprsend_config.api_events_route}`,{method:t,body:JSON.stringify(i),headers:{"Content-Type":"application/json"}})}self.addEventListener("push",(function(i){const t=validate_notification(i.data.json());console.log("Received notification",t),track_data({event:"$notification_delivered",properties:{id:safe_get((()=>t.data.notification_id))}}),i.waitUntil(self.registration.showNotification(t.title||"",t))})),self.addEventListener("notificationclose",(function(i){var t=i.notification;console.log("Closed notification",t),track_data({event:"$notification_dismiss",properties:{id:safe_get((()=>t.data.notification_id))}})})),self.addEventListener("notificationclick",(function(i){i.notification.close();var t=i.notification;console.log("Clicked notification",t),track_data({event:"$notification_clicked",properties:{id:safe_get((()=>t.data.notification_id)),label_id:i.action}});var n=safe_get((()=>t.data.launch_urls)),e="/";n?i.action&&n[i.action]?e=n[i.action]:n.default&&(e=n.default):e="/",clients.openWindow(e)}));
package/src/config.js CHANGED
@@ -1,11 +1,10 @@
1
1
  const package_data = require("../package.json");
2
2
 
3
- // suprsend sdk related config
4
3
  const config = {
5
4
  api_url: "https://hub.suprsend.com",
6
5
  sdk_version: package_data.version,
7
6
  batch_size: 20,
8
- service_worker_file: "suprsend_service_worker.js",
7
+ service_worker_file: "serviceworker.js",
9
8
  sw_delay: 5000, //in ms,
10
9
  };
11
10
 
package/src/constants.js CHANGED
@@ -13,22 +13,22 @@ export const browser_useragent_map = {
13
13
  "Opera Mobile": ["Opera Mobi"],
14
14
  Opera: ["Opera"],
15
15
  "Internet Explorer": ["Trident", "MSIE"],
16
- Chrome: ["Chrome"],
16
+ Chrome: ["Chrome", "CriOS"],
17
17
  Firefox: ["Firefox"],
18
18
  Safari: ["Safari"],
19
19
  Mozilla: ["Mozilla"],
20
20
  };
21
21
 
22
22
  export const browser_version_useragent_map = {
23
- Edge: /Edge\/([0-9]+(\.[0-9]+)?)/,
24
- "Opera Mini": /Opera Mini\/([0-9]+(\.[0-9]+)?)/,
25
- "Opera Mobile": /Version\/([0-9]+(\.[0-9]+)?)/,
26
- Opera: /Version\/([0-9]+(\.[0-9]+)?)/,
27
- "Internet Explorer": "rv:",
28
- Chrome: /Chrome\/([0-9]+(\.[0-9]+)?)/,
29
- Firefox: /rv:([0-9]+(\.[0-9]+)?)/,
30
- Safari: /Version\/([0-9]+(\.[0-9]+)?)/,
31
- Mozilla: /rv:([0-9]+(\.[0-9]+)?)/,
23
+ Edge: [/Edge\/([0-9]+(\.[0-9]+)?)/],
24
+ "Opera Mini": [/Opera Mini\/([0-9]+(\.[0-9]+)?)/],
25
+ "Opera Mobile": [/Version\/([0-9]+(\.[0-9]+)?)/],
26
+ Opera: [/Version\/([0-9]+(\.[0-9]+)?)/],
27
+ "Internet Explorer": ["rv:"],
28
+ Chrome: [/Chrome\/([0-9]+(\.[0-9]+)?)/, /CriOS\/([0-9]+(\.[0-9]+)?)/],
29
+ Firefox: [/rv:([0-9]+(\.[0-9]+)?)/],
30
+ Safari: [/Version\/([0-9]+(\.[0-9]+)?)/],
31
+ Mozilla: [/rv:([0-9]+(\.[0-9]+)?)/],
32
32
  };
33
33
 
34
34
  export const os_useragent_map = {
package/src/errors.js ADDED
@@ -0,0 +1,20 @@
1
+ export class SuprsendError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "SuprsendError";
5
+ }
6
+ }
7
+
8
+ export class SSConfigurationError extends SuprsendError {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "SuprsendConfigurationError";
12
+ }
13
+ }
14
+
15
+ export class SSValidationError extends SuprsendError {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "SuprsendValidationError";
19
+ }
20
+ }
package/src/index.js CHANGED
@@ -3,26 +3,31 @@ import config from "./config";
3
3
  import User from "./user";
4
4
  import ServiceWorker from "./service_worker";
5
5
  import { constants } from "./constants";
6
+ import { SSConfigurationError } from "./errors";
6
7
 
7
8
  var suprSendInstance;
8
9
  export var init_at;
9
10
 
10
11
  class SuprSend {
11
- setCustomConfigProperty(key, value = "", mandatory = false) {
12
- if (value) {
13
- config[key] = value;
14
- } else {
15
- if (mandatory) {
16
- throw Error("Mandatory Key Missing:", key);
17
- }
12
+ init(ENV_API_KEY, SIGNING_KEY, config_keys = {}) {
13
+ config_keys.env = ENV_API_KEY;
14
+ config_keys.signing_key = SIGNING_KEY;
15
+ init_at = new Date();
16
+ var distinct_id = utils.get_cookie(constants.distinct_id);
17
+ if (!suprSendInstance) {
18
+ suprSendInstance = {};
19
+ this.setCustomConfig(config_keys);
18
20
  }
19
- }
20
-
21
- setCustomConfig(config_keys) {
22
- this.setCustomConfigProperty("env_key", config_keys.env, true);
23
- this.setCustomConfigProperty("signing_key", config_keys.signing_key, true);
24
- this.setCustomConfigProperty("api_url", config_keys?.api_url);
25
- this.setCustomConfigProperty("vapid_key", config_keys?.vapid_key);
21
+ if (!distinct_id) {
22
+ distinct_id = utils.uuid();
23
+ utils.set_cookie(constants.distinct_id, distinct_id);
24
+ }
25
+ suprSendInstance.distinct_id = distinct_id;
26
+ this.user = new User(suprSendInstance);
27
+ this.sw = new ServiceWorker(suprSendInstance);
28
+ this.sw.update_subscription();
29
+ SuprSend.setEnvProperties();
30
+ utils.schedule_flush();
26
31
  }
27
32
 
28
33
  static setEnvProperties() {
@@ -41,25 +46,25 @@ class SuprSend {
41
46
  };
42
47
  }
43
48
 
44
- init(ENV_API_KEY, SIGNING_KEY, config_keys = {}) {
45
- config_keys.env = ENV_API_KEY;
46
- config_keys.signing_key = SIGNING_KEY;
47
- init_at = new Date();
48
- var distinct_id = utils.get_cookie(constants.distinct_id);
49
- if (!suprSendInstance) {
50
- suprSendInstance = {};
51
- this.setCustomConfig(config_keys);
52
- }
53
- if (!distinct_id) {
54
- distinct_id = utils.uuid();
55
- utils.set_cookie(constants.distinct_id, distinct_id);
49
+ setCustomConfigProperty(key, value = "", mandatory = false) {
50
+ if (value) {
51
+ config[key] = value;
52
+ } else {
53
+ if (mandatory) {
54
+ throw new SSConfigurationError(`Mandatory Key Missing: ${key}`);
55
+ }
56
56
  }
57
- suprSendInstance.distinct_id = distinct_id;
58
- this.user = new User(suprSendInstance);
59
- this.sw = new ServiceWorker(suprSendInstance);
60
- this.sw.update_subscription();
61
- SuprSend.setEnvProperties();
62
- utils.schedule_flush();
57
+ }
58
+
59
+ setCustomConfig(config_keys) {
60
+ this.setCustomConfigProperty("env_key", config_keys.env, true);
61
+ this.setCustomConfigProperty("signing_key", config_keys.signing_key, true);
62
+ this.setCustomConfigProperty("api_url", config_keys?.api_url);
63
+ this.setCustomConfigProperty("vapid_key", config_keys?.vapid_key);
64
+ this.setCustomConfigProperty(
65
+ "service_worker_file",
66
+ config_keys?.sw_file_name
67
+ );
63
68
  }
64
69
 
65
70
  set_super_properties(props = {}) {
@@ -67,13 +72,19 @@ class SuprSend {
67
72
  constants.super_properties_key
68
73
  );
69
74
  let new_super_props = { ...existing_super_properties, ...props };
75
+ let formatted_super_props = {};
76
+ for (let key in new_super_props) {
77
+ if (!utils.has_special_char(key)) {
78
+ formatted_super_props[key] = new_super_props[key];
79
+ }
80
+ }
70
81
  utils.set_local_storage_item(
71
82
  constants.super_properties_key,
72
- JSON.stringify(new_super_props)
83
+ JSON.stringify(formatted_super_props)
73
84
  );
74
85
  suprSendInstance.env_properties = {
75
86
  ...suprSendInstance.env_properties,
76
- ...new_super_props,
87
+ ...formatted_super_props,
77
88
  };
78
89
  }
79
90
 
@@ -94,25 +105,27 @@ class SuprSend {
94
105
  }
95
106
 
96
107
  track(event, props = {}) {
97
- if (event != undefined) {
98
- const super_props = utils.get_parsed_local_store_data(
99
- constants.super_properties_key
100
- );
101
- const formatted_data = utils.format_props({
102
- ...props,
103
- ...suprSendInstance.env_properties,
104
- ...super_props,
105
- $current_url: window.location.href,
106
- });
107
- utils.batch_or_call({
108
- event: String(event),
109
- distinct_id: suprSendInstance.distinct_id,
110
- env: config.env_key,
111
- properties: formatted_data,
112
- $insert_id: utils.uuid(),
113
- $time: utils.epoch_milliseconds(),
114
- });
108
+ if (event === undefined) {
109
+ return;
115
110
  }
111
+ const super_props = utils.get_parsed_local_store_data(
112
+ constants.super_properties_key
113
+ );
114
+ const formatted_data = utils.format_props({ key: props });
115
+ const final_data = {
116
+ ...formatted_data,
117
+ ...suprSendInstance.env_properties,
118
+ ...super_props,
119
+ $current_url: window.location.href,
120
+ };
121
+ utils.batch_or_call({
122
+ event: String(event),
123
+ distinct_id: suprSendInstance.distinct_id,
124
+ env: config.env_key,
125
+ properties: final_data,
126
+ $insert_id: utils.uuid(),
127
+ $time: utils.epoch_milliseconds(),
128
+ });
116
129
  }
117
130
 
118
131
  reset() {
@@ -69,7 +69,6 @@ class ServiceWorker {
69
69
  console.log("Provide vapid key while calling init");
70
70
  return;
71
71
  }
72
- console.log("vapid is", config.vapid_key);
73
72
  const applicationServerKey = utils.urlB64ToUint8Array(config.vapid_key);
74
73
  const subscription = await reg.pushManager.subscribe({
75
74
  applicationServerKey,
package/src/user.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import utils from "./utils";
2
2
  import config from "./config";
3
+ import { parsePhoneNumber } from "libphonenumber-js";
3
4
 
4
5
  class User {
5
6
  constructor(instance) {
6
7
  this.instance = instance;
7
8
  }
8
9
 
9
- _call_indetity(properties) {
10
+ _call_indentity(properties) {
10
11
  utils.batch_or_call({
11
12
  env: config.env_key,
12
13
  distinct_id: this.instance.distinct_id,
@@ -14,59 +15,99 @@ class User {
14
15
  });
15
16
  }
16
17
 
18
+ _allow_special_char_events = (key) => {
19
+ return (
20
+ key === "$email" ||
21
+ key === "$whatsapp" ||
22
+ key === "$sms" ||
23
+ key?.["$webpush"]
24
+ );
25
+ };
26
+
27
+ _convert_to_number(obj) {
28
+ if (!utils.is_empty(obj)) {
29
+ for (let key in obj) {
30
+ const number = Number(obj[key]);
31
+ if (!number) {
32
+ delete obj[key];
33
+ } else {
34
+ obj[key] = number;
35
+ }
36
+ }
37
+ }
38
+ return obj;
39
+ }
40
+
41
+ _validate_mobile_and_send(key, mobile) {
42
+ try {
43
+ const mobile_number = parsePhoneNumber(mobile);
44
+ if (mobile_number.isValid()) {
45
+ this.append(key, mobile);
46
+ } else {
47
+ console.log("Suprsend: Provide valid Mobile number");
48
+ }
49
+ } catch (err) {
50
+ console.log("Suprsend: Provide valid Mobile number");
51
+ }
52
+ }
53
+
17
54
  set(key, value) {
18
- const body = utils.format_props(key, value);
19
- if (body) {
20
- this._call_indetity({ $set: body });
55
+ const data = utils.format_props({ key, value });
56
+ if (!utils.is_empty(data)) {
57
+ this._call_indentity({ $set: data });
21
58
  }
22
59
  }
23
60
 
24
61
  set_once(key, value) {
25
- const body = utils.format_props(key, value);
26
- if (body) {
27
- this._call_indetity({ $set_once: body });
62
+ const data = utils.format_props({ key, value });
63
+ if (!utils.is_empty(data)) {
64
+ this._call_indentity({ $set_once: data });
28
65
  }
29
66
  }
30
67
 
31
68
  increment(key, value = 1) {
32
- const body = utils.format_props(key, value);
33
- if (body) {
34
- let keys_list = Object.keys(body);
35
- for (let i = 0; i < keys_list.length; i++) {
36
- const key_value = keys_list[i];
37
- const is_number = Number(body[key_value]);
38
- if (!is_number) {
39
- delete body[key_value];
40
- }
41
- }
42
- this._call_indetity({ $add: body });
69
+ const data = utils.format_props({ key, value });
70
+ const formatted_data = this._convert_to_number(data);
71
+ if (!utils.is_empty(formatted_data)) {
72
+ this._call_indentity({ $add: formatted_data });
43
73
  }
44
74
  }
45
75
 
46
76
  append(key, value) {
47
- const body = utils.format_props(key, value);
48
- if (body) {
49
- this._call_indetity({ $append: body });
77
+ const allow_special_tags = this._allow_special_char_events(key);
78
+ const data = utils.format_props({ key, value, allow_special_tags });
79
+ if (!utils.is_empty(data)) {
80
+ this._call_indentity({ $append: data });
50
81
  }
51
82
  }
52
83
 
53
84
  remove(key, value) {
54
- const body = utils.format_props(key, value);
55
- if (body) {
56
- this._call_indetity({ $remove: body });
85
+ const allow_special_tags = this._allow_special_char_events(key);
86
+ const data = utils.format_props({ key, value, allow_special_tags });
87
+ if (!utils.is_empty(data)) {
88
+ this._call_indentity({ $remove: data });
57
89
  }
58
90
  }
59
91
 
60
92
  unset(key) {
61
93
  let formatted_data;
62
94
  if (typeof key === "string") {
63
- formatted_data = [String(key)];
64
- } else if (key instanceof Array) {
65
- formatted_data = key.map((item) => String(item));
66
- } else {
67
- return;
95
+ if (!utils.has_special_char(key)) {
96
+ formatted_data = [key];
97
+ } else {
98
+ console.log("Suprsend: key cannot start with $ or ss_");
99
+ }
100
+ } else if (Array.isArray(key)) {
101
+ formatted_data = [];
102
+ key.forEach((item) => {
103
+ if (!utils.has_special_char(String(item))) {
104
+ formatted_data.push(String(item));
105
+ }
106
+ });
107
+ }
108
+ if (!utils.is_empty(formatted_data)) {
109
+ this._call_indentity({ $unset: formatted_data });
68
110
  }
69
- this._call_indetity({ $unset: formatted_data });
70
111
  }
71
112
 
72
113
  add_email(email = "") {
@@ -78,7 +119,7 @@ class User {
78
119
  }
79
120
 
80
121
  add_sms(mobile = "") {
81
- this.append("$sms", mobile);
122
+ this._validate_mobile_and_send("$sms", mobile);
82
123
  }
83
124
 
84
125
  remove_sms(mobile = "") {
@@ -86,10 +127,10 @@ class User {
86
127
  }
87
128
 
88
129
  add_whatsapp(mobile = "") {
89
- this.append("$whatsapp", mobile);
130
+ this._validate_mobile_and_send("$whatsapp", mobile);
90
131
  }
91
132
 
92
- remove_sms(mobile = "") {
133
+ remove_whatsapp(mobile = "") {
93
134
  this.remove("$whatsapp", mobile);
94
135
  }
95
136
 
package/src/utils.js CHANGED
@@ -96,18 +96,22 @@ function browser() {
96
96
  }
97
97
  }
98
98
  }
99
+ return "";
99
100
  }
100
101
 
101
102
  function browser_version() {
102
103
  const userAgent = navigator.userAgent;
103
104
  const browser_name = browser();
104
- const regex = browser_version_useragent_map[browser_name];
105
- if (regex) {
106
- const result = userAgent.match(regex);
107
- if (result && result.length > 1) {
108
- return result[1];
105
+ for (let regex_item of browser_version_useragent_map[browser_name]) {
106
+ const regex = regex_item;
107
+ if (regex) {
108
+ const result = userAgent.match(regex);
109
+ if (result && result.length > 1) {
110
+ return result[1];
111
+ }
109
112
  }
110
113
  }
114
+ return "";
111
115
  }
112
116
 
113
117
  function os() {
@@ -117,6 +121,7 @@ function os() {
117
121
  return i;
118
122
  }
119
123
  }
124
+ return "";
120
125
  }
121
126
 
122
127
  async function api(route, body, method = "POST") {
@@ -195,18 +200,23 @@ function batch_or_call(body) {
195
200
  }
196
201
  }
197
202
 
198
- function format_props(key, value) {
203
+ function format_props({ key, value, allow_special_tags = false }) {
199
204
  var formatted_data;
200
205
  if (key instanceof Object) {
201
206
  formatted_data = {};
202
- let keys_list = Object.keys(key);
203
- for (let i = 0; i < keys_list.length; i++) {
204
- const value = keys_list[i];
205
- if (key[value] !== undefined) {
206
- formatted_data[String(value)] = key[value];
207
+ for (let item in key) {
208
+ if (key[item] !== undefined) {
209
+ if (!allow_special_tags && has_special_char(item)) {
210
+ continue;
211
+ }
212
+ formatted_data[String(item)] = key[item];
207
213
  }
208
214
  }
209
215
  } else if (value != undefined) {
216
+ if (!allow_special_tags && has_special_char(String(key))) {
217
+ console.log("Suprsend: key cannot start with $ or ss_");
218
+ return;
219
+ }
210
220
  formatted_data = { [String(key)]: value };
211
221
  }
212
222
  return formatted_data;
@@ -225,6 +235,21 @@ function urlB64ToUint8Array(base64String) {
225
235
  return outputArray;
226
236
  }
227
237
 
238
+ const has_special_char = (str) => {
239
+ return str.startsWith("$") || str?.toLowerCase()?.startsWith("ss_");
240
+ };
241
+
242
+ const is_empty = (value) => {
243
+ if (Array.isArray(value)) {
244
+ return value.length === 0;
245
+ } else if (value instanceof Object) {
246
+ return Object.keys(value).length === 0;
247
+ } else {
248
+ const empty_values = ["", null, undefined];
249
+ return empty_values.includes(value);
250
+ }
251
+ };
252
+
228
253
  export default {
229
254
  uuid,
230
255
  epoch_milliseconds,
@@ -244,4 +269,6 @@ export default {
244
269
  format_props,
245
270
  urlB64ToUint8Array,
246
271
  batch_or_call,
272
+ has_special_char,
273
+ is_empty,
247
274
  };
package/. npmignore DELETED
@@ -1 +0,0 @@
1
- /node_modules
@@ -1,3 +0,0 @@
1
- {
2
- "editor.formatOnSave": true
3
- }