@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,183 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var constants = require('../core/constants.js');
|
|
5
|
+
var state = require('../core/state.js');
|
|
6
|
+
var logger = require('../core/logger.js');
|
|
7
|
+
var utils = require('../core/utils.js');
|
|
8
|
+
var storage = require('../core/storage.js');
|
|
9
|
+
var consent = require('../core/consent.js');
|
|
10
|
+
var transport = require('../core/transport.js');
|
|
11
|
+
var validate = require('../core/validate.js');
|
|
12
|
+
var hooks = require('../core/hooks.js');
|
|
13
|
+
|
|
14
|
+
function getUser() {
|
|
15
|
+
return state._ZAP._user_info || {}; // no i18n
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* user/register wants properties as the validated triple — the values, their types, and what
|
|
19
|
+
* was rejected — which is what UserBuilder.build() returns. A plain object passed to setUser
|
|
20
|
+
* is validated here, so both forms go up in the same shape.
|
|
21
|
+
*/
|
|
22
|
+
function asUserProperties(jsonObj) {
|
|
23
|
+
var maybeBuilt = jsonObj;
|
|
24
|
+
var built = maybeBuilt && typeof maybeBuilt === 'object' && maybeBuilt.data && maybeBuilt.datatype // no i18n
|
|
25
|
+
? maybeBuilt
|
|
26
|
+
: validate.userValidateJSON(jsonObj);
|
|
27
|
+
built.message.forEach(function (problem) {
|
|
28
|
+
logger.logFailure(constants.AP_LOG_MODULE.PROPERTIES, 'AppticsUser.setUser: dropped ' + problem.keys.join(', ') + ' (error ' + problem.code + ')'); // no i18n
|
|
29
|
+
});
|
|
30
|
+
return built;
|
|
31
|
+
}
|
|
32
|
+
function setUser(userid, groupid, jsonObj) {
|
|
33
|
+
let _jsonObj = jsonObj || {};
|
|
34
|
+
userid = userid || ''; // no i18n
|
|
35
|
+
groupid = groupid || ''; // no i18n
|
|
36
|
+
if (!consent.canTrack('setUser')) { // no i18n
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (utils.isEmpty(userid)) {
|
|
40
|
+
logger.logFailure(constants.AP_LOG_MODULE.PROPERTIES, 'AppticsUser.setUser: Invalid userid'); // no i18n
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
var user_info = {
|
|
44
|
+
mam: userid,
|
|
45
|
+
customergroupid: groupid,
|
|
46
|
+
appversionid: state._ZAP._device_info.appversionid || '', // no i18n
|
|
47
|
+
status: 1,
|
|
48
|
+
happendat: utils.nowMs()
|
|
49
|
+
};
|
|
50
|
+
if (!utils.isEmptyObj(_jsonObj)) { // no i18n
|
|
51
|
+
user_info.userproperties = asUserProperties(_jsonObj);
|
|
52
|
+
}
|
|
53
|
+
registerUserWithApptics(user_info).then(function (user_info) {
|
|
54
|
+
logger.Console.log('setUser: ' + JSON.stringify(user_info)); // no i18n
|
|
55
|
+
}).catch(function (err) {
|
|
56
|
+
logger.Console.log(err);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function addOrUpdateUser(user_info) {
|
|
60
|
+
user_info.status = 1;
|
|
61
|
+
var users = storage.user_defaults.item('users') || []; // no i18n
|
|
62
|
+
users = users.map((item) => ({ ...item, status: 0 }));
|
|
63
|
+
var index = users.findIndex((userobj) => userobj.userid === user_info.userid);
|
|
64
|
+
if (index !== -1) {
|
|
65
|
+
users[index] = { ...users[index], ...user_info };
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
users.push(user_info);
|
|
69
|
+
}
|
|
70
|
+
storage.user_defaults.add('users', users); // no i18n
|
|
71
|
+
// the user setUser registered, on its own as well: what this browser is tracking as now
|
|
72
|
+
storage.user_defaults.add('user_info', users[index !== -1 ? index : users.length - 1]); // no i18n
|
|
73
|
+
}
|
|
74
|
+
function getUserInfo() {
|
|
75
|
+
var stored = storage.user_defaults.item('user_info'); // no i18n
|
|
76
|
+
if (stored && typeof stored === 'object' && !utils.isEmpty(stored.userid)) { // no i18n
|
|
77
|
+
state._ZAP._user_info = stored;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// registered before user_info was kept: the active one of the users list
|
|
81
|
+
var users = storage.user_defaults.item('users') || []; // no i18n
|
|
82
|
+
var activeuser = {};
|
|
83
|
+
if (users.length > 0) {
|
|
84
|
+
activeuser = users.filter((item) => item.status === 1)[0] || {};
|
|
85
|
+
}
|
|
86
|
+
state._ZAP._user_info = activeuser;
|
|
87
|
+
}
|
|
88
|
+
// Function to register the user
|
|
89
|
+
function registerUserWithApptics(user_info) {
|
|
90
|
+
var label = 'registerUserWithApptics'; // no i18n
|
|
91
|
+
var blocked = transport.rejectIfDisabled(label) || transport.rejectIfOffline(label);
|
|
92
|
+
if (blocked) {
|
|
93
|
+
return blocked;
|
|
94
|
+
}
|
|
95
|
+
if (utils.isEmptyObj(user_info)) {
|
|
96
|
+
return Promise.reject(label + ': nothing to register'); // no i18n
|
|
97
|
+
}
|
|
98
|
+
var missing = transport.rejectIfNoDeviceIds(label);
|
|
99
|
+
if (missing) {
|
|
100
|
+
return missing;
|
|
101
|
+
}
|
|
102
|
+
var payload = JSON.stringify(utils.cloneMapExcludingKeys(user_info, ['userid', 'groupid', 'happendat'])); // no i18n
|
|
103
|
+
return transport.postToApptics('reguser', payload, transport.apiHeaders(), label).then(function (result) {
|
|
104
|
+
var data = result.data || {};
|
|
105
|
+
user_info.userid = data.userid;
|
|
106
|
+
user_info.groupid = data.customergroupid;
|
|
107
|
+
consent.setTrackingStatus(constants.AP_TRACKING_STATUS.USAGE_TRACKING_WITH_PII);
|
|
108
|
+
addOrUpdateUser(user_info);
|
|
109
|
+
getUserInfo();
|
|
110
|
+
// the user and their customer group are known now: remote config conditions on them re-apply
|
|
111
|
+
hooks.hooks.remoteConfigUserChanged();
|
|
112
|
+
return data;
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/** Builds validated user properties for setUser. */
|
|
116
|
+
class UserBuilder {
|
|
117
|
+
setFirstName(first_name) { this.first_name = first_name; return this; }
|
|
118
|
+
setLastName(last_name) { this.last_name = last_name; return this; }
|
|
119
|
+
setCompanyName(company_name) { this.company_name = company_name; return this; }
|
|
120
|
+
setEmailAddress(email_address) { this.email_address = email_address; return this; }
|
|
121
|
+
setContactNumber(contact_number) { this.contact_number = contact_number; return this; }
|
|
122
|
+
setCountry(country) { this.country = country; return this; }
|
|
123
|
+
setRegion(region) { this.region = region; return this; }
|
|
124
|
+
setCity(city) { this.city = city; return this; }
|
|
125
|
+
setGeoLocation(geo_location) { this.geo_location = geo_location; return this; }
|
|
126
|
+
setGender(gender) { this.gender = gender; return this; }
|
|
127
|
+
setPlanType(plan_type) { this.plan_type = plan_type; return this; }
|
|
128
|
+
setTimezone(timezone) { this.timezone = timezone; return this; }
|
|
129
|
+
setLanguage(language) { this.language = language; return this; }
|
|
130
|
+
setEngagementScore(engagement_score) { this.engagement_score = engagement_score; return this; }
|
|
131
|
+
setDateOfBirth(date_of_birth) { this.date_of_birth = date_of_birth; return this; }
|
|
132
|
+
addStringProperty(key, value) {
|
|
133
|
+
if (typeof value === 'string') { // no i18n
|
|
134
|
+
this[key] = value;
|
|
135
|
+
}
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
addBooleanProperty(key, value) {
|
|
139
|
+
if (typeof value === 'boolean') { // no i18n
|
|
140
|
+
this[key] = value;
|
|
141
|
+
}
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
addNumberProperty(key, value) {
|
|
145
|
+
if (typeof value === 'number' && Number.isFinite(value)) { // no i18n
|
|
146
|
+
this[key] = value;
|
|
147
|
+
}
|
|
148
|
+
return this;
|
|
149
|
+
}
|
|
150
|
+
setCustomProperties(obj) {
|
|
151
|
+
if (typeof obj === 'object' && obj !== null) { // no i18n
|
|
152
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
153
|
+
this[key] = value;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
build() {
|
|
159
|
+
const user = {};
|
|
160
|
+
const knownFields = [
|
|
161
|
+
'first_name', 'last_name', 'company_name', 'email_address', 'contact_number',
|
|
162
|
+
'country', 'region', 'city', 'geo_location', 'gender', 'plan_type', 'timezone',
|
|
163
|
+
'language', 'engagement_score', 'date_of_birth'
|
|
164
|
+
]; // no i18n
|
|
165
|
+
knownFields.forEach((field) => {
|
|
166
|
+
if (this[field] !== undefined)
|
|
167
|
+
user[field] = this[field];
|
|
168
|
+
});
|
|
169
|
+
Object.entries(this).forEach(([key, value]) => {
|
|
170
|
+
if (knownFields.indexOf(key) === -1 && typeof value !== 'function') { // no i18n
|
|
171
|
+
user[key] = value;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return validate.userValidateJSON(user);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
exports.UserBuilder = UserBuilder;
|
|
179
|
+
exports.addOrUpdateUser = addOrUpdateUser;
|
|
180
|
+
exports.getUser = getUser;
|
|
181
|
+
exports.getUserInfo = getUserInfo;
|
|
182
|
+
exports.registerUserWithApptics = registerUserWithApptics;
|
|
183
|
+
exports.setUser = setUser;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AppticsSdk } from './types.js';
|
|
2
|
+
/** Calls Apptics.init() when the page has the console snippet (window.appticssettings) and it hasn't run. */
|
|
3
|
+
export declare function autoInit(api: AppticsSdk['Apptics']): boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Queues autoInit so an app that sets window.appticssettings and calls init() in its
|
|
6
|
+
* top-level code runs first (autoInit then sees init() already ran).
|
|
7
|
+
*/
|
|
8
|
+
export declare function scheduleAutoInit(api: AppticsSdk['Apptics']): void;
|
|
@@ -0,0 +1,29 @@
|
|
|
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 { getSnippetOptions } from './core/env.js';
|
|
4
|
+
|
|
5
|
+
/** Calls Apptics.init() when the page has the console snippet (window.appticssettings) and it hasn't run. */
|
|
6
|
+
function autoInit(api) {
|
|
7
|
+
if (_ZAP._initialised) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
var opts = getSnippetOptions();
|
|
11
|
+
if (!opts || typeof opts !== 'object') { // no i18n
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return api.init(opts);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Queues autoInit so an app that sets window.appticssettings and calls init() in its
|
|
18
|
+
* top-level code runs first (autoInit then sees init() already ran).
|
|
19
|
+
*/
|
|
20
|
+
function scheduleAutoInit(api) {
|
|
21
|
+
if (typeof window === 'undefined') { // no i18n
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
queueMicrotask(function () {
|
|
25
|
+
autoInit(api);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { autoInit, scheduleAutoInit };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Dict } from '../types.js';
|
|
2
|
+
/** The largest value `attempt` may carry, from its own regex. */
|
|
3
|
+
export declare const MAX_ATTEMPTS = 5;
|
|
4
|
+
/**
|
|
5
|
+
* Whether this path has already used its five attempts, so the batch should be dropped rather
|
|
6
|
+
* than sent a sixth time with a param the proxy will refuse.
|
|
7
|
+
*/
|
|
8
|
+
export declare function attemptsExhausted(path: string): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Records that a send is going out, and returns the params to put on it.
|
|
11
|
+
*
|
|
12
|
+
* Call it once per request, immediately before sending: it is what moves the attempt counter on,
|
|
13
|
+
* so calling it twice for one request would skip a number and reach five early.
|
|
14
|
+
*/
|
|
15
|
+
export declare function beginAttempt(path: string): Dict;
|
|
16
|
+
/**
|
|
17
|
+
* Records that a send was accepted: the counter goes back to 1 and `lst` moves to now.
|
|
18
|
+
*
|
|
19
|
+
* For a page-close send the confirmation arrives after the caller has moved on, which is fine —
|
|
20
|
+
* nothing waits for this, it only decides what the *next* send says about itself.
|
|
21
|
+
*/
|
|
22
|
+
export declare function attemptSucceeded(path: string): void;
|
|
23
|
+
/** Gives up on the current run of attempts, so the next send starts again at 1. */
|
|
24
|
+
export declare function resetAttempts(path: string): void;
|
|
25
|
+
/** When this path last had a send accepted, or 0. */
|
|
26
|
+
export declare function lastSendTime(path: string): number;
|
|
27
|
+
/** Forgets every path's bookkeeping. reset() calls it. */
|
|
28
|
+
export declare function clearAttempts(): void;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { RETRY_CAPABLE_PATHS } from './constants.js';
|
|
3
|
+
import { user_defaults } from './storage.js';
|
|
4
|
+
import { nowMs } from './utils.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Retry bookkeeping: `attempt`, `firstattempttime` and `lst`.
|
|
8
|
+
*
|
|
9
|
+
* Every url that carries data declares all three; app/addversion and getsdkconfig declare none,
|
|
10
|
+
* because they are start-up calls whose answers the SDK needs rather than payloads it is pushing.
|
|
11
|
+
*
|
|
12
|
+
* What they are for is the case this SDK cannot see on its own. A send goes out, the server
|
|
13
|
+
* stores it, and the answer is lost on the way back — a closed tab, a dropped connection, a
|
|
14
|
+
* proxy that times out. The SDK has no way to tell that from a send that never arrived, so it
|
|
15
|
+
* sends again. `attempt` and `firstattempttime` are what let the server recognise the second
|
|
16
|
+
* copy as the same payload rather than storing it twice. Without them, every retry is a
|
|
17
|
+
* duplicate session.
|
|
18
|
+
*
|
|
19
|
+
* `attempt` is declared `regex="[1-5]"`, so five is not a policy choice here — it is the largest
|
|
20
|
+
* number the proxy will accept. A batch that has not landed in five passes is dropped, because
|
|
21
|
+
* the sixth would be refused for the param rather than for the payload, which would look like a
|
|
22
|
+
* different fault entirely.
|
|
23
|
+
*
|
|
24
|
+
* State is per path and survives a reload, which is the point: the tab that was closed mid-send
|
|
25
|
+
* is exactly the one that needs to say "attempt 2" when it comes back.
|
|
26
|
+
*/
|
|
27
|
+
/** The largest value `attempt` may carry, from its own regex. */
|
|
28
|
+
const MAX_ATTEMPTS = 5;
|
|
29
|
+
const STORE_KEY = 'sendstate'; // no i18n
|
|
30
|
+
function allState() {
|
|
31
|
+
var saved = user_defaults.item(STORE_KEY);
|
|
32
|
+
return saved && typeof saved === 'object' ? saved : {};
|
|
33
|
+
}
|
|
34
|
+
function stateOf(path) {
|
|
35
|
+
var state = allState()[path];
|
|
36
|
+
return state && typeof state === 'object'
|
|
37
|
+
? { attempt: Number(state.attempt) || 1, firstattempttime: Number(state.firstattempttime) || 0, lst: Number(state.lst) || 0 }
|
|
38
|
+
: { attempt: 1, firstattempttime: 0, lst: 0 };
|
|
39
|
+
}
|
|
40
|
+
function save(path, state) {
|
|
41
|
+
var all = allState();
|
|
42
|
+
all[path] = state;
|
|
43
|
+
user_defaults.add(STORE_KEY, all);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Whether this path has already used its five attempts, so the batch should be dropped rather
|
|
47
|
+
* than sent a sixth time with a param the proxy will refuse.
|
|
48
|
+
*/
|
|
49
|
+
function attemptsExhausted(path) {
|
|
50
|
+
return stateOf(path).attempt > MAX_ATTEMPTS;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Records that a send is going out, and returns the params to put on it.
|
|
54
|
+
*
|
|
55
|
+
* Call it once per request, immediately before sending: it is what moves the attempt counter on,
|
|
56
|
+
* so calling it twice for one request would skip a number and reach five early.
|
|
57
|
+
*/
|
|
58
|
+
function beginAttempt(path) {
|
|
59
|
+
if (!RETRY_CAPABLE_PATHS[path]) {
|
|
60
|
+
return {};
|
|
61
|
+
}
|
|
62
|
+
var state = stateOf(path);
|
|
63
|
+
var at = nowMs();
|
|
64
|
+
if (state.attempt <= 1 || state.firstattempttime === 0) {
|
|
65
|
+
state.attempt = 1;
|
|
66
|
+
state.firstattempttime = at;
|
|
67
|
+
}
|
|
68
|
+
var params = {
|
|
69
|
+
attempt: String(Math.min(state.attempt, MAX_ATTEMPTS)),
|
|
70
|
+
firstattempttime: state.firstattempttime,
|
|
71
|
+
};
|
|
72
|
+
// lst is the last send this path had accepted. There is none before the first one lands, and
|
|
73
|
+
// an optional param is better absent than present and meaningless
|
|
74
|
+
if (state.lst > 0) {
|
|
75
|
+
params.lst = state.lst; // no i18n
|
|
76
|
+
}
|
|
77
|
+
// the next send for this path is the next attempt, unless this one is confirmed first
|
|
78
|
+
save(path, { attempt: state.attempt + 1, firstattempttime: state.firstattempttime, lst: state.lst });
|
|
79
|
+
return params;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Records that a send was accepted: the counter goes back to 1 and `lst` moves to now.
|
|
83
|
+
*
|
|
84
|
+
* For a page-close send the confirmation arrives after the caller has moved on, which is fine —
|
|
85
|
+
* nothing waits for this, it only decides what the *next* send says about itself.
|
|
86
|
+
*/
|
|
87
|
+
function attemptSucceeded(path) {
|
|
88
|
+
if (!RETRY_CAPABLE_PATHS[path]) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
save(path, { attempt: 1, firstattempttime: 0, lst: nowMs() });
|
|
92
|
+
}
|
|
93
|
+
/** Gives up on the current run of attempts, so the next send starts again at 1. */
|
|
94
|
+
function resetAttempts(path) {
|
|
95
|
+
if (!RETRY_CAPABLE_PATHS[path]) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
var state = stateOf(path);
|
|
99
|
+
save(path, { attempt: 1, firstattempttime: 0, lst: state.lst });
|
|
100
|
+
}
|
|
101
|
+
/** Forgets every path's bookkeeping. reset() calls it. */
|
|
102
|
+
function clearAttempts() {
|
|
103
|
+
user_defaults.add(STORE_KEY, undefined);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { MAX_ATTEMPTS, attemptSucceeded, attemptsExhausted, beginAttempt, clearAttempts, resetAttempts };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Queue } from './storage.js';
|
|
2
|
+
import { type ApiPath } from './transport.js';
|
|
3
|
+
import type { Dict, QueueItem, ServerResult } from '../types.js';
|
|
4
|
+
/** Retries `send` in 500 ms, replacing any retry already scheduled. */
|
|
5
|
+
export declare function defer(send: () => Promise<void>): void;
|
|
6
|
+
export declare function clearDefer(): void;
|
|
7
|
+
/** Only one tab sends a queue at a time. Without the Web Locks API, runs directly (1.x behaviour). */
|
|
8
|
+
export declare function withSendLock(name: string, run: () => Promise<void>): Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* The batch to send: closed items within batch_request_timeout days.
|
|
11
|
+
* Expired items are removed from the queue without being sent.
|
|
12
|
+
*/
|
|
13
|
+
export declare function freshItems(queue: Queue, isClosed: (item: QueueItem) => boolean, timeOf: (item: QueueItem) => number): Promise<QueueItem[]>;
|
|
14
|
+
/**
|
|
15
|
+
* Sends one batch of queued items. Resolves only when the server has accepted it, so the caller
|
|
16
|
+
* removes the items then and not before: a batch that fails stays queued and goes again on the
|
|
17
|
+
* next pass, as attempt 2, which `attempt` and `firstattempttime` let the server recognise as a
|
|
18
|
+
* copy rather than store twice.
|
|
19
|
+
*
|
|
20
|
+
* With `forget` (the page is closing) it goes by keepalive, and the promise settles if the answer
|
|
21
|
+
* arrives before the page is gone. See postKeepalive.
|
|
22
|
+
*
|
|
23
|
+
* Five attempts is the cap, because `attempt` is declared regex="[1-5]" and a sixth would be
|
|
24
|
+
* refused for the param rather than for the payload.
|
|
25
|
+
*/
|
|
26
|
+
export declare function sendBatch(path: ApiPath, label: string, _payload: Dict, forget?: boolean): Promise<ServerResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Hands a send's outcome back to a sender. Normally the sender waits for it. While the page
|
|
29
|
+
* closes it does not: the request is already out, and waiting would hold the next queue's send
|
|
30
|
+
* until after the page has gone. The outcome is still handled whenever it arrives.
|
|
31
|
+
*/
|
|
32
|
+
export declare function awaitUnlessClosing(landed: Promise<unknown>, forget: boolean | undefined, label: string): Promise<void> | undefined;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { _ZAP, isExpired } from './state.js';
|
|
3
|
+
import { Console, warnAlways } from './logger.js';
|
|
4
|
+
import { isEmptyObj, sizeof } from './utils.js';
|
|
5
|
+
import './constants.js';
|
|
6
|
+
import './db.js';
|
|
7
|
+
import { shouldTrackOrSendData } from './consent.js';
|
|
8
|
+
import { attemptsExhausted, MAX_ATTEMPTS, resetAttempts } from './attempts.js';
|
|
9
|
+
import { rejectIfDisabled, rejectIfNoDeviceIds, getMetaObj, postKeepalive, apiHeaders, postToApptics } from './transport.js';
|
|
10
|
+
|
|
11
|
+
/** Retries `send` in 500 ms, replacing any retry already scheduled. */
|
|
12
|
+
function defer(send) {
|
|
13
|
+
if (_ZAP._deferSendTimer) {
|
|
14
|
+
clearTimeout(_ZAP._deferSendTimer);
|
|
15
|
+
}
|
|
16
|
+
_ZAP._deferSendTimer = setTimeout(send, 500);
|
|
17
|
+
}
|
|
18
|
+
function clearDefer() {
|
|
19
|
+
if (_ZAP._deferSendTimer) {
|
|
20
|
+
clearTimeout(_ZAP._deferSendTimer);
|
|
21
|
+
_ZAP._deferSendTimer = null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Only one tab sends a queue at a time. Without the Web Locks API, runs directly (1.x behaviour). */
|
|
25
|
+
function withSendLock(name, run) {
|
|
26
|
+
var locks = typeof navigator !== 'undefined' ? navigator.locks : undefined; // no i18n
|
|
27
|
+
if (!locks || typeof locks.request !== 'function') { // no i18n
|
|
28
|
+
return run();
|
|
29
|
+
}
|
|
30
|
+
return locks.request('apptics-send-' + name, { ifAvailable: true }, function (lock) {
|
|
31
|
+
return lock ? run() : undefined;
|
|
32
|
+
}).then(function () { return undefined; });
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The batch to send: closed items within batch_request_timeout days.
|
|
36
|
+
* Expired items are removed from the queue without being sent.
|
|
37
|
+
*/
|
|
38
|
+
function freshItems(queue, isClosed, timeOf) {
|
|
39
|
+
return queue.payload().then(function (items) {
|
|
40
|
+
var fresh = [];
|
|
41
|
+
var expired = [];
|
|
42
|
+
items.forEach(function (item) {
|
|
43
|
+
if (!isClosed(item)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
(isExpired(timeOf(item)) ? expired : fresh).push(item);
|
|
47
|
+
});
|
|
48
|
+
return expired.length ? queue.remove(expired).then(function () { return fresh; }) : fresh;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Sends one batch of queued items. Resolves only when the server has accepted it, so the caller
|
|
53
|
+
* removes the items then and not before: a batch that fails stays queued and goes again on the
|
|
54
|
+
* next pass, as attempt 2, which `attempt` and `firstattempttime` let the server recognise as a
|
|
55
|
+
* copy rather than store twice.
|
|
56
|
+
*
|
|
57
|
+
* With `forget` (the page is closing) it goes by keepalive, and the promise settles if the answer
|
|
58
|
+
* arrives before the page is gone. See postKeepalive.
|
|
59
|
+
*
|
|
60
|
+
* Five attempts is the cap, because `attempt` is declared regex="[1-5]" and a sixth would be
|
|
61
|
+
* refused for the param rather than for the payload.
|
|
62
|
+
*/
|
|
63
|
+
function sendBatch(path, label, _payload, forget) {
|
|
64
|
+
var blocked = rejectIfDisabled(label);
|
|
65
|
+
if (blocked) {
|
|
66
|
+
return blocked;
|
|
67
|
+
}
|
|
68
|
+
if (!shouldTrackOrSendData()) {
|
|
69
|
+
return Promise.reject(label + ': tracking is off'); // no i18n
|
|
70
|
+
}
|
|
71
|
+
if (isEmptyObj(_payload)) {
|
|
72
|
+
return Promise.reject(label + ': nothing to send'); // no i18n
|
|
73
|
+
}
|
|
74
|
+
var missing = rejectIfNoDeviceIds(label);
|
|
75
|
+
if (missing) {
|
|
76
|
+
return missing;
|
|
77
|
+
}
|
|
78
|
+
if (!_payload.hasOwnProperty('meta')) { // no i18n
|
|
79
|
+
_payload.meta = getMetaObj();
|
|
80
|
+
}
|
|
81
|
+
if (attemptsExhausted(path)) {
|
|
82
|
+
// five passes without a confirmed landing. Keeping it would block every later batch
|
|
83
|
+
// behind a payload the proxy will now refuse for its attempt number alone
|
|
84
|
+
warnAlways('apptics: dropping a ' + label + ' batch after ' + MAX_ATTEMPTS + ' attempts'); // no i18n
|
|
85
|
+
resetAttempts(path);
|
|
86
|
+
return Promise.resolve({ result: 'success' }); // no i18n
|
|
87
|
+
}
|
|
88
|
+
Console.log(label + ': Payload size : ' + (sizeof(_payload) / 1024)); // no i18n
|
|
89
|
+
var body = JSON.stringify(_payload);
|
|
90
|
+
return forget
|
|
91
|
+
? postKeepalive(path, body, apiHeaders(), label)
|
|
92
|
+
: postToApptics(path, body, apiHeaders(), label);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Hands a send's outcome back to a sender. Normally the sender waits for it. While the page
|
|
96
|
+
* closes it does not: the request is already out, and waiting would hold the next queue's send
|
|
97
|
+
* until after the page has gone. The outcome is still handled whenever it arrives.
|
|
98
|
+
*/
|
|
99
|
+
function awaitUnlessClosing(landed, forget, label) {
|
|
100
|
+
if (!forget) {
|
|
101
|
+
return landed.then(function () { return undefined; });
|
|
102
|
+
}
|
|
103
|
+
landed.catch(function (err) {
|
|
104
|
+
Console.log(label + ': not confirmed before the page closed, kept for the next send: ' + String(err)); // no i18n
|
|
105
|
+
});
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export { awaitUnlessClosing, clearDefer, defer, freshItems, sendBatch, withSendLock };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a log line was raised, for logSDK's `filename` and `linenumber`.
|
|
3
|
+
*
|
|
4
|
+
* There is no caller introspection in a browser, so the only way to answer "which line was this"
|
|
5
|
+
* is to throw an Error nobody catches and read its stack. That is cheap enough for a channel that
|
|
6
|
+
* only carries the SDK's own failures, but it is never on the app's path: everything here is
|
|
7
|
+
* wrapped so a stack we cannot parse costs a line its call site and nothing more.
|
|
8
|
+
*/
|
|
9
|
+
export interface CallSite {
|
|
10
|
+
/** The script's basename, not its url: logSDK caps filename at 100 characters. */
|
|
11
|
+
filename: string;
|
|
12
|
+
/** 0 when the stack gave us nothing — see entryFor() for why it is still sent. */
|
|
13
|
+
linenumber: number;
|
|
14
|
+
/** The calling function's name, when the frame has one. Anonymous callers have none. */
|
|
15
|
+
fn?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const NO_CALL_SITE: CallSite;
|
|
18
|
+
/**
|
|
19
|
+
* A call typed in the browser's developer console: there is no file, line or function to report,
|
|
20
|
+
* only the console's own placeholder for its input, so the line says plainly that it came from
|
|
21
|
+
* nowhere in the app. file_name and the function field both accept `-`.
|
|
22
|
+
*/
|
|
23
|
+
export declare const CONSOLE_CALL_SITE: CallSite;
|
|
24
|
+
/**
|
|
25
|
+
* The frame `skip` frames down a stack, counting only the ones that name a file — V8 heads its
|
|
26
|
+
* stacks with an `Error` line and Firefox does not, so counting raw lines would read a different
|
|
27
|
+
* frame in each browser.
|
|
28
|
+
*/
|
|
29
|
+
export declare function callSiteFromStack(stack: string | undefined, skip: number): CallSite;
|
|
30
|
+
/**
|
|
31
|
+
* Where the caller `skip` levels above this call sits. `skip` is 0 for this function's own
|
|
32
|
+
* caller; each wrapper between that caller and the place worth reporting adds one.
|
|
33
|
+
*/
|
|
34
|
+
export declare function callSite(skip: number): CallSite;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
/**
|
|
3
|
+
* Where a log line was raised, for logSDK's `filename` and `linenumber`.
|
|
4
|
+
*
|
|
5
|
+
* There is no caller introspection in a browser, so the only way to answer "which line was this"
|
|
6
|
+
* is to throw an Error nobody catches and read its stack. That is cheap enough for a channel that
|
|
7
|
+
* only carries the SDK's own failures, but it is never on the app's path: everything here is
|
|
8
|
+
* wrapped so a stack we cannot parse costs a line its call site and nothing more.
|
|
9
|
+
*/
|
|
10
|
+
const NO_CALL_SITE = { filename: '', linenumber: 0, fn: '' };
|
|
11
|
+
/**
|
|
12
|
+
* A call typed in the browser's developer console: there is no file, line or function to report,
|
|
13
|
+
* only the console's own placeholder for its input, so the line says plainly that it came from
|
|
14
|
+
* nowhere in the app. file_name and the function field both accept `-`.
|
|
15
|
+
*/
|
|
16
|
+
const CONSOLE_CALL_SITE = { filename: '-', linenumber: 0, fn: '-' };
|
|
17
|
+
/**
|
|
18
|
+
* The frame of code run from the developer console, in each browser:
|
|
19
|
+
* V8 (Chrome, Edge): ` at <anonymous>:1:15` (the file is <anonymous>)
|
|
20
|
+
* Firefox: `@debugger eval code:1:15`
|
|
21
|
+
* Safari: `global code` or `eval code`, with no location at all
|
|
22
|
+
* V8 names a string run through eval or new Function <anonymous> too; it is no file either.
|
|
23
|
+
*/
|
|
24
|
+
const CONSOLE_FILE_V8 = '<anonymous>'; // no i18n
|
|
25
|
+
const CONSOLE_FIREFOX = /debugger eval code/; // no i18n
|
|
26
|
+
const CONSOLE_SAFARI = /^(global|eval) code(@|$)/; // no i18n
|
|
27
|
+
/** logSDK declares max-len="100" on filename. */
|
|
28
|
+
const FILENAME_MAX = 100;
|
|
29
|
+
/**
|
|
30
|
+
* The file and line out of one stack frame, in either shape a browser produces:
|
|
31
|
+
* V8: ` at doThing (https://host/app.js:12:34)`
|
|
32
|
+
* Firefox/Safari: `doThing@https://host/app.js:12:34`
|
|
33
|
+
* Both end in `:line:column`, which is the only part that is the same everywhere, so the match is
|
|
34
|
+
* anchored there and the column is read only so that it can be thrown away — without it the
|
|
35
|
+
* pattern would take the column for the line.
|
|
36
|
+
*/
|
|
37
|
+
const FRAME = /([^\s(@]+):(\d+):(\d+)\)?$/;
|
|
38
|
+
/** `https://host/path/app.js?v=2` -> `app.js`. The url would not fit in 100 characters. */
|
|
39
|
+
function basename(url) {
|
|
40
|
+
var path = url.split(/[?#]/)[0] || url;
|
|
41
|
+
var parts = path.split('/');
|
|
42
|
+
return (parts[parts.length - 1] || path).slice(0, FILENAME_MAX);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The function a frame names, in either shape:
|
|
46
|
+
* V8: `at doThing (` — or `at async doThing (`
|
|
47
|
+
* Firefox/Safari: `doThing@`
|
|
48
|
+
* A frame without one (`at https://…` or `@https://…`) is an anonymous caller, and gets ''.
|
|
49
|
+
*/
|
|
50
|
+
const FRAME_FN_V8 = /^at\s+(?:async\s+)?(.+?)\s+\(/;
|
|
51
|
+
const FRAME_FN_OTHER = /^([^@\s]+)@/;
|
|
52
|
+
function frameFunction(frame) {
|
|
53
|
+
var match = FRAME_FN_V8.exec(frame) || FRAME_FN_OTHER.exec(frame);
|
|
54
|
+
return match ? (match[1] || '') : ''; // no i18n
|
|
55
|
+
}
|
|
56
|
+
function parseFrame(line) {
|
|
57
|
+
var trimmed = line.trim();
|
|
58
|
+
var match = FRAME.exec(trimmed);
|
|
59
|
+
if (match === null) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
var line_no = Number(match[2]);
|
|
63
|
+
if (!isFinite(line_no)) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return { filename: basename(match[1] || ''), linenumber: line_no, fn: frameFunction(trimmed) };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The frame `skip` frames down a stack, counting only the ones that name a file — V8 heads its
|
|
70
|
+
* stacks with an `Error` line and Firefox does not, so counting raw lines would read a different
|
|
71
|
+
* frame in each browser.
|
|
72
|
+
*/
|
|
73
|
+
function callSiteFromStack(stack, skip) {
|
|
74
|
+
if (!stack) {
|
|
75
|
+
return NO_CALL_SITE;
|
|
76
|
+
}
|
|
77
|
+
var lines = stack.split('\n');
|
|
78
|
+
var seen = 0;
|
|
79
|
+
for (var i = 0; i < lines.length; i++) {
|
|
80
|
+
var raw = (lines[i] || '').trim(); // no i18n
|
|
81
|
+
var frame = parseFrame(raw);
|
|
82
|
+
if (frame === null) {
|
|
83
|
+
// Safari's console frame has no location, so it is never a frame parseFrame reads
|
|
84
|
+
if (seen === skip && CONSOLE_SAFARI.test(raw)) {
|
|
85
|
+
return CONSOLE_CALL_SITE;
|
|
86
|
+
}
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (seen++ < skip) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
return frame.filename === CONSOLE_FILE_V8 || CONSOLE_FIREFOX.test(raw) ? CONSOLE_CALL_SITE : frame;
|
|
93
|
+
}
|
|
94
|
+
return NO_CALL_SITE;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Where the caller `skip` levels above this call sits. `skip` is 0 for this function's own
|
|
98
|
+
* caller; each wrapper between that caller and the place worth reporting adds one.
|
|
99
|
+
*/
|
|
100
|
+
function callSite(skip) {
|
|
101
|
+
try {
|
|
102
|
+
// +1 for this function's own frame, which is never the answer
|
|
103
|
+
return callSiteFromStack(new Error().stack, skip + 1);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
return NO_CALL_SITE;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export { CONSOLE_CALL_SITE, NO_CALL_SITE, callSite, callSiteFromStack };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TrackingStatus } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* DNT counts only when not ignored; while it counts, tracking is NO_TRACKING (opt_in_status).
|
|
4
|
+
* Decided on every visit and never stored: storing it made one visit with DNT on look like the
|
|
5
|
+
* visitor's own choice, so tracking stayed off after DNT was turned off or ignore_do_not_track set.
|
|
6
|
+
*/
|
|
7
|
+
export declare function hasDoNotTrackEnabled(): boolean;
|
|
8
|
+
export declare function opt_in_status(): TrackingStatus;
|
|
9
|
+
export declare function shouldTrackOrSendData(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The guard every tracking call starts with: init() has run, the client isn't a crawler,
|
|
12
|
+
* and consent / server flags allow tracking.
|
|
13
|
+
*/
|
|
14
|
+
export declare function canTrack(caller: string): boolean;
|
|
15
|
+
/** canTrack plus the server's `engagementtracking` flag from getupdates (events, screens, sessions, api calls). */
|
|
16
|
+
export declare function canTrackEngagement(caller: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* canTrack plus the server's heatmap flag (getsdkconfig module 18).
|
|
19
|
+
*
|
|
20
|
+
* Heatmap rode the engagement flag until v2 gave it one of its own, which matters: an app may
|
|
21
|
+
* well want events while declining to have every click's coordinates recorded.
|
|
22
|
+
*/
|
|
23
|
+
export declare function canTrackHeatmap(caller: string): boolean;
|
|
24
|
+
/** canTrack plus the server's `errortracking` flag from getupdates. */
|
|
25
|
+
export declare function canTrackErrors(caller: string): boolean;
|
|
26
|
+
export declare function setTrackingStatus(trackingState: TrackingStatus): void;
|
|
27
|
+
export declare function getTrackingStatus(): TrackingStatus;
|