@suprsend/web-sdk 0.1.17 → 0.1.21

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://hub.suprsend.com",
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://hub.suprsend.com",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,11 @@
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
+ flush_interval: 3000,
8
+ service_worker_file: "serviceworker.js",
9
9
  sw_delay: 5000, //in ms,
10
10
  };
11
11
 
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.bulk_call_api();
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() {
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
@@ -174,7 +174,7 @@ function bulk_call_api() {
174
174
  /*
175
175
  schedule the flush in some time future
176
176
  */
177
- function schedule_flush(delay = 5000) {
177
+ function schedule_flush(delay = config.flush_interval) {
178
178
  setTimeout(() => {
179
179
  bulk_call_api();
180
180
  }, delay);
@@ -200,18 +200,23 @@ function batch_or_call(body) {
200
200
  }
201
201
  }
202
202
 
203
- function format_props(key, value) {
203
+ function format_props({ key, value, allow_special_tags = false }) {
204
204
  var formatted_data;
205
205
  if (key instanceof Object) {
206
206
  formatted_data = {};
207
- let keys_list = Object.keys(key);
208
- for (let i = 0; i < keys_list.length; i++) {
209
- const value = keys_list[i];
210
- if (key[value] !== undefined) {
211
- 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];
212
213
  }
213
214
  }
214
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
+ }
215
220
  formatted_data = { [String(key)]: value };
216
221
  }
217
222
  return formatted_data;
@@ -230,6 +235,21 @@ function urlB64ToUint8Array(base64String) {
230
235
  return outputArray;
231
236
  }
232
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
+
233
253
  export default {
234
254
  uuid,
235
255
  epoch_milliseconds,
@@ -249,4 +269,7 @@ export default {
249
269
  format_props,
250
270
  urlB64ToUint8Array,
251
271
  batch_or_call,
272
+ has_special_char,
273
+ is_empty,
274
+ bulk_call_api,
252
275
  };
package/. npmignore DELETED
@@ -1 +0,0 @@
1
- /node_modules
@@ -1,3 +0,0 @@
1
- {
2
- "editor.formatOnSave": true
3
- }