@zoho_apptics/apptics-js-sdk 1.0.2 → 2.0.0-beta.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.
- package/CHANGELOG.md +193 -0
- package/LICENSE +13 -0
- package/README.md +846 -11
- package/dist/apptics.esm.min.js +3 -0
- package/dist/apptics.esm.min.js.map +1 -0
- package/dist/apptics.min.js +3 -0
- package/dist/apptics.min.js.map +1 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
- package/dist/cjs/auto-init.d.ts +8 -0
- package/dist/cjs/auto-init.js +32 -0
- package/dist/cjs/core/attempts.d.ts +28 -0
- package/dist/cjs/core/attempts.js +113 -0
- package/dist/cjs/core/batch.d.ts +32 -0
- package/dist/cjs/core/batch.js +116 -0
- package/dist/cjs/core/callsite.d.ts +34 -0
- package/dist/cjs/core/callsite.js +115 -0
- package/dist/cjs/core/consent.d.ts +27 -0
- package/dist/cjs/core/consent.js +132 -0
- package/dist/cjs/core/constants.d.ts +237 -0
- package/dist/cjs/core/constants.js +315 -0
- package/dist/cjs/core/db.d.ts +48 -0
- package/dist/cjs/core/db.js +467 -0
- package/dist/cjs/core/debug.d.ts +22 -0
- package/dist/cjs/core/debug.js +88 -0
- package/dist/cjs/core/env.d.ts +28 -0
- package/dist/cjs/core/env.js +267 -0
- package/dist/cjs/core/framework-id.d.ts +1 -0
- package/dist/cjs/core/framework-id.js +9 -0
- package/dist/cjs/core/guard.d.ts +11 -0
- package/dist/cjs/core/guard.js +86 -0
- package/dist/cjs/core/hooks.d.ts +39 -0
- package/dist/cjs/core/hooks.js +22 -0
- package/dist/cjs/core/locks.d.ts +9 -0
- package/dist/cjs/core/locks.js +22 -0
- package/dist/cjs/core/logger.d.ts +15 -0
- package/dist/cjs/core/logger.js +50 -0
- package/dist/cjs/core/state.d.ts +70 -0
- package/dist/cjs/core/state.js +78 -0
- package/dist/cjs/core/storage.d.ts +61 -0
- package/dist/cjs/core/storage.js +327 -0
- package/dist/cjs/core/stylesheet.d.ts +8 -0
- package/dist/cjs/core/stylesheet.js +29 -0
- package/dist/cjs/core/transport.d.ts +85 -0
- package/dist/cjs/core/transport.js +456 -0
- package/dist/cjs/core/utils.d.ts +26 -0
- package/dist/cjs/core/utils.js +175 -0
- package/dist/cjs/core/validate.d.ts +18 -0
- package/dist/cjs/core/validate.js +233 -0
- package/dist/cjs/device/appversion.d.ts +15 -0
- package/dist/cjs/device/appversion.js +170 -0
- package/dist/cjs/device/index.d.ts +34 -0
- package/dist/cjs/device/index.js +472 -0
- package/dist/cjs/engagement/api-rules.d.ts +49 -0
- package/dist/cjs/engagement/api-rules.js +206 -0
- package/dist/cjs/engagement/auto-api.d.ts +7 -0
- package/dist/cjs/engagement/auto-api.js +200 -0
- package/dist/cjs/engagement/auto-screen.d.ts +18 -0
- package/dist/cjs/engagement/auto-screen.js +305 -0
- package/dist/cjs/engagement/defined-events.d.ts +23 -0
- package/dist/cjs/engagement/defined-events.js +58 -0
- package/dist/cjs/engagement/send.d.ts +6 -0
- package/dist/cjs/engagement/send.js +104 -0
- package/dist/cjs/engagement/session.d.ts +51 -0
- package/dist/cjs/engagement/session.js +463 -0
- package/dist/cjs/engagement/track.d.ts +56 -0
- package/dist/cjs/engagement/track.js +375 -0
- package/dist/cjs/errors/send.d.ts +6 -0
- package/dist/cjs/errors/send.js +73 -0
- package/dist/cjs/errors/track.d.ts +4 -0
- package/dist/cjs/errors/track.js +167 -0
- package/dist/cjs/feedback/controls.d.ts +37 -0
- package/dist/cjs/feedback/controls.js +316 -0
- package/dist/cjs/feedback/index.d.ts +9 -0
- package/dist/cjs/feedback/index.js +56 -0
- package/dist/cjs/feedback/logs.d.ts +45 -0
- package/dist/cjs/feedback/logs.js +241 -0
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +28 -0
- package/dist/cjs/feedback/send.js +203 -0
- package/dist/cjs/feedback/styles.d.ts +10 -0
- package/dist/cjs/feedback/styles.js +8 -0
- package/dist/cjs/feedback/template.d.ts +2 -0
- package/dist/cjs/feedback/template.js +162 -0
- package/dist/cjs/feedback/ui.d.ts +63 -0
- package/dist/cjs/feedback/ui.js +709 -0
- package/dist/cjs/heatmap/send.d.ts +13 -0
- package/dist/cjs/heatmap/send.js +82 -0
- package/dist/cjs/heatmap/track.d.ts +4 -0
- package/dist/cjs/heatmap/track.js +448 -0
- package/dist/cjs/index.d.ts +22 -0
- package/dist/cjs/index.js +162 -0
- package/dist/cjs/lifecycle.d.ts +21 -0
- package/dist/cjs/lifecycle.js +734 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/remoteconfig/country.d.ts +4 -0
- package/dist/cjs/remoteconfig/country.js +73 -0
- package/dist/cjs/remoteconfig/custom.d.ts +10 -0
- package/dist/cjs/remoteconfig/custom.js +58 -0
- package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
- package/dist/cjs/remoteconfig/evaluate.js +184 -0
- package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
- package/dist/cjs/remoteconfig/fetch.js +200 -0
- package/dist/cjs/remoteconfig/index.d.ts +15 -0
- package/dist/cjs/remoteconfig/index.js +116 -0
- package/dist/cjs/remoteconfig/store.d.ts +18 -0
- package/dist/cjs/remoteconfig/store.js +98 -0
- package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/cjs/remoteconfig/tz-countries.js +13 -0
- package/dist/cjs/remotelog/gate.d.ts +2 -0
- package/dist/cjs/remotelog/gate.js +28 -0
- package/dist/cjs/remotelog/index.d.ts +8 -0
- package/dist/cjs/remotelog/logger.d.ts +22 -0
- package/dist/cjs/remotelog/logger.js +111 -0
- package/dist/cjs/remotelog/send.d.ts +13 -0
- package/dist/cjs/remotelog/send.js +153 -0
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.d.ts +8 -0
- package/dist/cjs/scheduler.js +78 -0
- package/dist/cjs/types.d.ts +385 -0
- package/dist/cjs/user/index.d.ts +33 -0
- package/dist/cjs/user/index.js +183 -0
- package/dist/esm/auto-init.d.ts +8 -0
- package/dist/esm/auto-init.js +29 -0
- package/dist/esm/core/attempts.d.ts +28 -0
- package/dist/esm/core/attempts.js +106 -0
- package/dist/esm/core/batch.d.ts +32 -0
- package/dist/esm/core/batch.js +109 -0
- package/dist/esm/core/callsite.d.ts +34 -0
- package/dist/esm/core/callsite.js +110 -0
- package/dist/esm/core/consent.d.ts +27 -0
- package/dist/esm/core/consent.js +122 -0
- package/dist/esm/core/constants.d.ts +237 -0
- package/dist/esm/core/constants.js +292 -0
- package/dist/esm/core/db.d.ts +48 -0
- package/dist/esm/core/db.js +453 -0
- package/dist/esm/core/debug.d.ts +22 -0
- package/dist/esm/core/debug.js +81 -0
- package/dist/esm/core/env.d.ts +28 -0
- package/dist/esm/core/env.js +251 -0
- package/dist/esm/core/framework-id.d.ts +1 -0
- package/dist/esm/core/framework-id.js +7 -0
- package/dist/esm/core/guard.d.ts +11 -0
- package/dist/esm/core/guard.js +83 -0
- package/dist/esm/core/hooks.d.ts +39 -0
- package/dist/esm/core/hooks.js +20 -0
- package/dist/esm/core/locks.d.ts +9 -0
- package/dist/esm/core/locks.js +20 -0
- package/dist/esm/core/logger.d.ts +15 -0
- package/dist/esm/core/logger.js +46 -0
- package/dist/esm/core/state.d.ts +70 -0
- package/dist/esm/core/state.js +73 -0
- package/dist/esm/core/storage.d.ts +61 -0
- package/dist/esm/core/storage.js +314 -0
- package/dist/esm/core/stylesheet.d.ts +8 -0
- package/dist/esm/core/stylesheet.js +27 -0
- package/dist/esm/core/transport.d.ts +85 -0
- package/dist/esm/core/transport.js +439 -0
- package/dist/esm/core/utils.d.ts +26 -0
- package/dist/esm/core/utils.js +161 -0
- package/dist/esm/core/validate.d.ts +18 -0
- package/dist/esm/core/validate.js +226 -0
- package/dist/esm/device/appversion.d.ts +15 -0
- package/dist/esm/device/appversion.js +164 -0
- package/dist/esm/device/index.d.ts +34 -0
- package/dist/esm/device/index.js +461 -0
- package/dist/esm/engagement/api-rules.d.ts +49 -0
- package/dist/esm/engagement/api-rules.js +199 -0
- package/dist/esm/engagement/auto-api.d.ts +7 -0
- package/dist/esm/engagement/auto-api.js +198 -0
- package/dist/esm/engagement/auto-screen.d.ts +18 -0
- package/dist/esm/engagement/auto-screen.js +301 -0
- package/dist/esm/engagement/defined-events.d.ts +23 -0
- package/dist/esm/engagement/defined-events.js +55 -0
- package/dist/esm/engagement/send.d.ts +6 -0
- package/dist/esm/engagement/send.js +101 -0
- package/dist/esm/engagement/session.d.ts +51 -0
- package/dist/esm/engagement/session.js +448 -0
- package/dist/esm/engagement/track.d.ts +56 -0
- package/dist/esm/engagement/track.js +365 -0
- package/dist/esm/errors/send.d.ts +6 -0
- package/dist/esm/errors/send.js +70 -0
- package/dist/esm/errors/track.d.ts +4 -0
- package/dist/esm/errors/track.js +163 -0
- package/dist/esm/feedback/controls.d.ts +37 -0
- package/dist/esm/feedback/controls.js +309 -0
- package/dist/esm/feedback/index.d.ts +9 -0
- package/dist/esm/feedback/index.js +51 -0
- package/dist/esm/feedback/logs.d.ts +45 -0
- package/dist/esm/feedback/logs.js +227 -0
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +28 -0
- package/dist/esm/feedback/send.js +198 -0
- package/dist/esm/feedback/styles.d.ts +10 -0
- package/dist/esm/feedback/styles.js +4 -0
- package/dist/esm/feedback/template.d.ts +2 -0
- package/dist/esm/feedback/template.js +159 -0
- package/dist/esm/feedback/ui.d.ts +63 -0
- package/dist/esm/feedback/ui.js +703 -0
- package/dist/esm/heatmap/send.d.ts +13 -0
- package/dist/esm/heatmap/send.js +79 -0
- package/dist/esm/heatmap/track.d.ts +4 -0
- package/dist/esm/heatmap/track.js +445 -0
- package/dist/esm/index.d.ts +22 -0
- package/dist/esm/index.js +150 -0
- package/dist/esm/lifecycle.d.ts +21 -0
- package/dist/esm/lifecycle.js +729 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/remoteconfig/country.d.ts +4 -0
- package/dist/esm/remoteconfig/country.js +70 -0
- package/dist/esm/remoteconfig/custom.d.ts +10 -0
- package/dist/esm/remoteconfig/custom.js +54 -0
- package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
- package/dist/esm/remoteconfig/evaluate.js +179 -0
- package/dist/esm/remoteconfig/fetch.d.ts +24 -0
- package/dist/esm/remoteconfig/fetch.js +196 -0
- package/dist/esm/remoteconfig/index.d.ts +15 -0
- package/dist/esm/remoteconfig/index.js +111 -0
- package/dist/esm/remoteconfig/store.d.ts +18 -0
- package/dist/esm/remoteconfig/store.js +91 -0
- package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/esm/remoteconfig/tz-countries.js +11 -0
- package/dist/esm/remotelog/gate.d.ts +2 -0
- package/dist/esm/remotelog/gate.js +26 -0
- package/dist/esm/remotelog/index.d.ts +8 -0
- package/dist/esm/remotelog/logger.d.ts +22 -0
- package/dist/esm/remotelog/logger.js +108 -0
- package/dist/esm/remotelog/send.d.ts +13 -0
- package/dist/esm/remotelog/send.js +149 -0
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.d.ts +8 -0
- package/dist/esm/scheduler.js +74 -0
- package/dist/esm/types.d.ts +385 -0
- package/dist/esm/user/index.d.ts +33 -0
- package/dist/esm/user/index.js +176 -0
- package/dist/feedback.esm.min.js +3 -0
- package/dist/feedback.esm.min.js.map +1 -0
- package/package.json +122 -7
- package/src/appticssdk.js +0 -1
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { Console } from './logger.js';
|
|
3
|
+
import { isEmpty, isEmptyObj } from './utils.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `ap_` / `AP_` begins the names of Apptics' own defined events and properties (DefinedEvents), so
|
|
7
|
+
* an app's event, group or property key may not, as on Android: one that does is not logged.
|
|
8
|
+
*/
|
|
9
|
+
const RESERVED_PREFIX = /^ap_/i;
|
|
10
|
+
function validateEventOrGroup(str) {
|
|
11
|
+
if (!str || str === '') { // no i18n
|
|
12
|
+
Console.warn('validateEventOrGroup: Invalid event or group name'); // no i18n
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
// a letter, then up to 99 letters, digits or _, and not the reserved ap_ prefix
|
|
16
|
+
const pattern = /^[a-zA-Z][a-zA-Z0-9_]{0,99}$/;
|
|
17
|
+
if (RESERVED_PREFIX.test(str)) {
|
|
18
|
+
Console.warn('validateEventOrGroup: "' + str + '" starts with ap_, which is reserved for defined events'); // no i18n
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
return pattern.test(str);
|
|
22
|
+
}
|
|
23
|
+
/** A property key: a letter, then up to 49 letters, digits or _, and not the reserved ap_ prefix. */
|
|
24
|
+
const PROPERTY_KEY = /^[a-zA-Z][a-zA-Z0-9_]{0,49}$/;
|
|
25
|
+
/** At most this many properties on one event, screen or error. */
|
|
26
|
+
const MAX_PROPERTIES = 24;
|
|
27
|
+
/** At most this many characters of properties in all, as the payload carries them. */
|
|
28
|
+
const MAX_PROPERTIES_LENGTH = 7666;
|
|
29
|
+
/** At most this many characters in one value. */
|
|
30
|
+
const MAX_VALUE_LENGTH = 250;
|
|
31
|
+
/**
|
|
32
|
+
* A value as the payload carries it, with its type. Numbers are Number and booleans Boolean, sent
|
|
33
|
+
* as 1 and 0 — the form the server has always received a Boolean in. Anything else (an object, an
|
|
34
|
+
* array, null) is turned into text, as on Android, rather than dropped.
|
|
35
|
+
*/
|
|
36
|
+
function typedValue(value) {
|
|
37
|
+
if (typeof value === 'number' && isFinite(value)) { // no i18n
|
|
38
|
+
return { value: value, type: 'Number' }; // no i18n
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === 'boolean') { // no i18n
|
|
41
|
+
return { value: value ? 1 : 0, type: 'Boolean' }; // no i18n
|
|
42
|
+
}
|
|
43
|
+
if (typeof value === 'string') { // no i18n
|
|
44
|
+
return { value: value, type: 'String' }; // no i18n
|
|
45
|
+
}
|
|
46
|
+
var text;
|
|
47
|
+
try {
|
|
48
|
+
text = value === undefined ? 'undefined' : JSON.stringify(value); // no i18n
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
text = String(value);
|
|
52
|
+
}
|
|
53
|
+
return { value: text === undefined ? String(value) : text, type: 'String' }; // no i18n
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Event, screen and error properties as the payload carries them: `{ data, datatype, isvalid }`.
|
|
57
|
+
*
|
|
58
|
+
* Each property is judged on its own, as on Android: a bad key (wrong characters, too long, the
|
|
59
|
+
* reserved ap_ prefix) or a value over 250 characters drops that property, and the rest are kept
|
|
60
|
+
* and the event is still logged. Past 24 properties, or 7,666 characters in all, the later ones are
|
|
61
|
+
* dropped. When nothing valid is left the result says why, in the shape the server has always taken
|
|
62
|
+
* for a refused set: `{ isvalid: false, message: { code, keys, valuekeys } }`.
|
|
63
|
+
*/
|
|
64
|
+
function validateJSON(jsonObj) {
|
|
65
|
+
if (isEmpty(jsonObj) || isEmptyObj(jsonObj)) {
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
var obj = jsonObj;
|
|
69
|
+
var data = {};
|
|
70
|
+
var datatype = {};
|
|
71
|
+
var codes = [];
|
|
72
|
+
var badKeys = [];
|
|
73
|
+
var longKeys = [];
|
|
74
|
+
function note(code) {
|
|
75
|
+
if (codes.indexOf(code) === -1) {
|
|
76
|
+
codes.push(code);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
Object.keys(obj).forEach(function (key) {
|
|
80
|
+
if (!PROPERTY_KEY.test(key) || RESERVED_PREFIX.test(key)) {
|
|
81
|
+
Console.warn('validateJSON: dropped the property \'' + key + '\': a key is a letter, then up to 49 letters, digits or _, and not ap_'); // no i18n
|
|
82
|
+
badKeys.push(key.substring(0, 50));
|
|
83
|
+
note(103);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
var typed = typedValue(obj[key]);
|
|
87
|
+
if (String(typed.value).length > MAX_VALUE_LENGTH) {
|
|
88
|
+
Console.warn('validateJSON: dropped the property \'' + key + '\': its value is longer than ' + MAX_VALUE_LENGTH + ' characters'); // no i18n
|
|
89
|
+
longKeys.push(key);
|
|
90
|
+
note(104);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (Object.keys(data).length >= MAX_PROPERTIES) {
|
|
94
|
+
Console.warn('validateJSON: dropped the property \'' + key + '\': at most ' + MAX_PROPERTIES + ' properties'); // no i18n
|
|
95
|
+
note(102);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
var withIt = Object.assign({}, data);
|
|
99
|
+
withIt[key] = typed.value;
|
|
100
|
+
if (JSON.stringify(withIt).length > MAX_PROPERTIES_LENGTH) {
|
|
101
|
+
Console.warn('validateJSON: dropped the property \'' + key + '\': properties are at most ' + MAX_PROPERTIES_LENGTH + ' characters in all'); // no i18n
|
|
102
|
+
note(101);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
data[key] = typed.value;
|
|
106
|
+
datatype[key] = typed.type;
|
|
107
|
+
});
|
|
108
|
+
if (Object.keys(data).length > 0) {
|
|
109
|
+
return { data: data, datatype: datatype, isvalid: true };
|
|
110
|
+
}
|
|
111
|
+
if (codes.length === 0) {
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
return { isvalid: false, message: { code: codes, keys: badKeys, valuekeys: longKeys } };
|
|
115
|
+
}
|
|
116
|
+
/** user/register accepts at most 10000 characters of body (max-len on its inputstream). */
|
|
117
|
+
const USER_PROPERTIES_MAX_LENGTH = 10000;
|
|
118
|
+
/** user/register accepts at most this many user properties. */
|
|
119
|
+
const USER_PROPERTIES_MAX_COUNT = 45;
|
|
120
|
+
function userValidateJSON(obj) {
|
|
121
|
+
const data = {};
|
|
122
|
+
const datatype = {};
|
|
123
|
+
const message = [];
|
|
124
|
+
const errorMap = { 101: [], 102: [], 103: [], 104: [] };
|
|
125
|
+
const keyRegex = /^[a-zA-Z][a-zA-Z0-9_]{0,49}$/;
|
|
126
|
+
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
|
127
|
+
let propCount = 0;
|
|
128
|
+
// Main fields and their validation
|
|
129
|
+
const stringFields = [
|
|
130
|
+
'first_name', 'last_name', 'company_name', 'contact_number', 'country', 'region', 'city', 'geo_location', 'gender', 'plan_type', 'timezone', 'language'
|
|
131
|
+
]; // no i18n
|
|
132
|
+
const emailField = 'email_address'; // no i18n
|
|
133
|
+
const numberFields = ['engagement_score', 'date_of_birth']; // no i18n
|
|
134
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
135
|
+
propCount++;
|
|
136
|
+
// Key validation
|
|
137
|
+
if (typeof key !== 'string' || key.length > 50 || !keyRegex.test(key)) { // no i18n
|
|
138
|
+
errorMap[103].push(key);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// String fields
|
|
142
|
+
if (stringFields.includes(key)) {
|
|
143
|
+
if (typeof value !== 'string' || value.length > 255) { // no i18n
|
|
144
|
+
errorMap[103].push(key);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
data[key] = value;
|
|
148
|
+
datatype[key] = 'String'; // no i18n
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
// Email field
|
|
152
|
+
if (key === emailField) {
|
|
153
|
+
if (typeof value !== 'string' || value.length > 255 || !emailRegex.test(value)) { // no i18n
|
|
154
|
+
errorMap[103].push(key);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
data[key] = value;
|
|
158
|
+
datatype[key] = 'String'; // no i18n
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
// Number fields (engagement_score)
|
|
162
|
+
if (numberFields.includes(key)) {
|
|
163
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) { // no i18n
|
|
164
|
+
errorMap[103].push(key);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
data[key] = value;
|
|
168
|
+
datatype[key] = 'Number'; // no i18n
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
// Custom properties: key must be string, value must be Boolean, String (≤255), or Number (finite)
|
|
172
|
+
if (typeof value === 'boolean') { // no i18n
|
|
173
|
+
data[key] = value;
|
|
174
|
+
datatype[key] = 'Boolean'; // no i18n
|
|
175
|
+
}
|
|
176
|
+
else if (typeof value === 'string') { // no i18n
|
|
177
|
+
if (value.length > 255) {
|
|
178
|
+
errorMap[103].push(key);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
data[key] = value;
|
|
182
|
+
datatype[key] = 'String'; // no i18n
|
|
183
|
+
}
|
|
184
|
+
else if (typeof value === 'number') { // no i18n
|
|
185
|
+
if (!Number.isFinite(value)) {
|
|
186
|
+
errorMap[104].push(key);
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
data[key] = value;
|
|
190
|
+
datatype[key] = 'Number'; // no i18n
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
errorMap[104].push(key);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Property count validation
|
|
198
|
+
if (propCount > USER_PROPERTIES_MAX_COUNT) {
|
|
199
|
+
errorMap[102] = Object.keys(obj);
|
|
200
|
+
}
|
|
201
|
+
// Total length validation: the payload carries data and datatype, so both are counted
|
|
202
|
+
const jsonLength = JSON.stringify(data).length + JSON.stringify(datatype).length;
|
|
203
|
+
if (jsonLength > USER_PROPERTIES_MAX_LENGTH) {
|
|
204
|
+
errorMap[101] = Object.keys(obj);
|
|
205
|
+
}
|
|
206
|
+
// Prepare message array with error_type
|
|
207
|
+
for (const code of [101, 102, 103, 104]) {
|
|
208
|
+
if (errorMap[code].length > 0) {
|
|
209
|
+
message.push({ code, keys: errorMap[code] });
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return { data, datatype, message };
|
|
213
|
+
}
|
|
214
|
+
function validateScreenName(str) {
|
|
215
|
+
if (!str || str === '') { // no i18n
|
|
216
|
+
Console.warn('validateScreenName: Invalid screenName'); // no i18n
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
// Define the regular expression pattern
|
|
220
|
+
//^[a-zA-Z][\\w\\s]{1,100}$
|
|
221
|
+
const pattern = /^[\w\s.\-_&/:*]{0,99}$/;
|
|
222
|
+
// Test if the string matches the pattern
|
|
223
|
+
return pattern.test(str);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export { USER_PROPERTIES_MAX_COUNT, USER_PROPERTIES_MAX_LENGTH, userValidateJSON, validateEventOrGroup, validateJSON, validateScreenName };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Whether we already hold usable ids for the app and version running now. */
|
|
2
|
+
export declare function hasAppVersion(): boolean;
|
|
3
|
+
export declare function appVersionFailedForNetwork(): boolean;
|
|
4
|
+
export declare function registerAppVersion(): Promise<boolean>;
|
|
5
|
+
/**
|
|
6
|
+
* Puts the stored ids back on `_ZAP._device_info`.
|
|
7
|
+
*
|
|
8
|
+
* checkAndUpdateDeviceInfo rebuilds that object from scratch on every start, which would drop
|
|
9
|
+
* what registration put there. Called from inside that rebuild, so the ids survive it.
|
|
10
|
+
*/
|
|
11
|
+
export declare function applyStoredAppVersion(): void;
|
|
12
|
+
/** Forgets the stored ids. reset() calls it, so the next start registers from scratch. */
|
|
13
|
+
export declare function clearAppVersion(): void;
|
|
14
|
+
/** The url this call goes to, for logging and tests. */
|
|
15
|
+
export declare function appVersionUrl(): string;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { _ZAP } from '../core/state.js';
|
|
3
|
+
import { Console, warnAlways } from '../core/logger.js';
|
|
4
|
+
import { isEmpty, isSuccess } from '../core/utils.js';
|
|
5
|
+
import { user_defaults } from '../core/storage.js';
|
|
6
|
+
import { postToApptics, getHeaderParams } from '../core/transport.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* app/addversion — the first call the SDK makes, and the one everything else waits for.
|
|
10
|
+
*
|
|
11
|
+
* It is called with only what init() was given — zak, identifier and appversion — and registers
|
|
12
|
+
* this build with the platform. It answers with the ids every later call needs: aaid, the portal
|
|
13
|
+
* and project ids that form every other url, apid and mapid that authenticate the requests, and
|
|
14
|
+
* appversionid and appreleaseversionid that go in every payload's `meta`. metaWebSDKV2 declares all ten of its fields mandatory, so without this
|
|
15
|
+
* call v2 engagement, exception and log requests are refused outright.
|
|
16
|
+
*
|
|
17
|
+
* It is throttled at ten calls a minute per identifier+platform+version, so the answer is stored
|
|
18
|
+
* and reused: a returning visitor on the same build does not call it again. A changed appversion
|
|
19
|
+
* is a different build and does call it again, which is the point of the endpoint.
|
|
20
|
+
*/
|
|
21
|
+
const LABEL = 'registerAppVersion'; // no i18n
|
|
22
|
+
/** Where the answer is kept between visits. */
|
|
23
|
+
const STORE_KEY = 'app_version_info'; // no i18n
|
|
24
|
+
/** The ids this call is the only source of. Everything else flows from them. */
|
|
25
|
+
const RESULT_KEYS = ['apid', 'mapid', 'aaid', 'appversionid', 'appreleaseversionid', 'platformid', 'projectid', 'portalid']; // no i18n
|
|
26
|
+
var inFlight = null;
|
|
27
|
+
/** The stored answer, if this exact build already registered. */
|
|
28
|
+
function stored() {
|
|
29
|
+
var saved = user_defaults.item(STORE_KEY);
|
|
30
|
+
return saved && typeof saved === 'object' ? saved : {};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The ids without which no later url can be built or authenticated. portalid and projectid are
|
|
34
|
+
* the two path segments of every v2 url but this one; aaid names the app.
|
|
35
|
+
*/
|
|
36
|
+
const REQUIRED_KEYS = ['aaid', 'portalid', 'projectid', 'apid', 'mapid', 'appversionid']; // no i18n
|
|
37
|
+
function missingKeys(data) {
|
|
38
|
+
return REQUIRED_KEYS.filter(function (key) { return isEmpty(data[key]); });
|
|
39
|
+
}
|
|
40
|
+
/** Whether we already hold usable ids for the app and version running now. */
|
|
41
|
+
function hasAppVersion() {
|
|
42
|
+
var saved = stored();
|
|
43
|
+
return missingKeys(saved).length === 0
|
|
44
|
+
&& saved.appversion === _ZAP._defaultoptions.appversion
|
|
45
|
+
&& saved.identifier === _ZAP._defaultoptions.identifier
|
|
46
|
+
&& saved.serviceUrl === _ZAP._defaultoptions.serviceUrl;
|
|
47
|
+
}
|
|
48
|
+
/** Puts the ids where the rest of the SDK reads them from. */
|
|
49
|
+
function apply(data) {
|
|
50
|
+
RESULT_KEYS.forEach(function (key) {
|
|
51
|
+
if (!isEmpty(data[key])) {
|
|
52
|
+
_ZAP._device_info[key] = data[key];
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// the app and the org/project segments of every url come from here, not from init().
|
|
56
|
+
// The server sends portalid as a string and projectid as a number; both become url text
|
|
57
|
+
if (!isEmpty(data.aaid)) {
|
|
58
|
+
_ZAP._defaultoptions.aaid = data.aaid;
|
|
59
|
+
}
|
|
60
|
+
if (!isEmpty(data.portalid)) {
|
|
61
|
+
_ZAP._defaultoptions.zsoid = String(data.portalid);
|
|
62
|
+
}
|
|
63
|
+
if (!isEmpty(data.projectid)) {
|
|
64
|
+
_ZAP._defaultoptions.projectID = String(data.projectid);
|
|
65
|
+
}
|
|
66
|
+
// apid and mapid are also read straight off state for the request headers. The server
|
|
67
|
+
// answers with numbers, and a header value is a string
|
|
68
|
+
if (!isEmpty(data.apid)) {
|
|
69
|
+
_ZAP._apid = String(data.apid);
|
|
70
|
+
}
|
|
71
|
+
if (!isEmpty(data.mapid)) {
|
|
72
|
+
_ZAP._mapid = String(data.mapid);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Registers this build, or reuses the stored answer.
|
|
77
|
+
*
|
|
78
|
+
* Resolves true when the ids are in place, false when they are not — the caller stops there
|
|
79
|
+
* rather than carrying on to calls that would be refused for missing them.
|
|
80
|
+
*/
|
|
81
|
+
/**
|
|
82
|
+
* Whether the last registration failed for want of a network (offline, a dropped connection, a
|
|
83
|
+
* timeout) rather than because the server answered no. Only the first is worth trying again.
|
|
84
|
+
*/
|
|
85
|
+
var failedForNetwork = false;
|
|
86
|
+
function appVersionFailedForNetwork() {
|
|
87
|
+
return failedForNetwork;
|
|
88
|
+
}
|
|
89
|
+
function registerAppVersion() {
|
|
90
|
+
failedForNetwork = false;
|
|
91
|
+
if (hasAppVersion()) {
|
|
92
|
+
apply(stored());
|
|
93
|
+
Console.log(LABEL + ': reusing the ids stored for ' + _ZAP._defaultoptions.appversion); // no i18n
|
|
94
|
+
return Promise.resolve(true);
|
|
95
|
+
}
|
|
96
|
+
// a second caller joins the first rather than spending another of the ten calls a minute
|
|
97
|
+
if (inFlight !== null) {
|
|
98
|
+
return inFlight;
|
|
99
|
+
}
|
|
100
|
+
var frameworkid = !isEmpty(_ZAP._device_info.frameworkid) ? _ZAP._device_info.frameworkid : _ZAP._frameworkID;
|
|
101
|
+
if (frameworkid === 0) {
|
|
102
|
+
// a build made without npm pack: it still runs, but the server has no record of this SDK
|
|
103
|
+
warnAlways(LABEL + ': this build has no registered framework id; build the package with npm pack'); // no i18n
|
|
104
|
+
}
|
|
105
|
+
if (isEmpty(frameworkid) || isEmpty(_ZAP._defaultoptions.identifier) || isEmpty(_ZAP._defaultoptions.zak)) {
|
|
106
|
+
warnAlways(LABEL + ': need zak, an identifier and a frameworkid to register this app version'); // no i18n
|
|
107
|
+
return Promise.resolve(false);
|
|
108
|
+
}
|
|
109
|
+
inFlight = postToApptics('addversion', '', { zak: getHeaderParams('zak') }, LABEL) // no i18n
|
|
110
|
+
.then(function (result) {
|
|
111
|
+
if (!isSuccess(result) || !result.data) {
|
|
112
|
+
warnAlways(LABEL + ': the server did not return the app ids'); // no i18n
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
var data = result.data;
|
|
116
|
+
var missing = missingKeys(data);
|
|
117
|
+
if (missing.length > 0) {
|
|
118
|
+
warnAlways(LABEL + ': the answer has no ' + missing.join(', ')); // no i18n
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
apply(data);
|
|
122
|
+
// stored against the app and version it describes, so a new build registers again
|
|
123
|
+
var keep = {
|
|
124
|
+
appversion: _ZAP._defaultoptions.appversion,
|
|
125
|
+
identifier: _ZAP._defaultoptions.identifier,
|
|
126
|
+
serviceUrl: _ZAP._defaultoptions.serviceUrl,
|
|
127
|
+
};
|
|
128
|
+
RESULT_KEYS.forEach(function (key) {
|
|
129
|
+
if (!isEmpty(data[key])) {
|
|
130
|
+
keep[key] = data[key];
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
user_defaults.add(STORE_KEY, keep);
|
|
134
|
+
Console.log(LABEL + ': registered ' + _ZAP._defaultoptions.appversion + ' as apid ' + data.apid); // no i18n
|
|
135
|
+
return true;
|
|
136
|
+
})
|
|
137
|
+
.catch(function (err) {
|
|
138
|
+
// a server answer carries serverResult; a request that never got one does not
|
|
139
|
+
failedForNetwork = !(err && err.serverResult) || navigator.onLine === false;
|
|
140
|
+
warnAlways(LABEL + ': could not register this app version: ' + String(err && err.message ? err.message : err)); // no i18n
|
|
141
|
+
return false;
|
|
142
|
+
})
|
|
143
|
+
.then(function (ok) {
|
|
144
|
+
inFlight = null;
|
|
145
|
+
return ok;
|
|
146
|
+
});
|
|
147
|
+
return inFlight;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Puts the stored ids back on `_ZAP._device_info`.
|
|
151
|
+
*
|
|
152
|
+
* checkAndUpdateDeviceInfo rebuilds that object from scratch on every start, which would drop
|
|
153
|
+
* what registration put there. Called from inside that rebuild, so the ids survive it.
|
|
154
|
+
*/
|
|
155
|
+
function applyStoredAppVersion() {
|
|
156
|
+
apply(stored());
|
|
157
|
+
}
|
|
158
|
+
/** Forgets the stored ids. reset() calls it, so the next start registers from scratch. */
|
|
159
|
+
function clearAppVersion() {
|
|
160
|
+
inFlight = null;
|
|
161
|
+
user_defaults.add(STORE_KEY, undefined);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export { appVersionFailedForNetwork, applyStoredAppVersion, clearAppVersion, hasAppVersion, registerAppVersion };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DeviceInfo, Dict, ServerResult } from '../types.js';
|
|
2
|
+
export declare function checkAndUpdateDeviceInfo(): void;
|
|
3
|
+
export declare function addOrUpdateDeviceID(device_info: DeviceInfo, previous?: DeviceInfo): void;
|
|
4
|
+
/**
|
|
5
|
+
* Sets up this browser's device at start-up: the uuid, the device info, and a registration when
|
|
6
|
+
* the browser has none.
|
|
7
|
+
*
|
|
8
|
+
* Settings are this tab's own copy, loaded when it started. On a browser with no device yet, two
|
|
9
|
+
* tabs started together would each register it and store two devices, so the first visit runs
|
|
10
|
+
* under a lock shared by every tab of the site: the tab that gets it re-reads storage, registers
|
|
11
|
+
* only when no other tab has, and keeps the lock until the new ids are stored. The next tab then
|
|
12
|
+
* finds them. Start-up goes on as soon as the device info is set, without waiting for the server.
|
|
13
|
+
*/
|
|
14
|
+
export declare function prepareDevice(): Promise<void>;
|
|
15
|
+
/** True while the device has no server ids, so nothing can be sent yet. */
|
|
16
|
+
export declare function needsDevice(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Resolves once a registration that is in flight has settled, succeeded or not, and right away
|
|
19
|
+
* when none is running. Callers use it to wait for the ids rather than poll for them.
|
|
20
|
+
*/
|
|
21
|
+
export declare function deviceReady(): Promise<void>;
|
|
22
|
+
export declare function registerDeviceWithApptics(): Promise<ServerResult | DeviceInfo>;
|
|
23
|
+
export declare function updateDeviceWithApptics(device_info: DeviceInfo): Promise<ServerResult>;
|
|
24
|
+
export declare function getUpdatesFromServer(): Promise<Dict>;
|
|
25
|
+
/**
|
|
26
|
+
* Start-up: applies the server settings saved by the last getupdates, so a "stop tracking"
|
|
27
|
+
* answer holds across page loads instead of resetting to "on" until the next check.
|
|
28
|
+
*/
|
|
29
|
+
export declare function restoreServerSettings(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Fetches the server settings now if the device is registered. On a first visit the ids don't
|
|
32
|
+
* exist yet; registerDeviceWithApptics calls this as soon as device/add succeeds.
|
|
33
|
+
*/
|
|
34
|
+
export declare function refreshServerSettings(): void;
|