@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,199 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { warnAlways } from '../core/logger.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Which API calls are recorded, and under what name: Android's AppticsApiTracker.configure, built
|
|
6
|
+
* with ApiTrackingBuilder the way user properties are built with UserBuilder:
|
|
7
|
+
*
|
|
8
|
+
* configureApiTracking(new ApiTrackingBuilder()
|
|
9
|
+
* .allowRegex(/^https:\/\/api\.zylker\.(com|in)\//)
|
|
10
|
+
* .ignoreEquals('https://api.zylker.com/health')
|
|
11
|
+
* .ignoreRegex(/\/internal\//)
|
|
12
|
+
* .build());
|
|
13
|
+
*
|
|
14
|
+
* Four rules, two kinds by two: an exact URL (equals) or a regular expression (regex), to allow or
|
|
15
|
+
* to ignore. Each is tried on the call's URL as `scheme://host[:port]/path`, with its query and
|
|
16
|
+
* fragment removed, so one rule can name a host, a path or a single endpoint. Ignore rules are
|
|
17
|
+
* tried first and always win; then, when there are allow rules, a call must match one of them.
|
|
18
|
+
*
|
|
19
|
+
* The name a call is recorded under is not configured: the query goes, and numeric ids, UUIDs and
|
|
20
|
+
* JWTs in its path become `*`, so `/users/42/orders` and `/users/43/orders` are one endpoint.
|
|
21
|
+
*
|
|
22
|
+
* Both apply to every call, the ones the fetch/XHR wrappers catch and the ones the app reports
|
|
23
|
+
* with startTrackApi(), so one set of rules decides what the API page shows.
|
|
24
|
+
*/
|
|
25
|
+
function emptyConfig() {
|
|
26
|
+
return { allowEquals: [], ignoreEquals: [], allowRegex: [], ignoreRegex: [] };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A URL as the rules compare it: `scheme://host[:port]/path`, the scheme and host lower-cased,
|
|
30
|
+
* the default port dropped, the query, fragment and a trailing slash removed. '' when it is not
|
|
31
|
+
* an absolute http(s) URL.
|
|
32
|
+
*/
|
|
33
|
+
function ruleUrl(raw) {
|
|
34
|
+
try {
|
|
35
|
+
var url = new URL(String(raw).trim());
|
|
36
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') { // no i18n
|
|
37
|
+
return ''; // no i18n
|
|
38
|
+
}
|
|
39
|
+
var path = url.pathname.length > 1 ? url.pathname.replace(/\/+$/, '') : url.pathname; // no i18n
|
|
40
|
+
return url.protocol + '//' + url.host.toLowerCase() + path; // no i18n
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
return ''; // no i18n
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** The exact URLs given, as rules compare them; anything that is not an absolute URL is refused, and said. */
|
|
47
|
+
function cleanUrls(method, values) {
|
|
48
|
+
var out = [];
|
|
49
|
+
values.forEach(function (value) {
|
|
50
|
+
var url = typeof value === 'string' ? ruleUrl(value) : ''; // no i18n
|
|
51
|
+
if (url === '') { // no i18n
|
|
52
|
+
warnAlways('ApiTrackingBuilder.' + method + ': ' + JSON.stringify(value) + ' is not a full http(s) URL, as https://api.example.com/health, and is left out'); // no i18n
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (out.indexOf(url) === -1) {
|
|
56
|
+
out.push(url);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
/** The regular expressions given, a RegExp or its source as a string; an invalid one is refused, and said. */
|
|
62
|
+
function cleanRegexes(method, values) {
|
|
63
|
+
var out = [];
|
|
64
|
+
values.forEach(function (value) {
|
|
65
|
+
if (value instanceof RegExp) {
|
|
66
|
+
out.push(value);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (typeof value === 'string' && value !== '') { // no i18n
|
|
70
|
+
try {
|
|
71
|
+
out.push(new RegExp(value));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
// falls through to the warning
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
warnAlways('ApiTrackingBuilder.' + method + ': ' + String(value) + ' is not a regular expression, and is left out'); // no i18n
|
|
79
|
+
});
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Builds the rules configure takes. Each method may be called more than once, and takes any number
|
|
84
|
+
* of values: `new ApiTrackingBuilder().allowEquals(a, b).ignoreRegex(/x/).build()`.
|
|
85
|
+
*/
|
|
86
|
+
class ApiTrackingBuilder {
|
|
87
|
+
constructor() {
|
|
88
|
+
this.rules = emptyConfig();
|
|
89
|
+
}
|
|
90
|
+
/** Records only these URLs (and those any allowRegex matches): `https://api.example.com/orders`. */
|
|
91
|
+
allowEquals(...urls) { this.rules.allowEquals.push(...cleanUrls('allowEquals', urls)); return this; }
|
|
92
|
+
/** Never records these URLs: `https://api.example.com/health`. */
|
|
93
|
+
ignoreEquals(...urls) { this.rules.ignoreEquals.push(...cleanUrls('ignoreEquals', urls)); return this; }
|
|
94
|
+
/** Records only URLs these match (and those any allowEquals names): `/^https:\/\/api\.example\.com\//`. */
|
|
95
|
+
allowRegex(...patterns) { this.rules.allowRegex.push(...cleanRegexes('allowRegex', patterns)); return this; }
|
|
96
|
+
/** Never records URLs these match: `/\/internal\//`. */
|
|
97
|
+
ignoreRegex(...patterns) { this.rules.ignoreRegex.push(...cleanRegexes('ignoreRegex', patterns)); return this; }
|
|
98
|
+
build() {
|
|
99
|
+
return {
|
|
100
|
+
allowEquals: this.rules.allowEquals.slice(),
|
|
101
|
+
ignoreEquals: this.rules.ignoreEquals.slice(),
|
|
102
|
+
allowRegex: this.rules.allowRegex.slice(),
|
|
103
|
+
ignoreRegex: this.rules.ignoreRegex.slice(),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 2.0.0-beta's builder methods, now gone. Called, each throws a TypeError that names what to use
|
|
109
|
+
* instead: a rule silently missing would record what the app meant to leave out.
|
|
110
|
+
*/
|
|
111
|
+
const RETIRED_METHODS = Object.freeze({
|
|
112
|
+
ignoreDomains: 'ignoreRegex', // no i18n
|
|
113
|
+
allowOnlyDomains: 'allowRegex', // no i18n
|
|
114
|
+
allowOnlyTLDs: 'allowRegex', // no i18n
|
|
115
|
+
ignoreTLDs: 'ignoreRegex', // no i18n
|
|
116
|
+
ignoreDomainPrefixes: 'ignoreRegex', // no i18n
|
|
117
|
+
ignoreDomainSuffixes: 'ignoreRegex', // no i18n
|
|
118
|
+
groupDomains: '', // no i18n
|
|
119
|
+
ignoreEndpoint: 'ignoreEquals or ignoreRegex', // no i18n
|
|
120
|
+
addPattern: '', // no i18n
|
|
121
|
+
preserveSegments: '', // no i18n
|
|
122
|
+
autoDetection: '', // no i18n
|
|
123
|
+
});
|
|
124
|
+
Object.keys(RETIRED_METHODS).forEach(function (name) {
|
|
125
|
+
Object.defineProperty(ApiTrackingBuilder.prototype, name, {
|
|
126
|
+
configurable: true,
|
|
127
|
+
value: function () {
|
|
128
|
+
var instead = RETIRED_METHODS[name];
|
|
129
|
+
throw new TypeError('ApiTrackingBuilder.' + name + '() is no longer supported: ' // no i18n
|
|
130
|
+
+ (instead ? 'use ' + instead + ', which match the URL without its query' : 'ids, UUIDs and tokens in a path become * on their own')); // no i18n
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
var current = emptyConfig();
|
|
135
|
+
/** The values of `list` that are of the kind `keep` accepts. */
|
|
136
|
+
function listOf(value, keep) {
|
|
137
|
+
return Array.isArray(value) ? value.filter(keep) : [];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Applies the rules, replacing any given before. Takes what build() returned, or the builder
|
|
141
|
+
* itself. Call it once, before the requests it should cover; it works before init().
|
|
142
|
+
*/
|
|
143
|
+
function configureApiTracking(config) {
|
|
144
|
+
var built = (config instanceof ApiTrackingBuilder ? config.build() : config);
|
|
145
|
+
if (!built || typeof built !== 'object') { // no i18n
|
|
146
|
+
current = emptyConfig();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
var isString = function (one) { return typeof one === 'string'; }; // no i18n
|
|
150
|
+
var isRegExp = function (one) { return one instanceof RegExp; };
|
|
151
|
+
current = {
|
|
152
|
+
// a hand-written object gets the same clean-up the builder gives
|
|
153
|
+
allowEquals: cleanUrls('allowEquals', listOf(built.allowEquals, isString)), // no i18n
|
|
154
|
+
ignoreEquals: cleanUrls('ignoreEquals', listOf(built.ignoreEquals, isString)), // no i18n
|
|
155
|
+
allowRegex: listOf(built.allowRegex, isRegExp).concat(cleanRegexes('allowRegex', listOf(built.allowRegex, isString))), // no i18n
|
|
156
|
+
ignoreRegex: listOf(built.ignoreRegex, isRegExp).concat(cleanRegexes('ignoreRegex', listOf(built.ignoreRegex, isString))), // no i18n
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/** Whether a regular expression matches, whatever its flags: a /g one keeps a position between tests. */
|
|
160
|
+
function regexMatches(re, url) {
|
|
161
|
+
re.lastIndex = 0;
|
|
162
|
+
return re.test(url);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Whether a call to `url` is recorded. Ignore rules are tried first and always win; then, when
|
|
166
|
+
* there are allow rules, the URL must match one. A URL that cannot be read is not recorded.
|
|
167
|
+
*/
|
|
168
|
+
function urlTracked(url) {
|
|
169
|
+
var target = ruleUrl(url);
|
|
170
|
+
if (target === '') { // no i18n
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (current.ignoreEquals.indexOf(target) !== -1 || current.ignoreRegex.some(function (re) { return regexMatches(re, target); })) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (current.allowEquals.length === 0 && current.allowRegex.length === 0) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
return current.allowEquals.indexOf(target) !== -1 || current.allowRegex.some(function (re) { return regexMatches(re, target); });
|
|
180
|
+
}
|
|
181
|
+
/* ---------------- the name a call is recorded under ---------------- */
|
|
182
|
+
/** The path without its query string or fragment, which is where ids and tokens travel. */
|
|
183
|
+
function withoutQuery(path) {
|
|
184
|
+
var cut = String(path || '').split(/[?#]/)[0]; // no i18n
|
|
185
|
+
return cut === '' ? '/' : cut; // no i18n
|
|
186
|
+
}
|
|
187
|
+
const NUMERIC = /^\d+$/;
|
|
188
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
189
|
+
// three base64url parts, the first starting eyJ — the encoding of '{"' every JWT header opens
|
|
190
|
+
// with. Without that start, a file name such as jquery.min.js would pass for a token
|
|
191
|
+
const JWT = /^eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
192
|
+
/** The path a call is recorded under: the query goes, and numeric ids, UUIDs and JWTs become `*`. */
|
|
193
|
+
function normalizeApiPath(path) {
|
|
194
|
+
return withoutQuery(path).split('/').map(function (segment) {
|
|
195
|
+
return segment !== '' && (NUMERIC.test(segment) || UUID.test(segment) || JWT.test(segment)) ? '*' : segment; // no i18n
|
|
196
|
+
}).join('/'); // no i18n
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export { ApiTrackingBuilder, configureApiTracking, normalizeApiPath, ruleUrl, urlTracked, withoutQuery };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Starts or stops automatic tracking. Safe to call repeatedly: installing twice would report each
|
|
3
|
+
* call twice, so the second call is a no-op.
|
|
4
|
+
*/
|
|
5
|
+
export declare function enableApiTracking(enabled?: boolean): void;
|
|
6
|
+
/** Whether the wrappers are currently in place. */
|
|
7
|
+
export declare function isApiTrackingEnabled(): boolean;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { _ZAP, getConfig } from '../core/state.js';
|
|
3
|
+
import { Console } from '../core/logger.js';
|
|
4
|
+
import { nowMs, isEmpty } from '../core/utils.js';
|
|
5
|
+
import { trackApi } from './track.js';
|
|
6
|
+
import { urlTracked } from './api-rules.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Automatic API tracking: wraps XMLHttpRequest and fetch so calls the app makes are recorded
|
|
10
|
+
* without it calling startTrackApi() / endTrackApi() itself.
|
|
11
|
+
*
|
|
12
|
+
* Two rules shape everything here. The wrappers must be invisible — return values, `this`, thrown
|
|
13
|
+
* errors and argument forms all pass straight through, because a mistake in this file breaks the
|
|
14
|
+
* host app's networking rather than just losing a metric. And the SDK's own traffic must never be
|
|
15
|
+
* recorded: engagement/add reports API calls, so tracking it would have each batch produce the
|
|
16
|
+
* items for the next one.
|
|
17
|
+
*
|
|
18
|
+
* Off unless `track_api` is set. Hostnames and paths of every request an app makes is a wider
|
|
19
|
+
* grant than events and screens, so it is the app's decision, not a default.
|
|
20
|
+
*/
|
|
21
|
+
var installed = false;
|
|
22
|
+
var originalOpen = null;
|
|
23
|
+
var originalSend = null;
|
|
24
|
+
var originalFetch = null;
|
|
25
|
+
/**
|
|
26
|
+
* Whether a request is the SDK's own, which is never reported: engagement/add carries API items, so
|
|
27
|
+
* recording it would have every batch produce the items for the next one, forever.
|
|
28
|
+
*
|
|
29
|
+
* Two checks, because one is not enough to bet a loop on. The host comparison is a prefix match, so
|
|
30
|
+
* it misses if the host is spelled differently in the request than in serviceUrl — a trailing
|
|
31
|
+
* slash is enough. The path check catches those, since every SDK call sits under the configured
|
|
32
|
+
* endpoint whatever host it goes to, including through a same-origin proxy.
|
|
33
|
+
*/
|
|
34
|
+
function isOwnRequest(url, pathname) {
|
|
35
|
+
var own = [_ZAP._defaultoptions.base_domain, _ZAP._defaultoptions.serviceUrl];
|
|
36
|
+
for (var i = 0; i < own.length; i++) {
|
|
37
|
+
if (!isEmpty(own[i]) && url.indexOf(own[i]) === 0) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
var endpoint = getConfig('endpoint');
|
|
42
|
+
return !isEmpty(endpoint) && pathname.indexOf(endpoint) === 0;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether one URL is ours to report: an http(s) call, not the SDK's own, and one the
|
|
46
|
+
* configureApiTracking rules let through. Anything unparseable is skipped rather than guessed at.
|
|
47
|
+
*/
|
|
48
|
+
function shouldTrack(rawUrl) {
|
|
49
|
+
if (isEmpty(rawUrl)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
var absolute;
|
|
53
|
+
try {
|
|
54
|
+
absolute = new URL(rawUrl, window.location.href);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
if (absolute.protocol !== 'https:' && absolute.protocol !== 'http:') { // no i18n
|
|
60
|
+
return false; // data:, blob:, chrome-extension: and friends
|
|
61
|
+
}
|
|
62
|
+
if (isOwnRequest(absolute.href, absolute.pathname)) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
// configureApiTracking's rules: checked here too so a call left out is not even timed. trackApi checks them again, for the
|
|
66
|
+
// calls an app reports itself
|
|
67
|
+
return urlTracked(absolute.href);
|
|
68
|
+
}
|
|
69
|
+
/** One finished call. Never throws: a tracking failure must not surface in the app's call. */
|
|
70
|
+
function record(method, url, starttime, responsecode, responsemessage) {
|
|
71
|
+
try {
|
|
72
|
+
trackApi({
|
|
73
|
+
url: url,
|
|
74
|
+
method: method,
|
|
75
|
+
responsecode: responsecode,
|
|
76
|
+
responsemessage: responsemessage,
|
|
77
|
+
starttime: starttime,
|
|
78
|
+
endtime: nowMs(),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
Console.log('auto api tracking: ' + String(err)); // no i18n
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** The url of any of fetch's three first-argument forms. */
|
|
86
|
+
function fetchUrl(input) {
|
|
87
|
+
if (typeof input === 'string') { // no i18n
|
|
88
|
+
return input;
|
|
89
|
+
}
|
|
90
|
+
if (typeof Request !== 'undefined' && input instanceof Request) { // no i18n
|
|
91
|
+
return input.url;
|
|
92
|
+
}
|
|
93
|
+
return String(input);
|
|
94
|
+
}
|
|
95
|
+
function fetchMethod(input, init) {
|
|
96
|
+
if (init && init.method) {
|
|
97
|
+
return init.method;
|
|
98
|
+
}
|
|
99
|
+
if (typeof Request !== 'undefined' && input instanceof Request) { // no i18n
|
|
100
|
+
return input.method;
|
|
101
|
+
}
|
|
102
|
+
return 'GET'; // no i18n
|
|
103
|
+
}
|
|
104
|
+
function installXhr() {
|
|
105
|
+
if (typeof XMLHttpRequest !== 'function') { // no i18n
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
originalOpen = XMLHttpRequest.prototype.open;
|
|
109
|
+
originalSend = XMLHttpRequest.prototype.send;
|
|
110
|
+
XMLHttpRequest.prototype.open = function (method, url) {
|
|
111
|
+
try {
|
|
112
|
+
var href = String(url);
|
|
113
|
+
if (shouldTrack(href)) {
|
|
114
|
+
// starttime is set in send(): open() may happen long before the call goes out
|
|
115
|
+
this._ap_api = { method: String(method || 'GET'), url: href, starttime: 0 }; // no i18n
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this._ap_api = undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
this._ap_api = undefined;
|
|
123
|
+
}
|
|
124
|
+
return originalOpen.apply(this, arguments);
|
|
125
|
+
};
|
|
126
|
+
XMLHttpRequest.prototype.send = function () {
|
|
127
|
+
const tracked = this._ap_api;
|
|
128
|
+
if (tracked) {
|
|
129
|
+
tracked.starttime = nowMs();
|
|
130
|
+
// loadend fires for success, error, abort and timeout alike, so one listener covers
|
|
131
|
+
// every ending; status 0 is how a network failure arrives here
|
|
132
|
+
this.addEventListener('loadend', function () {
|
|
133
|
+
var message = this.status === 0 ? 'Network Error' : (this.statusText || '-'); // no i18n
|
|
134
|
+
record(tracked.method, tracked.url, tracked.starttime, this.status, message);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return originalSend.apply(this, arguments);
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function installFetch() {
|
|
141
|
+
if (typeof fetch !== 'function') { // no i18n
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
originalFetch = fetch;
|
|
145
|
+
window.fetch = function (input, init) {
|
|
146
|
+
var url, method, starttime;
|
|
147
|
+
try {
|
|
148
|
+
url = fetchUrl(input);
|
|
149
|
+
method = fetchMethod(input, init);
|
|
150
|
+
starttime = nowMs();
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
return originalFetch(input, init);
|
|
154
|
+
}
|
|
155
|
+
if (!shouldTrack(url)) {
|
|
156
|
+
return originalFetch(input, init);
|
|
157
|
+
}
|
|
158
|
+
return originalFetch(input, init).then(function (response) {
|
|
159
|
+
record(method, url, starttime, response.status, response.statusText || '-'); // no i18n
|
|
160
|
+
return response;
|
|
161
|
+
}, function (err) {
|
|
162
|
+
// a rejected fetch never reached a status; the rejection is re-thrown untouched
|
|
163
|
+
record(method, url, starttime, 0, String(err && err.message ? err.message : err) || 'Network Error'); // no i18n
|
|
164
|
+
throw err;
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Starts or stops automatic tracking. Safe to call repeatedly: installing twice would report each
|
|
170
|
+
* call twice, so the second call is a no-op.
|
|
171
|
+
*/
|
|
172
|
+
function enableApiTracking(enabled) {
|
|
173
|
+
var on = typeof enabled === 'boolean' ? enabled : true; // no i18n
|
|
174
|
+
if (on === installed) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (on) {
|
|
178
|
+
installXhr();
|
|
179
|
+
installFetch();
|
|
180
|
+
installed = true;
|
|
181
|
+
Console.log('automatic api tracking: on'); // no i18n
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (originalOpen) {
|
|
185
|
+
XMLHttpRequest.prototype.open = originalOpen;
|
|
186
|
+
}
|
|
187
|
+
if (originalSend) {
|
|
188
|
+
XMLHttpRequest.prototype.send = originalSend;
|
|
189
|
+
}
|
|
190
|
+
if (originalFetch) {
|
|
191
|
+
window.fetch = originalFetch;
|
|
192
|
+
}
|
|
193
|
+
originalOpen = originalSend = originalFetch = null;
|
|
194
|
+
installed = false;
|
|
195
|
+
Console.log('automatic api tracking: off'); // no i18n
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export { enableApiTracking };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** A path as screen_routes compares it: no query or fragment, no trailing slash but on the root. */
|
|
2
|
+
export declare function routePath(path: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Starts or stops automatic screen tracking. Starting records the screen the page is on, unless
|
|
5
|
+
* the app has already called inScreen() for it. Safe to call repeatedly: wrapping history twice
|
|
6
|
+
* would check every navigation twice, so a second start does nothing. `wait` replaces SETTLE_MS,
|
|
7
|
+
* for tests.
|
|
8
|
+
*/
|
|
9
|
+
export declare function enableScreenTracking(enabled?: boolean, wait?: number): void;
|
|
10
|
+
/**
|
|
11
|
+
* The name automatic tracking gives the page as it is now: the cleaned path. For records that
|
|
12
|
+
* need a screen when none has been
|
|
13
|
+
* tracked — heatmaps with `track_screen: false` and no inScreen() call, say. '' when the page
|
|
14
|
+
* has no name.
|
|
15
|
+
*/
|
|
16
|
+
export declare function automaticScreenName(): string;
|
|
17
|
+
/** Whether history is being watched right now. */
|
|
18
|
+
export declare function isScreenTrackingEnabled(): boolean;
|