@zoho_apptics/apptics-js-sdk 1.0.2 → 2.0.0-beta
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 +87 -0
- package/LICENSE +13 -0
- package/README.md +747 -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/batch-BU_mTpm1.min.js +3 -0
- package/dist/chunks/batch-BU_mTpm1.min.js.map +1 -0
- package/dist/chunks/logs-Bea5zUW_.min.js +3 -0
- package/dist/chunks/logs-Bea5zUW_.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 +115 -0
- package/dist/cjs/core/callsite.d.ts +28 -0
- package/dist/cjs/core/callsite.js +93 -0
- package/dist/cjs/core/consent.d.ts +23 -0
- package/dist/cjs/core/consent.js +130 -0
- package/dist/cjs/core/constants.d.ts +212 -0
- package/dist/cjs/core/constants.js +299 -0
- package/dist/cjs/core/db.d.ts +40 -0
- package/dist/cjs/core/db.js +334 -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 +2 -0
- package/dist/cjs/core/guard.js +72 -0
- package/dist/cjs/core/hooks.d.ts +41 -0
- package/dist/cjs/core/hooks.js +23 -0
- package/dist/cjs/core/logger.d.ts +16 -0
- package/dist/cjs/core/logger.js +86 -0
- package/dist/cjs/core/state.d.ts +69 -0
- package/dist/cjs/core/state.js +78 -0
- package/dist/cjs/core/storage.d.ts +46 -0
- package/dist/cjs/core/storage.js +229 -0
- package/dist/cjs/core/transport.d.ts +83 -0
- package/dist/cjs/core/transport.js +441 -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 +20 -0
- package/dist/cjs/device/appversion.js +158 -0
- package/dist/cjs/device/index.d.ts +23 -0
- package/dist/cjs/device/index.js +372 -0
- package/dist/cjs/engagement/api-rules.d.ts +84 -0
- package/dist/cjs/engagement/api-rules.js +219 -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 +16 -0
- package/dist/cjs/engagement/auto-screen.js +241 -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 +99 -0
- package/dist/cjs/engagement/session.d.ts +42 -0
- package/dist/cjs/engagement/session.js +327 -0
- package/dist/cjs/engagement/track.d.ts +56 -0
- package/dist/cjs/engagement/track.js +350 -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 +127 -0
- package/dist/cjs/feedback/controls.d.ts +31 -0
- package/dist/cjs/feedback/controls.js +286 -0
- package/dist/cjs/feedback/index.d.ts +8 -0
- package/dist/cjs/feedback/index.js +37 -0
- package/dist/cjs/feedback/logs.d.ts +40 -0
- package/dist/cjs/feedback/logs.js +202 -0
- package/dist/cjs/feedback/send.d.ts +25 -0
- package/dist/cjs/feedback/send.js +178 -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 +55 -0
- package/dist/cjs/feedback/ui.js +631 -0
- package/dist/cjs/heatmap/index.d.ts +16 -0
- package/dist/cjs/heatmap/index.js +22 -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 +377 -0
- package/dist/cjs/index.d.ts +68 -0
- package/dist/cjs/index.js +150 -0
- package/dist/cjs/lifecycle.d.ts +14 -0
- package/dist/cjs/lifecycle.js +538 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/remoteconfig/custom.d.ts +10 -0
- package/dist/cjs/remoteconfig/custom.js +78 -0
- package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
- package/dist/cjs/remoteconfig/evaluate.js +171 -0
- package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
- package/dist/cjs/remoteconfig/fetch.js +214 -0
- package/dist/cjs/remoteconfig/index.d.ts +13 -0
- package/dist/cjs/remoteconfig/index.js +90 -0
- package/dist/cjs/remoteconfig/store.d.ts +18 -0
- package/dist/cjs/remoteconfig/store.js +98 -0
- package/dist/cjs/remotelog/gate.d.ts +11 -0
- package/dist/cjs/remotelog/gate.js +54 -0
- package/dist/cjs/remotelog/index.d.ts +25 -0
- package/dist/cjs/remotelog/index.js +55 -0
- package/dist/cjs/remotelog/logger.d.ts +28 -0
- package/dist/cjs/remotelog/logger.js +139 -0
- package/dist/cjs/remotelog/send.d.ts +13 -0
- package/dist/cjs/remotelog/send.js +153 -0
- package/dist/cjs/scheduler.d.ts +8 -0
- package/dist/cjs/scheduler.js +78 -0
- package/dist/cjs/types.d.ts +312 -0
- package/dist/cjs/user/index.d.ts +33 -0
- package/dist/cjs/user/index.js +178 -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 +108 -0
- package/dist/esm/core/callsite.d.ts +28 -0
- package/dist/esm/core/callsite.js +89 -0
- package/dist/esm/core/consent.d.ts +23 -0
- package/dist/esm/core/consent.js +120 -0
- package/dist/esm/core/constants.d.ts +212 -0
- package/dist/esm/core/constants.js +277 -0
- package/dist/esm/core/db.d.ts +40 -0
- package/dist/esm/core/db.js +324 -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 +2 -0
- package/dist/esm/core/guard.js +70 -0
- package/dist/esm/core/hooks.d.ts +41 -0
- package/dist/esm/core/hooks.js +21 -0
- package/dist/esm/core/logger.d.ts +16 -0
- package/dist/esm/core/logger.js +82 -0
- package/dist/esm/core/state.d.ts +69 -0
- package/dist/esm/core/state.js +73 -0
- package/dist/esm/core/storage.d.ts +46 -0
- package/dist/esm/core/storage.js +219 -0
- package/dist/esm/core/transport.d.ts +83 -0
- package/dist/esm/core/transport.js +425 -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 +20 -0
- package/dist/esm/device/appversion.js +153 -0
- package/dist/esm/device/index.d.ts +23 -0
- package/dist/esm/device/index.js +362 -0
- package/dist/esm/engagement/api-rules.d.ts +84 -0
- package/dist/esm/engagement/api-rules.js +211 -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 +16 -0
- package/dist/esm/engagement/auto-screen.js +238 -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 +96 -0
- package/dist/esm/engagement/session.d.ts +42 -0
- package/dist/esm/engagement/session.js +315 -0
- package/dist/esm/engagement/track.d.ts +56 -0
- package/dist/esm/engagement/track.js +340 -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 +123 -0
- package/dist/esm/feedback/controls.d.ts +31 -0
- package/dist/esm/feedback/controls.js +280 -0
- package/dist/esm/feedback/index.d.ts +8 -0
- package/dist/esm/feedback/index.js +30 -0
- package/dist/esm/feedback/logs.d.ts +40 -0
- package/dist/esm/feedback/logs.js +188 -0
- package/dist/esm/feedback/send.d.ts +25 -0
- package/dist/esm/feedback/send.js +175 -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 +55 -0
- package/dist/esm/feedback/ui.js +626 -0
- package/dist/esm/heatmap/index.d.ts +16 -0
- package/dist/esm/heatmap/index.js +17 -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 +374 -0
- package/dist/esm/index.d.ts +68 -0
- package/dist/esm/index.js +122 -0
- package/dist/esm/lifecycle.d.ts +14 -0
- package/dist/esm/lifecycle.js +534 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/remoteconfig/custom.d.ts +10 -0
- package/dist/esm/remoteconfig/custom.js +74 -0
- package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
- package/dist/esm/remoteconfig/evaluate.js +166 -0
- package/dist/esm/remoteconfig/fetch.d.ts +24 -0
- package/dist/esm/remoteconfig/fetch.js +210 -0
- package/dist/esm/remoteconfig/index.d.ts +13 -0
- package/dist/esm/remoteconfig/index.js +85 -0
- package/dist/esm/remoteconfig/store.d.ts +18 -0
- package/dist/esm/remoteconfig/store.js +91 -0
- package/dist/esm/remotelog/gate.d.ts +11 -0
- package/dist/esm/remotelog/gate.js +48 -0
- package/dist/esm/remotelog/index.d.ts +25 -0
- package/dist/esm/remotelog/index.js +51 -0
- package/dist/esm/remotelog/logger.d.ts +28 -0
- package/dist/esm/remotelog/logger.js +136 -0
- package/dist/esm/remotelog/send.d.ts +13 -0
- package/dist/esm/remotelog/send.js +149 -0
- package/dist/esm/scheduler.d.ts +8 -0
- package/dist/esm/scheduler.js +74 -0
- package/dist/esm/types.d.ts +312 -0
- package/dist/esm/user/index.d.ts +33 -0
- package/dist/esm/user/index.js +170 -0
- package/dist/feedback.esm.min.js +3 -0
- package/dist/feedback.esm.min.js.map +1 -0
- package/dist/heatmap.esm.min.js +3 -0
- package/dist/heatmap.esm.min.js.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +124 -7
- package/src/appticssdk.js +0 -1
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var constants = require('./constants.js');
|
|
5
|
+
var state = require('./state.js');
|
|
6
|
+
var logger = require('./logger.js');
|
|
7
|
+
var utils = require('./utils.js');
|
|
8
|
+
var storage = require('./storage.js');
|
|
9
|
+
var env = require('./env.js');
|
|
10
|
+
var consent = require('./consent.js');
|
|
11
|
+
var attempts = require('./attempts.js');
|
|
12
|
+
var debug = require('./debug.js');
|
|
13
|
+
var hooks = require('./hooks.js');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* How long a request may take before it counts as failed. Without a limit a request the network
|
|
17
|
+
* never answers keeps its caller waiting for good, and with it the queue it was sending.
|
|
18
|
+
*/
|
|
19
|
+
const REQUEST_TIMEOUT_MS = 30000;
|
|
20
|
+
// Function to make an asynchronous request
|
|
21
|
+
function makeRequest(url, method, payload, headers) {
|
|
22
|
+
return new Promise((resolve, reject) => {
|
|
23
|
+
let xhr = new XMLHttpRequest();
|
|
24
|
+
xhr.open(method, url);
|
|
25
|
+
if (headers) {
|
|
26
|
+
Object.keys(headers).forEach((key) => {
|
|
27
|
+
xhr.setRequestHeader(key, headers[key]);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
xhr.withCredentials = false;
|
|
31
|
+
xhr.timeout = REQUEST_TIMEOUT_MS;
|
|
32
|
+
xhr.onreadystatechange = function () {
|
|
33
|
+
if (xhr.readyState === 4) { // XMLHttpRequest.DONE == 4, except in safari 4
|
|
34
|
+
if (xhr.status == 200) {
|
|
35
|
+
resolve(xhr.responseText);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
reject(xhr.statusText);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
xhr.onerror = function () {
|
|
43
|
+
reject('makeRequest: Network Error'); // no i18n
|
|
44
|
+
};
|
|
45
|
+
xhr.ontimeout = function () {
|
|
46
|
+
reject('makeRequest: timed out after ' + REQUEST_TIMEOUT_MS + ' ms'); // no i18n
|
|
47
|
+
};
|
|
48
|
+
xhr.send(payload);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function getHeaderParams(key) {
|
|
52
|
+
if (key == 'zak') { // no i18n
|
|
53
|
+
return state._ZAP._defaultoptions.zak;
|
|
54
|
+
}
|
|
55
|
+
else if (key == 'uuid') { // no i18n
|
|
56
|
+
return state._ZAP._uuid;
|
|
57
|
+
}
|
|
58
|
+
else if (key == 'apid') { // no i18n
|
|
59
|
+
return state._ZAP._apid || storage.getKeyFromDeviceInfo(key);
|
|
60
|
+
}
|
|
61
|
+
else if (key == 'mapid') { // no i18n
|
|
62
|
+
return state._ZAP._mapid || storage.getKeyFromDeviceInfo(key);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
return ''; // no i18n
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* appversionid and frameworkid, which every v2 url but app/addversion declares min-occurrences="1".
|
|
70
|
+
*
|
|
71
|
+
* They are appended whatever else the path carries, including on a call made before the device is
|
|
72
|
+
* registered: leaving a mandatory param out gets the request refused for *that* param, which sends
|
|
73
|
+
* whoever reads the error looking for the wrong fault. frameworkid falls back to the SDK's own id
|
|
74
|
+
* because device_info has none until the first device/add answers.
|
|
75
|
+
*
|
|
76
|
+
* Returned with a leading separator chosen by the caller's query, so it appends to '' as well as
|
|
77
|
+
* to an existing '?...' string.
|
|
78
|
+
*/
|
|
79
|
+
function mandatoryParams(existing) {
|
|
80
|
+
var out = ''; // no i18n
|
|
81
|
+
var appversionid = state._ZAP._device_info.appversionid;
|
|
82
|
+
var frameworkid = !utils.isEmpty(state._ZAP._device_info.frameworkid) ? state._ZAP._device_info.frameworkid : state._ZAP._frameworkID;
|
|
83
|
+
if (!utils.isEmpty(appversionid)) {
|
|
84
|
+
out += '&appversionid=' + encodeURIComponent(String(appversionid)); // no i18n
|
|
85
|
+
}
|
|
86
|
+
if (!utils.isEmpty(frameworkid)) {
|
|
87
|
+
out += '&frameworkid=' + encodeURIComponent(String(frameworkid)); // no i18n
|
|
88
|
+
}
|
|
89
|
+
if (out === '') {
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
// '&...' becomes '?...' when it is the whole query
|
|
93
|
+
return (existing === undefined || existing === '') ? '?' + out.slice(1) : out;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A label for this device built from what we already know about it: "Chrome 141 on macOS".
|
|
97
|
+
*
|
|
98
|
+
* safe_string accepts letters, digits and spaces, so nothing here needs escaping beyond the
|
|
99
|
+
* encoding every param gets. Parts we do not have are left out rather than written as 'unknown',
|
|
100
|
+
* which would read as a name in the list.
|
|
101
|
+
*/
|
|
102
|
+
function derivedDeviceName() {
|
|
103
|
+
var info = state._ZAP._device_info;
|
|
104
|
+
var browser = utils.isEmpty(info.browser) ? '' : String(info.browser); // no i18n
|
|
105
|
+
var version = utils.isEmpty(info.browserversion) ? '' : String(info.browserversion); // no i18n
|
|
106
|
+
var os = utils.isEmpty(info.osversion) ? '' : String(info.osversion); // no i18n
|
|
107
|
+
var name = browser === '' ? '' : (version === '' ? browser : browser + ' ' + version); // no i18n
|
|
108
|
+
if (os === '') {
|
|
109
|
+
return name;
|
|
110
|
+
}
|
|
111
|
+
return name === '' ? os : name + ' on ' + os; // no i18n
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* `devicename`, which only engagement, exception and log declare. It is how the debug stream
|
|
115
|
+
* tells one tester's browser from another's, so it is sent when the app named this device, and
|
|
116
|
+
* otherwise only while tracing, where a derived label beats an unnamed row. safe_string needs at
|
|
117
|
+
* least one character, so an empty name is left out rather than sent as ''.
|
|
118
|
+
*/
|
|
119
|
+
function deviceNameParam(path, existing) {
|
|
120
|
+
if (!constants.DEBUG_CAPABLE_PATHS[path]) {
|
|
121
|
+
return ''; // no i18n
|
|
122
|
+
}
|
|
123
|
+
var given = state.getConfig('device_name'); // no i18n
|
|
124
|
+
var name = !utils.isEmpty(given) ? String(given) : (debug.debugEnabled() ? derivedDeviceName() : ''); // no i18n
|
|
125
|
+
if (name === '') {
|
|
126
|
+
return ''; // no i18n
|
|
127
|
+
}
|
|
128
|
+
var param = 'devicename=' + encodeURIComponent(name.slice(0, constants.DEVICE_NAME_MAX)); // no i18n
|
|
129
|
+
return (existing === '' ? '?' : '&') + param; // no i18n
|
|
130
|
+
}
|
|
131
|
+
function getParameters(path) {
|
|
132
|
+
let returnVal = ''; // no i18n
|
|
133
|
+
// app/addversion runs before any id exists: it identifies the build, not the device
|
|
134
|
+
if (path == 'addversion') { // no i18n
|
|
135
|
+
var frameworkid = !utils.isEmpty(state._ZAP._device_info.frameworkid) ? state._ZAP._device_info.frameworkid : state._ZAP._frameworkID;
|
|
136
|
+
return '?version=' + encodeURIComponent(state._ZAP._defaultoptions.appversion)
|
|
137
|
+
+ '&platform=' + encodeURIComponent(state._ZAP._platform)
|
|
138
|
+
+ '&identifier=' + encodeURIComponent(state._ZAP._defaultoptions.identifier)
|
|
139
|
+
+ '&frameworkid=' + encodeURIComponent(String(frameworkid)); // no i18n
|
|
140
|
+
}
|
|
141
|
+
// v2 device/add declares neither identifier, platform nor appversion: the build is already
|
|
142
|
+
// known from app/addversion, and sending an undeclared param is refused as EXTRA_PARAM_FOUND.
|
|
143
|
+
// What it does declare as mandatory is appversionid, which v1 treated as optional.
|
|
144
|
+
if (path == 'regdevice') { // no i18n
|
|
145
|
+
returnVal = '?isanonrequired=' + state._ZAP._isanonrequired + '&devicestatus=' + state._ZAP._devicestatus; // no i18n
|
|
146
|
+
if (!utils.isEmpty(state._ZAP._device_info.deviceid)) {
|
|
147
|
+
returnVal += '&deviceid=' + encodeURIComponent(String(state._ZAP._device_info.deviceid)); // no i18n
|
|
148
|
+
}
|
|
149
|
+
return returnVal + mandatoryParams(returnVal);
|
|
150
|
+
}
|
|
151
|
+
// getsdkconfig is the one call that must NOT carry deviceid: it declares the param with
|
|
152
|
+
// decrypt-label="authenticate", which a web SDK cannot satisfy, and sending it is answered
|
|
153
|
+
// with NOT_AUTHENTICATED. getupdates declares the same param with plain paramdecrypt, so it
|
|
154
|
+
// does carry one. It still needs appversionid and frameworkid, added at the end.
|
|
155
|
+
var identifiesDevice = path != 'sdkconfig'; // no i18n
|
|
156
|
+
if (identifiesDevice) {
|
|
157
|
+
if (utils.isEmpty(state._ZAP._device_info.deviceid)) {
|
|
158
|
+
hooks.hooks.refreshDeviceInfo();
|
|
159
|
+
// the device is missing, but appversionid and frameworkid are mandatory regardless:
|
|
160
|
+
// dropping them turns one missing id into a refusal that names the wrong param
|
|
161
|
+
return returnVal + mandatoryParams(returnVal);
|
|
162
|
+
}
|
|
163
|
+
returnVal = '?deviceid=' + encodeURIComponent(String(state._ZAP._device_info.deviceid)); // no i18n
|
|
164
|
+
}
|
|
165
|
+
// feedback is attributed to whoever the form's user picker selected, not to the active user:
|
|
166
|
+
// the person reporting may not be the one the app last registered, and may choose Anonymous
|
|
167
|
+
if (path === 'feedback') { // no i18n
|
|
168
|
+
var picked = hooks.hooks.feedbackUser();
|
|
169
|
+
if (picked) {
|
|
170
|
+
returnVal += '&userid=' + encodeURIComponent(String(picked.userid)); // no i18n
|
|
171
|
+
if (!utils.isEmpty(picked.groupid)) {
|
|
172
|
+
returnVal += '&customergroupid=' + encodeURIComponent(String(picked.groupid)); // no i18n
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return returnVal + mandatoryParams(returnVal);
|
|
176
|
+
}
|
|
177
|
+
// add userid and groupid if opt_in_status is USAGE_TRACKING_WITH_PII
|
|
178
|
+
if (path != 'reguser' && identifiesDevice) { // no i18n
|
|
179
|
+
if (consent.opt_in_status() == constants.AP_TRACKING_STATUS.USAGE_TRACKING_WITH_PII) {
|
|
180
|
+
if (state._ZAP._user_info.hasOwnProperty('userid') && !utils.isEmpty(state._ZAP._user_info.userid)) { // no i18n
|
|
181
|
+
returnVal += '&userid=' + encodeURIComponent(String(state._ZAP._user_info.userid)); // no i18n
|
|
182
|
+
}
|
|
183
|
+
if (state._ZAP._user_info.hasOwnProperty('groupid') && !utils.isEmpty(state._ZAP._user_info.groupid)) { // no i18n
|
|
184
|
+
returnVal += '&customergroupid=' + encodeURIComponent(String(state._ZAP._user_info.groupid)); // no i18n
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
returnVal += deviceNameParam(path, returnVal);
|
|
189
|
+
return returnVal + mandatoryParams(returnVal);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* `extra` appends params the path needs per call rather than per state, which console/addlog does
|
|
193
|
+
* (happenedtime and module change with every line). Values are encoded; the keys are our own.
|
|
194
|
+
*/
|
|
195
|
+
function getUrlWithParams(path, extra) {
|
|
196
|
+
var base = state._ZAP._defaultoptions.base_domain + state.getConfig('endpoint') + constants.API_VERSIONS[path] + '/'; // no i18n
|
|
197
|
+
var org = constants.PATHS_WITHOUT_ORG[path] ? '' : state._ZAP._defaultoptions.zsoid + '/' + state._ZAP._defaultoptions.projectID + '/'; // no i18n
|
|
198
|
+
// the debug twin sits under the same version, as debug/engagement/add next to engagement/add
|
|
199
|
+
var leaf = (usesDebugApi(path) ? 'debug/' : '') + constants.DEFAULT_API_PATHS[path]; // no i18n
|
|
200
|
+
var url = base + org + leaf + getParameters(path);
|
|
201
|
+
if (!extra) {
|
|
202
|
+
return url;
|
|
203
|
+
}
|
|
204
|
+
var joiner = url.indexOf('?') === -1 ? '?' : '&'; // no i18n
|
|
205
|
+
Object.keys(extra).forEach(function (key) {
|
|
206
|
+
if (extra[key] === undefined || extra[key] === null || extra[key] === '') { // no i18n
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
url += joiner + key + '=' + encodeURIComponent(String(extra[key]));
|
|
210
|
+
joiner = '&'; // no i18n
|
|
211
|
+
});
|
|
212
|
+
return url;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Says where a debug request can be traced. The answer carries a request id and a link into the
|
|
216
|
+
* server's own logs, which is the whole reason for calling the debug twin — printing it is what
|
|
217
|
+
* makes it reachable.
|
|
218
|
+
*/
|
|
219
|
+
function reportDebugTrace(label, result) {
|
|
220
|
+
var data = result.data;
|
|
221
|
+
if (!data || utils.isEmpty(data.req_id)) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
logger.Console.log(label + ': req_id ' + String(data.req_id)
|
|
225
|
+
+ (utils.isEmpty(data.path) ? '' : ', path ' + String(data.path))
|
|
226
|
+
+ (utils.isEmpty(data.producerLogsUrl) ? '' : ', logs ' + String(data.producerLogsUrl))); // no i18n
|
|
227
|
+
}
|
|
228
|
+
/** Whether this path should go to its debug twin. */
|
|
229
|
+
function usesDebugApi(path) {
|
|
230
|
+
// debugEnabled(), not the raw flag: an expired debug session is off even though init() asked
|
|
231
|
+
// for it, and an expired session must stop reaching the twin
|
|
232
|
+
return debug.debugEnabled() && constants.DEBUG_CAPABLE_PATHS[path] === true; // no i18n
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Headers for every call made after device registration.
|
|
236
|
+
*
|
|
237
|
+
* apid and mapid are the only two, because they are the only two every url allows: the v2
|
|
238
|
+
* endpoints declare Access-Control-Allow-Headers as exactly "apid,mapid", and a browser drops
|
|
239
|
+
* any header outside that list at the preflight. Asking for the debug twin is done with the url
|
|
240
|
+
* alone, which needs no permission to send.
|
|
241
|
+
*/
|
|
242
|
+
function apiHeaders() {
|
|
243
|
+
return { apid: getHeaderParams('apid'), mapid: getHeaderParams('mapid') }; // no i18n
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Refusals that mean the payload itself is wrong. Sending it again cannot help, however many
|
|
247
|
+
* times it is tried — the proxy raises these before the server ever sees the body
|
|
248
|
+
* (JProxyExcepHandler: PATTERN_NOT_MATCHED and EXTRA_PARAM_FOUND share one message).
|
|
249
|
+
*/
|
|
250
|
+
const PERMANENT_REFUSALS = {
|
|
251
|
+
PATTERN_NOT_MATCHED: true, // no i18n
|
|
252
|
+
EXTRA_PARAM_FOUND: true, // no i18n
|
|
253
|
+
INVALID_INPUTSTREAM: true, // no i18n
|
|
254
|
+
INVALID_PARAM: true, // no i18n
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Whether a rejection is the server saying "never": the batch is malformed and keeping it would
|
|
258
|
+
* block every later batch behind it, for good. Anything else — a 5xx, a timeout, no network — is
|
|
259
|
+
* worth retrying and must not be treated this way.
|
|
260
|
+
*/
|
|
261
|
+
function isPermanentRefusal(err) {
|
|
262
|
+
var result = err ? err.serverResult : undefined;
|
|
263
|
+
var code = result ? result.error_code : undefined;
|
|
264
|
+
return !utils.isEmpty(code) && PERMANENT_REFUSALS[String(code)] === true;
|
|
265
|
+
}
|
|
266
|
+
/** The reason a response gives, preferring its error_code over the bare "failure". */
|
|
267
|
+
function refusalOf(label, result, raw) {
|
|
268
|
+
var reason = result && !utils.isEmpty(result.error_code) ? String(result.error_code)
|
|
269
|
+
: (result ? String(result.result) : String(raw));
|
|
270
|
+
var err = new Error(label + ': ' + reason);
|
|
271
|
+
if (result) {
|
|
272
|
+
err.serverResult = result;
|
|
273
|
+
}
|
|
274
|
+
return err;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* What an answer means: the parsed result when it reports success, otherwise a rejection that
|
|
278
|
+
* carries the server's own answer. Success also resets the path's attempt counter.
|
|
279
|
+
*/
|
|
280
|
+
function settle(path, label, response) {
|
|
281
|
+
var _result = JSON.parse(response);
|
|
282
|
+
if (utils.isSuccess(_result)) {
|
|
283
|
+
attempts.attemptSucceeded(path);
|
|
284
|
+
reportDebugTrace(label, _result);
|
|
285
|
+
return Promise.resolve(_result);
|
|
286
|
+
}
|
|
287
|
+
var refused = refusalOf(label, _result, response);
|
|
288
|
+
return Promise.reject(refused);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Sends one request. Resolves with the parsed response when it reports success; otherwise
|
|
292
|
+
* rejects with an Error carrying the server's answer. Requests are independent of each other:
|
|
293
|
+
* nothing orders one behind another.
|
|
294
|
+
*/
|
|
295
|
+
function postToApptics(path, payload, headers, label, extra) {
|
|
296
|
+
var url = getUrlWithParams(path, Object.assign(attempts.beginAttempt(path), {}));
|
|
297
|
+
return makeRequest(url, state.getConfig('method'), payload, headers).then(function (response) {
|
|
298
|
+
return settle(path, label, response);
|
|
299
|
+
}, function (err) {
|
|
300
|
+
return Promise.reject(label + ': ' + err); // no i18n
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* The most a page may have in flight as keepalive bodies at once. Browsers refuse a keepalive
|
|
305
|
+
* request once the bodies still in flight would pass 64 KiB; this stays under that with room for
|
|
306
|
+
* the url and headers.
|
|
307
|
+
*/
|
|
308
|
+
const KEEPALIVE_BUDGET = 60 * 1024;
|
|
309
|
+
/** Bytes of keepalive bodies sent and not yet answered. */
|
|
310
|
+
var keepaliveInFlight = 0;
|
|
311
|
+
function byteLength(payload) {
|
|
312
|
+
if (payload === null || payload === undefined) {
|
|
313
|
+
return 0;
|
|
314
|
+
}
|
|
315
|
+
if (typeof payload === 'string') { // no i18n
|
|
316
|
+
return typeof TextEncoder === 'function' ? new TextEncoder().encode(payload).length : payload.length * 3; // no i18n
|
|
317
|
+
}
|
|
318
|
+
var sized = payload;
|
|
319
|
+
return Number(sized.size || sized.byteLength || 0);
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Sends as the page goes away, where a normal XHR would be cancelled with the document.
|
|
323
|
+
*
|
|
324
|
+
* `keepalive` is what lets the request outlive the page. fetch is used rather than sendBeacon
|
|
325
|
+
* because sendBeacon cannot carry the apid and mapid headers these urls require.
|
|
326
|
+
*
|
|
327
|
+
* The request leaves before this returns, so a caller never has to wait on it to send the next
|
|
328
|
+
* thing. The promise settles when the answer arrives, if the page is still there to see it: a
|
|
329
|
+
* caller removes what it sent only then. Data whose answer never comes stays stored and goes
|
|
330
|
+
* again on the next visit as attempt 2, which `attempt` and `firstattempttime` let the server
|
|
331
|
+
* recognise as a copy rather than store twice.
|
|
332
|
+
*
|
|
333
|
+
* A body that would take the page past the keepalive budget is not sent at all: the browser would
|
|
334
|
+
* refuse it, and it is better kept for the next visit than lost.
|
|
335
|
+
*/
|
|
336
|
+
function postKeepalive(path, payload, headers, label, extra) {
|
|
337
|
+
var size = byteLength(payload);
|
|
338
|
+
if (typeof fetch === 'function' && keepaliveInFlight + size > KEEPALIVE_BUDGET) { // no i18n
|
|
339
|
+
logger.Console.log(label + ': ' + size + ' bytes is over the keepalive budget, kept for the next send'); // no i18n
|
|
340
|
+
return Promise.reject(label + ': too large to send while the page closes'); // no i18n
|
|
341
|
+
}
|
|
342
|
+
var url = getUrlWithParams(path, Object.assign(attempts.beginAttempt(path), {}));
|
|
343
|
+
if (typeof fetch === 'function') { // no i18n
|
|
344
|
+
var answer;
|
|
345
|
+
try {
|
|
346
|
+
answer = fetch(url, {
|
|
347
|
+
method: state.getConfig('method'),
|
|
348
|
+
headers: Object.assign({ 'content-type': 'text/plain' }, headers), // no i18n
|
|
349
|
+
body: payload,
|
|
350
|
+
keepalive: true,
|
|
351
|
+
credentials: 'omit', // no i18n
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
catch (err) {
|
|
355
|
+
return Promise.reject(label + ': keepalive send not accepted: ' + String(err)); // no i18n
|
|
356
|
+
}
|
|
357
|
+
keepaliveInFlight += size;
|
|
358
|
+
var release = function () {
|
|
359
|
+
keepaliveInFlight = Math.max(0, keepaliveInFlight - size);
|
|
360
|
+
};
|
|
361
|
+
return answer.then(function (response) {
|
|
362
|
+
release();
|
|
363
|
+
if (!response || response.ok !== true) {
|
|
364
|
+
var status = response ? String(response.status) : ''; // no i18n
|
|
365
|
+
return Promise.reject(label + ': ' + status); // no i18n
|
|
366
|
+
}
|
|
367
|
+
var read = typeof response.text === 'function' ? response.text() : Promise.resolve(''); // no i18n
|
|
368
|
+
return read.then(function (text) {
|
|
369
|
+
// an ok answer with no readable body is taken at its status
|
|
370
|
+
if (utils.isEmpty(text)) {
|
|
371
|
+
return settle(path, label, JSON.stringify({ result: 'success' })); // no i18n
|
|
372
|
+
}
|
|
373
|
+
return settle(path, label, text);
|
|
374
|
+
});
|
|
375
|
+
}, function (err) {
|
|
376
|
+
release();
|
|
377
|
+
return Promise.reject(label + ': ' + String(err)); // no i18n
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
// no fetch at all: a normal request, which the browser may cancel as the page goes away
|
|
381
|
+
return makeRequest(url, state.getConfig('method'), payload, headers).then(function (response) {
|
|
382
|
+
return settle(path, label, response);
|
|
383
|
+
}, function (err) {
|
|
384
|
+
return Promise.reject(label + ': ' + err); // no i18n
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
/** A labelled rejection for crawlers (the SDK does nothing for them), otherwise null. */
|
|
388
|
+
function rejectIfDisabled(label) {
|
|
389
|
+
return env.is_apptics_disabled() ? Promise.reject(label + ': apptics is disabled for this client') : null; // no i18n
|
|
390
|
+
}
|
|
391
|
+
/** A labelled rejection when the browser is offline, otherwise null. */
|
|
392
|
+
function rejectIfOffline(label) {
|
|
393
|
+
if (navigator.onLine == false) {
|
|
394
|
+
logger.Console.warn(label + ': No Network'); // no i18n
|
|
395
|
+
return Promise.reject(label + ': No Network'); // no i18n
|
|
396
|
+
}
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
/** Device ids sent alongside every payload. */
|
|
400
|
+
function getMetaObj() {
|
|
401
|
+
return {
|
|
402
|
+
frameworkid: state._ZAP._device_info.frameworkid || storage.getKeyFromDeviceInfo('frameworkid'), // no i18n
|
|
403
|
+
aaid: state._ZAP._device_info.aaid || storage.getKeyFromDeviceInfo('aaid'), // no i18n
|
|
404
|
+
apid: state._ZAP._device_info.apid || storage.getKeyFromDeviceInfo('apid'), // no i18n
|
|
405
|
+
appversionid: state._ZAP._device_info.appversionid || storage.getKeyFromDeviceInfo('appversionid'), // no i18n
|
|
406
|
+
modelid: state._ZAP._device_info.modelid || storage.getKeyFromDeviceInfo('modelid'), // no i18n
|
|
407
|
+
appreleaseversionid: state._ZAP._device_info.appreleaseversionid || storage.getKeyFromDeviceInfo('appreleaseversionid'), // no i18n
|
|
408
|
+
osversionid: state._ZAP._device_info.osversionid || storage.getKeyFromDeviceInfo('osversionid'), // no i18n
|
|
409
|
+
timezoneid: state._ZAP._device_info.timezoneid || storage.getKeyFromDeviceInfo('timezoneid'), // no i18n
|
|
410
|
+
platformid: state._ZAP._device_info.platformid || storage.getKeyFromDeviceInfo('platformid'), // no i18n
|
|
411
|
+
devicetypeid: state._ZAP._device_info.devicetypeid || storage.getKeyFromDeviceInfo('devicetypeid') // no i18n
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Calls made after registration need apid/mapid. Without them this asks device.ts to
|
|
416
|
+
* refresh device info (which registers) and returns a labelled rejection; otherwise null. Shared by sender, user
|
|
417
|
+
* and feedback (P11).
|
|
418
|
+
*/
|
|
419
|
+
function rejectIfNoDeviceIds(label) {
|
|
420
|
+
if (utils.isEmpty(getHeaderParams('apid')) && utils.isEmpty(getHeaderParams('mapid'))) { // no i18n
|
|
421
|
+
hooks.hooks.refreshDeviceInfo();
|
|
422
|
+
return Promise.reject(label + ': You must call apptics.init() first'); // no i18n
|
|
423
|
+
}
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
exports.KEEPALIVE_BUDGET = KEEPALIVE_BUDGET;
|
|
428
|
+
exports.REQUEST_TIMEOUT_MS = REQUEST_TIMEOUT_MS;
|
|
429
|
+
exports.apiHeaders = apiHeaders;
|
|
430
|
+
exports.getHeaderParams = getHeaderParams;
|
|
431
|
+
exports.getMetaObj = getMetaObj;
|
|
432
|
+
exports.getParameters = getParameters;
|
|
433
|
+
exports.getUrlWithParams = getUrlWithParams;
|
|
434
|
+
exports.isPermanentRefusal = isPermanentRefusal;
|
|
435
|
+
exports.makeRequest = makeRequest;
|
|
436
|
+
exports.postKeepalive = postKeepalive;
|
|
437
|
+
exports.postToApptics = postToApptics;
|
|
438
|
+
exports.rejectIfDisabled = rejectIfDisabled;
|
|
439
|
+
exports.rejectIfNoDeviceIds = rejectIfNoDeviceIds;
|
|
440
|
+
exports.rejectIfOffline = rejectIfOffline;
|
|
441
|
+
exports.usesDebugApi = usesDebugApi;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Dict } from '../types.js';
|
|
2
|
+
/** Returned from an `each` iterator to stop iterating objects and array-likes. */
|
|
3
|
+
export declare const BREAKER: {};
|
|
4
|
+
/** Current time in epoch milliseconds, the unit of every SDK timestamp. */
|
|
5
|
+
export declare function nowMs(): number;
|
|
6
|
+
/** True when an Apptics API response reports success. */
|
|
7
|
+
export declare function isSuccess(result: unknown): boolean;
|
|
8
|
+
export declare function isEmpty(val: unknown): boolean;
|
|
9
|
+
export declare function isEmptyObj(obj: unknown): boolean;
|
|
10
|
+
export declare function objectsAreEqual(obj1: unknown, obj2: unknown): boolean;
|
|
11
|
+
export declare function compareTwoObjects(_newmap: Dict, _oldmap: Dict): boolean;
|
|
12
|
+
export declare function cloneMapExcludingKeys(originalObj: Dict, excludeKeys: readonly string[]): Dict;
|
|
13
|
+
/** Clones a JSON-compatible value. */
|
|
14
|
+
export declare function cloneObject<T>(obj: T): T;
|
|
15
|
+
/** Approximate size of a value in bytes (2 bytes per character). */
|
|
16
|
+
export declare function sizeof(obj: unknown): number;
|
|
17
|
+
/** True if `needle` occurs in the string or array. */
|
|
18
|
+
export declare function includes(haystack: {
|
|
19
|
+
indexOf(needle: any): number;
|
|
20
|
+
}, needle: unknown): boolean;
|
|
21
|
+
export declare function each(obj: any, iterator: (value: any, key: any, collection: any) => unknown, context?: unknown): void;
|
|
22
|
+
/**
|
|
23
|
+
* Generates a new distinct id (code lifted from apptics js).
|
|
24
|
+
* Busy-waits until Date.now() ticks.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getNewUUID(): string;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var constants = require('./constants.js');
|
|
5
|
+
|
|
6
|
+
/** Returned from an `each` iterator to stop iterating objects and array-likes. */
|
|
7
|
+
const BREAKER = {};
|
|
8
|
+
/** Current time in epoch milliseconds, the unit of every SDK timestamp. */
|
|
9
|
+
function nowMs() {
|
|
10
|
+
return Math.round(new Date().getTime());
|
|
11
|
+
}
|
|
12
|
+
/** True when an Apptics API response reports success. */
|
|
13
|
+
function isSuccess(result) {
|
|
14
|
+
return !!result && typeof result === 'object' && result.result == constants.AP_CONSTANTS.SUCCESS;
|
|
15
|
+
}
|
|
16
|
+
function isEmpty(val) {
|
|
17
|
+
// Check if val is empty
|
|
18
|
+
return val === undefined || val === null || val === ''; // no i18n
|
|
19
|
+
}
|
|
20
|
+
function isEmptyObj(obj) {
|
|
21
|
+
return !!obj && typeof obj === 'object' && Object.keys(obj).length === 0 && obj.constructor === Object;
|
|
22
|
+
}
|
|
23
|
+
function objectsAreEqual(obj1, obj2) {
|
|
24
|
+
// Compare the objects' properties
|
|
25
|
+
return JSON.stringify(obj1) === JSON.stringify(obj2);
|
|
26
|
+
}
|
|
27
|
+
function compareTwoObjects(_newmap, _oldmap) {
|
|
28
|
+
// Check if the number of keys is the same.
|
|
29
|
+
if (Object.keys(_newmap).length !== Object.keys(_oldmap).length) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
// Check if keys are the same
|
|
33
|
+
for (let key in _newmap) {
|
|
34
|
+
if (!_oldmap.hasOwnProperty(key)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// Check if objects associated with each key are the same
|
|
39
|
+
for (let key in _newmap) {
|
|
40
|
+
if (!objectsAreEqual(_newmap[key], _oldmap[key])) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Hash maps have the same keys and objects
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
function cloneMapExcludingKeys(originalObj, excludeKeys) {
|
|
48
|
+
const clonedObj = {};
|
|
49
|
+
for (let key in originalObj) {
|
|
50
|
+
if (!excludeKeys.includes(key)) {
|
|
51
|
+
clonedObj[key] = originalObj[key];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return clonedObj;
|
|
55
|
+
}
|
|
56
|
+
/** Clones a JSON-compatible value. */
|
|
57
|
+
function cloneObject(obj) {
|
|
58
|
+
return JSON.parse(JSON.stringify(obj));
|
|
59
|
+
}
|
|
60
|
+
/** Approximate size of a value in bytes (2 bytes per character). */
|
|
61
|
+
function sizeof(obj) {
|
|
62
|
+
let bytes = 0;
|
|
63
|
+
function sizeOf(value) {
|
|
64
|
+
if (value !== null && typeof value === 'object') { // no i18n
|
|
65
|
+
for (const key in value) {
|
|
66
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
67
|
+
bytes += key.length * 2; // 2 bytes per character
|
|
68
|
+
sizeOf(value[key]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (typeof value === 'string') { // no i18n
|
|
73
|
+
bytes += value.length * 2; // 2 bytes per character
|
|
74
|
+
}
|
|
75
|
+
else if (typeof value === 'number') { // no i18n
|
|
76
|
+
bytes += 8; // 8 bytes for a number
|
|
77
|
+
}
|
|
78
|
+
else if (typeof value === 'boolean') { // no i18n
|
|
79
|
+
bytes += 4; // 4 bytes for a boolean
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
sizeOf(obj);
|
|
83
|
+
return bytes;
|
|
84
|
+
}
|
|
85
|
+
/** True if `needle` occurs in the string or array. */
|
|
86
|
+
function includes(haystack, needle) {
|
|
87
|
+
return haystack.indexOf(needle) !== -1;
|
|
88
|
+
}
|
|
89
|
+
function each(obj, iterator, context) {
|
|
90
|
+
if (obj === null || obj === undefined) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (Array.prototype.forEach && obj.forEach === Array.prototype.forEach) {
|
|
94
|
+
obj.forEach(iterator, context);
|
|
95
|
+
}
|
|
96
|
+
else if (obj.length === +obj.length) {
|
|
97
|
+
for (var i = 0, l = obj.length; i < l; i++) {
|
|
98
|
+
if (i in obj && iterator.call(context, obj[i], i, obj) === BREAKER) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
for (var key in obj) {
|
|
105
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
106
|
+
if (iterator.call(context, obj[key], key, obj) === BREAKER) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Generates a new distinct id (code lifted from apptics js).
|
|
115
|
+
* Busy-waits until Date.now() ticks.
|
|
116
|
+
*/
|
|
117
|
+
function getNewUUID() {
|
|
118
|
+
// Time/ticks information
|
|
119
|
+
var T = function () {
|
|
120
|
+
var d = Date.now(), i = 0;
|
|
121
|
+
// this while loop figures how many browser ticks go by
|
|
122
|
+
// before Date.now() returns a new number, ie the amount
|
|
123
|
+
// of ticks that go by per millisecond
|
|
124
|
+
while (d == Date.now()) {
|
|
125
|
+
i++;
|
|
126
|
+
}
|
|
127
|
+
return d.toString(16) + i.toString(16);
|
|
128
|
+
};
|
|
129
|
+
// Math.Random entropy
|
|
130
|
+
var R = function () {
|
|
131
|
+
return Math.random().toString(16).replace('.', ''); // no i18n
|
|
132
|
+
};
|
|
133
|
+
// User agent entropy
|
|
134
|
+
// This function takes the user agent string, and then xors
|
|
135
|
+
// together each sequence of 8 bytes. This produces a final
|
|
136
|
+
// sequence of 8 bytes which it returns as hex.
|
|
137
|
+
var UA = function () {
|
|
138
|
+
var ua = navigator.userAgent, i, ch, buffer = [], ret = 0;
|
|
139
|
+
function xor(result, byte_array) {
|
|
140
|
+
var j, tmp = 0;
|
|
141
|
+
for (j = 0; j < byte_array.length; j++) {
|
|
142
|
+
tmp |= buffer[j] << (j * 8);
|
|
143
|
+
}
|
|
144
|
+
return result ^ tmp;
|
|
145
|
+
}
|
|
146
|
+
for (i = 0; i < ua.length; i++) {
|
|
147
|
+
ch = ua.charCodeAt(i);
|
|
148
|
+
buffer.unshift(ch & 0xff);
|
|
149
|
+
if (buffer.length >= 4) {
|
|
150
|
+
ret = xor(ret, buffer);
|
|
151
|
+
buffer = [];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (buffer.length > 0) {
|
|
155
|
+
ret = xor(ret, buffer);
|
|
156
|
+
}
|
|
157
|
+
return ret.toString(16);
|
|
158
|
+
};
|
|
159
|
+
var se = (screen.height * screen.width).toString(16);
|
|
160
|
+
return T() + '-' + R() + '-' + UA() + '-' + se + '-' + T(); // no i18n
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
exports.BREAKER = BREAKER;
|
|
164
|
+
exports.cloneMapExcludingKeys = cloneMapExcludingKeys;
|
|
165
|
+
exports.cloneObject = cloneObject;
|
|
166
|
+
exports.compareTwoObjects = compareTwoObjects;
|
|
167
|
+
exports.each = each;
|
|
168
|
+
exports.getNewUUID = getNewUUID;
|
|
169
|
+
exports.includes = includes;
|
|
170
|
+
exports.isEmpty = isEmpty;
|
|
171
|
+
exports.isEmptyObj = isEmptyObj;
|
|
172
|
+
exports.isSuccess = isSuccess;
|
|
173
|
+
exports.nowMs = nowMs;
|
|
174
|
+
exports.objectsAreEqual = objectsAreEqual;
|
|
175
|
+
exports.sizeof = sizeof;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Dict, Properties, UserValidationResult } from '../types.js';
|
|
2
|
+
export declare function validateEventOrGroup(str: string | null | undefined): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Event, screen and error properties as the payload carries them: `{ data, datatype, isvalid }`.
|
|
5
|
+
*
|
|
6
|
+
* Each property is judged on its own, as on Android: a bad key (wrong characters, too long, the
|
|
7
|
+
* reserved ap_ prefix) or a value over 250 characters drops that property, and the rest are kept
|
|
8
|
+
* and the event is still logged. Past 24 properties, or 7,666 characters in all, the later ones are
|
|
9
|
+
* dropped. When nothing valid is left the result says why, in the shape the server has always taken
|
|
10
|
+
* for a refused set: `{ isvalid: false, message: { code, keys, valuekeys } }`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateJSON(jsonObj: Properties | Dict | null | undefined): Dict;
|
|
13
|
+
/** user/register accepts at most 10000 characters of body (max-len on its inputstream). */
|
|
14
|
+
export declare const USER_PROPERTIES_MAX_LENGTH = 10000;
|
|
15
|
+
/** user/register accepts at most this many user properties. */
|
|
16
|
+
export declare const USER_PROPERTIES_MAX_COUNT = 45;
|
|
17
|
+
export declare function userValidateJSON(obj: Dict): UserValidationResult;
|
|
18
|
+
export declare function validateScreenName(str: string | null | undefined): boolean;
|