@zoho_apptics/apptics-js-sdk 2.0.0-beta → 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 +111 -5
- package/README.md +399 -300
- package/dist/apptics.esm.min.js +2 -2
- package/dist/apptics.esm.min.js.map +1 -1
- package/dist/apptics.min.js +2 -2
- package/dist/apptics.min.js.map +1 -1
- 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 +3 -3
- package/dist/cjs/auto-init.js +1 -1
- package/dist/cjs/core/attempts.js +1 -1
- package/dist/cjs/core/batch.js +2 -1
- package/dist/cjs/core/callsite.d.ts +6 -0
- package/dist/cjs/core/callsite.js +25 -3
- package/dist/cjs/core/consent.d.ts +5 -1
- package/dist/cjs/core/consent.js +8 -6
- package/dist/cjs/core/constants.d.ts +35 -10
- package/dist/cjs/core/constants.js +31 -15
- package/dist/cjs/core/db.d.ts +12 -4
- package/dist/cjs/core/db.js +167 -34
- package/dist/cjs/core/debug.js +1 -1
- package/dist/cjs/core/env.js +1 -1
- package/dist/cjs/core/framework-id.d.ts +1 -1
- package/dist/cjs/core/framework-id.js +3 -3
- package/dist/cjs/core/guard.d.ts +9 -0
- package/dist/cjs/core/guard.js +27 -13
- package/dist/cjs/core/hooks.d.ts +2 -4
- package/dist/cjs/core/hooks.js +2 -3
- package/dist/cjs/core/locks.d.ts +9 -0
- package/dist/cjs/core/locks.js +22 -0
- package/dist/cjs/core/logger.d.ts +3 -4
- package/dist/cjs/core/logger.js +4 -40
- package/dist/cjs/core/state.d.ts +3 -2
- package/dist/cjs/core/state.js +2 -2
- package/dist/cjs/core/storage.d.ts +20 -5
- package/dist/cjs/core/storage.js +115 -17
- package/dist/cjs/core/stylesheet.d.ts +8 -0
- package/dist/cjs/core/stylesheet.js +29 -0
- package/dist/cjs/core/transport.d.ts +2 -0
- package/dist/cjs/core/transport.js +19 -4
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +2 -2
- package/dist/cjs/device/appversion.d.ts +1 -6
- package/dist/cjs/device/appversion.js +13 -1
- package/dist/cjs/device/index.d.ts +11 -0
- package/dist/cjs/device/index.js +120 -20
- package/dist/cjs/engagement/api-rules.d.ts +32 -67
- package/dist/cjs/engagement/api-rules.js +144 -157
- package/dist/cjs/engagement/auto-api.js +2 -2
- package/dist/cjs/engagement/auto-screen.d.ts +2 -0
- package/dist/cjs/engagement/auto-screen.js +67 -3
- package/dist/cjs/engagement/defined-events.js +1 -1
- package/dist/cjs/engagement/send.js +6 -1
- package/dist/cjs/engagement/session.d.ts +12 -3
- package/dist/cjs/engagement/session.js +161 -25
- package/dist/cjs/engagement/track.js +45 -20
- package/dist/cjs/errors/send.js +1 -1
- package/dist/cjs/errors/track.d.ts +1 -1
- package/dist/cjs/errors/track.js +46 -6
- package/dist/cjs/feedback/controls.d.ts +6 -0
- package/dist/cjs/feedback/controls.js +31 -1
- package/dist/cjs/feedback/index.d.ts +8 -7
- package/dist/cjs/feedback/index.js +32 -13
- package/dist/cjs/feedback/logs.d.ts +10 -5
- package/dist/cjs/feedback/logs.js +52 -13
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +3 -0
- package/dist/cjs/feedback/send.js +28 -3
- package/dist/cjs/feedback/styles.d.ts +10 -0
- package/dist/cjs/feedback/styles.js +8 -0
- package/dist/cjs/feedback/template.d.ts +1 -1
- package/dist/cjs/feedback/template.js +12 -12
- package/dist/cjs/feedback/ui.d.ts +14 -6
- package/dist/cjs/feedback/ui.js +126 -48
- package/dist/cjs/heatmap/send.js +1 -1
- package/dist/cjs/heatmap/track.d.ts +1 -1
- package/dist/cjs/heatmap/track.js +87 -16
- package/dist/cjs/index.d.ts +17 -63
- package/dist/cjs/index.js +104 -92
- package/dist/cjs/lifecycle.d.ts +8 -1
- package/dist/cjs/lifecycle.js +258 -62
- package/dist/cjs/remoteconfig/country.d.ts +4 -0
- package/dist/cjs/remoteconfig/country.js +73 -0
- package/dist/cjs/remoteconfig/custom.d.ts +7 -7
- package/dist/cjs/remoteconfig/custom.js +31 -51
- package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
- package/dist/cjs/remoteconfig/evaluate.js +56 -43
- package/dist/cjs/remoteconfig/fetch.d.ts +3 -3
- package/dist/cjs/remoteconfig/fetch.js +9 -23
- package/dist/cjs/remoteconfig/index.d.ts +10 -8
- package/dist/cjs/remoteconfig/index.js +65 -39
- package/dist/cjs/remoteconfig/store.js +1 -1
- 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 -11
- package/dist/cjs/remotelog/gate.js +9 -35
- package/dist/cjs/remotelog/index.d.ts +4 -21
- package/dist/cjs/remotelog/logger.d.ts +11 -17
- package/dist/cjs/remotelog/logger.js +30 -58
- package/dist/cjs/remotelog/send.js +10 -10
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.js +2 -2
- package/dist/cjs/types.d.ts +155 -82
- package/dist/cjs/user/index.js +11 -6
- package/dist/esm/auto-init.d.ts +3 -3
- package/dist/esm/auto-init.js +1 -1
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +2 -1
- package/dist/esm/core/callsite.d.ts +6 -0
- package/dist/esm/core/callsite.js +25 -4
- package/dist/esm/core/consent.d.ts +5 -1
- package/dist/esm/core/consent.js +9 -7
- package/dist/esm/core/constants.d.ts +35 -10
- package/dist/esm/core/constants.js +30 -15
- package/dist/esm/core/db.d.ts +12 -4
- package/dist/esm/core/db.js +164 -35
- package/dist/esm/core/debug.js +1 -1
- package/dist/esm/core/env.js +1 -1
- package/dist/esm/core/framework-id.d.ts +1 -1
- package/dist/esm/core/framework-id.js +3 -3
- package/dist/esm/core/guard.d.ts +9 -0
- package/dist/esm/core/guard.js +27 -14
- package/dist/esm/core/hooks.d.ts +2 -4
- package/dist/esm/core/hooks.js +2 -3
- package/dist/esm/core/locks.d.ts +9 -0
- package/dist/esm/core/locks.js +20 -0
- package/dist/esm/core/logger.d.ts +3 -4
- package/dist/esm/core/logger.js +4 -40
- package/dist/esm/core/state.d.ts +3 -2
- package/dist/esm/core/state.js +2 -2
- package/dist/esm/core/storage.d.ts +20 -5
- package/dist/esm/core/storage.js +113 -18
- package/dist/esm/core/stylesheet.d.ts +8 -0
- package/dist/esm/core/stylesheet.js +27 -0
- package/dist/esm/core/transport.d.ts +2 -0
- package/dist/esm/core/transport.js +19 -5
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +2 -2
- package/dist/esm/device/appversion.d.ts +1 -6
- package/dist/esm/device/appversion.js +13 -2
- package/dist/esm/device/index.d.ts +11 -0
- package/dist/esm/device/index.js +123 -24
- package/dist/esm/engagement/api-rules.d.ts +32 -67
- package/dist/esm/engagement/api-rules.js +143 -155
- package/dist/esm/engagement/auto-api.js +3 -3
- package/dist/esm/engagement/auto-screen.d.ts +2 -0
- package/dist/esm/engagement/auto-screen.js +67 -4
- package/dist/esm/engagement/defined-events.js +1 -1
- package/dist/esm/engagement/send.js +6 -1
- package/dist/esm/engagement/session.d.ts +12 -3
- package/dist/esm/engagement/session.js +162 -29
- package/dist/esm/engagement/track.js +52 -27
- package/dist/esm/errors/send.js +1 -1
- package/dist/esm/errors/track.d.ts +1 -1
- package/dist/esm/errors/track.js +46 -6
- package/dist/esm/feedback/controls.d.ts +6 -0
- package/dist/esm/feedback/controls.js +31 -2
- package/dist/esm/feedback/index.d.ts +8 -7
- package/dist/esm/feedback/index.js +34 -13
- package/dist/esm/feedback/logs.d.ts +10 -5
- package/dist/esm/feedback/logs.js +54 -15
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +3 -0
- package/dist/esm/feedback/send.js +28 -5
- package/dist/esm/feedback/styles.d.ts +10 -0
- package/dist/esm/feedback/styles.js +4 -0
- package/dist/esm/feedback/template.d.ts +1 -1
- package/dist/esm/feedback/template.js +12 -12
- package/dist/esm/feedback/ui.d.ts +14 -6
- package/dist/esm/feedback/ui.js +126 -49
- package/dist/esm/heatmap/send.js +1 -1
- package/dist/esm/heatmap/track.d.ts +1 -1
- package/dist/esm/heatmap/track.js +87 -16
- package/dist/esm/index.d.ts +17 -63
- package/dist/esm/index.js +108 -80
- package/dist/esm/lifecycle.d.ts +8 -1
- package/dist/esm/lifecycle.js +266 -71
- package/dist/esm/remoteconfig/country.d.ts +4 -0
- package/dist/esm/remoteconfig/country.js +70 -0
- package/dist/esm/remoteconfig/custom.d.ts +7 -7
- package/dist/esm/remoteconfig/custom.js +32 -52
- package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
- package/dist/esm/remoteconfig/evaluate.js +57 -44
- package/dist/esm/remoteconfig/fetch.d.ts +3 -3
- package/dist/esm/remoteconfig/fetch.js +9 -23
- package/dist/esm/remoteconfig/index.d.ts +10 -8
- package/dist/esm/remoteconfig/index.js +66 -40
- package/dist/esm/remoteconfig/store.js +1 -1
- 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 -11
- package/dist/esm/remotelog/gate.js +10 -32
- package/dist/esm/remotelog/index.d.ts +4 -21
- package/dist/esm/remotelog/logger.d.ts +11 -17
- package/dist/esm/remotelog/logger.js +32 -60
- package/dist/esm/remotelog/send.js +12 -12
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.js +2 -2
- package/dist/esm/types.d.ts +155 -82
- package/dist/esm/user/index.js +12 -6
- package/dist/feedback.esm.min.js +2 -2
- package/dist/feedback.esm.min.js.map +1 -1
- package/package.json +14 -16
- package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
- package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
- package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
- package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
- package/dist/cjs/heatmap/index.d.ts +0 -16
- package/dist/cjs/heatmap/index.js +0 -22
- package/dist/cjs/remotelog/index.js +0 -55
- package/dist/esm/heatmap/index.d.ts +0 -16
- package/dist/esm/heatmap/index.js +0 -17
- package/dist/esm/remotelog/index.js +0 -51
- package/dist/heatmap.esm.min.js +0 -3
- package/dist/heatmap.esm.min.js.map +0 -1
- package/dist/style.css +0 -1
|
@@ -1,211 +1,199 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
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
|
+
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
5
|
+
* Which API calls are recorded, and under what name: Android's AppticsApiTracker.configure, built
|
|
4
6
|
* with ApiTrackingBuilder the way user properties are built with UserBuilder:
|
|
5
7
|
*
|
|
6
8
|
* configureApiTracking(new ApiTrackingBuilder()
|
|
7
|
-
* .
|
|
8
|
-
* .
|
|
9
|
-
* .
|
|
10
|
-
* .addPattern('/v2/catalog/{categoryId}/items/{itemId}')
|
|
9
|
+
* .allowRegex(/^https:\/\/api\.zylker\.(com|in)\//)
|
|
10
|
+
* .ignoreEquals('https://api.zylker.com/health')
|
|
11
|
+
* .ignoreRegex(/\/internal\//)
|
|
11
12
|
* .build());
|
|
12
13
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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.
|
|
18
21
|
*
|
|
19
22
|
* Both apply to every call, the ones the fetch/XHR wrappers catch and the ones the app reports
|
|
20
|
-
* with
|
|
23
|
+
* with startTrackApi(), so one set of rules decides what the API page shows.
|
|
21
24
|
*/
|
|
22
25
|
function emptyConfig() {
|
|
23
|
-
return {
|
|
24
|
-
ignoreDomains: [], allowOnlyDomains: [], allowOnlyTLDs: [], ignoreTLDs: [],
|
|
25
|
-
ignoreDomainPrefixes: [], ignoreDomainSuffixes: [], groupDomains: [], ignoreEndpoints: [],
|
|
26
|
-
patterns: [], preserveSegments: [], autoDetection: true,
|
|
27
|
-
};
|
|
26
|
+
return { allowEquals: [], ignoreEquals: [], allowRegex: [], ignoreRegex: [] };
|
|
28
27
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
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) {
|
|
31
48
|
var out = [];
|
|
32
49
|
values.forEach(function (value) {
|
|
33
|
-
|
|
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
|
|
34
53
|
return;
|
|
35
54
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
out.push(lower ? one.toLowerCase() : one);
|
|
55
|
+
if (out.indexOf(url) === -1) {
|
|
56
|
+
out.push(url);
|
|
39
57
|
}
|
|
40
58
|
});
|
|
41
59
|
return out;
|
|
42
60
|
}
|
|
43
|
-
/**
|
|
44
|
-
function
|
|
45
|
-
|
|
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;
|
|
46
81
|
}
|
|
47
|
-
/**
|
|
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
|
+
*/
|
|
48
86
|
class ApiTrackingBuilder {
|
|
49
87
|
constructor() {
|
|
50
88
|
this.rules = emptyConfig();
|
|
51
89
|
}
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
/** Records only
|
|
57
|
-
|
|
58
|
-
/** Never records
|
|
59
|
-
|
|
60
|
-
/** Never records hosts starting with these, e.g. `dev.`. */
|
|
61
|
-
ignoreDomainPrefixes(...prefixes) { this.rules.ignoreDomainPrefixes.push(...cleanList(prefixes, true)); return this; }
|
|
62
|
-
/** Never records hosts ending with these, e.g. `.cdn.net`. */
|
|
63
|
-
ignoreDomainSuffixes(...suffixes) { this.rules.ignoreDomainSuffixes.push(...cleanList(suffixes, true)); return this; }
|
|
64
|
-
/** Records the hosts a pattern such as `api.myapp.*` matches under that one name. */
|
|
65
|
-
groupDomains(...patterns) { this.rules.groupDomains.push(...cleanList(patterns, true)); return this; }
|
|
66
|
-
/** Never records these paths: exact, `*` or `{name}` for one segment, `**` at the end for a prefix. */
|
|
67
|
-
ignoreEndpoint(...paths) { this.rules.ignoreEndpoints.push(...cleanList(paths, false)); return this; }
|
|
68
|
-
/** Records a path this pattern matches as the pattern, with each `{name}` as `*`. Tried before auto-detection. */
|
|
69
|
-
addPattern(...patterns) { this.rules.patterns.push(...cleanList(patterns, false)); return this; }
|
|
70
|
-
/** Segments never turned into `*`, e.g. `v1` or `404`. */
|
|
71
|
-
preserveSegments(...segments) { this.rules.preserveSegments.push(...cleanList(segments, false)); return this; }
|
|
72
|
-
/** `false`: only addPattern paths are normalized; every other path is recorded as it is. */
|
|
73
|
-
autoDetection(enabled) { this.rules.autoDetection = enabled !== false; return this; }
|
|
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; }
|
|
74
98
|
build() {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
+
};
|
|
81
105
|
}
|
|
82
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
|
+
});
|
|
83
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
|
+
}
|
|
84
139
|
/**
|
|
85
140
|
* Applies the rules, replacing any given before. Takes what build() returned, or the builder
|
|
86
141
|
* itself. Call it once, before the requests it should cover; it works before init().
|
|
87
142
|
*/
|
|
88
143
|
function configureApiTracking(config) {
|
|
89
|
-
var built = config instanceof ApiTrackingBuilder ? config.build() : config;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
var value = built[key];
|
|
94
|
-
if (key === 'autoDetection') { // no i18n
|
|
95
|
-
next.autoDetection = value !== false;
|
|
96
|
-
}
|
|
97
|
-
else if (Array.isArray(value)) {
|
|
98
|
-
next[key] = value.slice();
|
|
99
|
-
}
|
|
100
|
-
});
|
|
144
|
+
var built = (config instanceof ApiTrackingBuilder ? config.build() : config);
|
|
145
|
+
if (!built || typeof built !== 'object') { // no i18n
|
|
146
|
+
current = emptyConfig();
|
|
147
|
+
return;
|
|
101
148
|
}
|
|
102
|
-
|
|
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
|
+
};
|
|
103
158
|
}
|
|
104
|
-
|
|
105
|
-
function
|
|
106
|
-
|
|
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);
|
|
107
163
|
}
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
if (current.allowOnlyDomains.length > 0 && current.allowOnlyDomains.indexOf(host) === -1) {
|
|
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
|
|
118
171
|
return false;
|
|
119
172
|
}
|
|
120
|
-
if (current.
|
|
173
|
+
if (current.ignoreEquals.indexOf(target) !== -1 || current.ignoreRegex.some(function (re) { return regexMatches(re, target); })) {
|
|
121
174
|
return false;
|
|
122
175
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
function wildcardHost(pattern) {
|
|
126
|
-
var source = pattern.split('*').map(function (part) {
|
|
127
|
-
return part.replace(/[.+?^${}()|[\]\\]/g, '\\$&'); // no i18n
|
|
128
|
-
}).join('[a-z0-9.-]+'); // no i18n
|
|
129
|
-
return new RegExp('^' + source + '$'); // no i18n
|
|
130
|
-
}
|
|
131
|
-
/** The name a host is recorded under: the first groupDomains pattern it matches, or itself. */
|
|
132
|
-
function groupedHost(hostname) {
|
|
133
|
-
var host = String(hostname || '').toLowerCase(); // no i18n
|
|
134
|
-
for (var i = 0; i < current.groupDomains.length; i++) {
|
|
135
|
-
var pattern = current.groupDomains[i];
|
|
136
|
-
if (pattern.indexOf('*') !== -1 && wildcardHost(pattern).test(host)) { // no i18n
|
|
137
|
-
return pattern;
|
|
138
|
-
}
|
|
176
|
+
if (current.allowEquals.length === 0 && current.allowRegex.length === 0) {
|
|
177
|
+
return true;
|
|
139
178
|
}
|
|
140
|
-
return
|
|
179
|
+
return current.allowEquals.indexOf(target) !== -1 || current.allowRegex.some(function (re) { return regexMatches(re, target); });
|
|
141
180
|
}
|
|
142
|
-
/* ----------------
|
|
181
|
+
/* ---------------- the name a call is recorded under ---------------- */
|
|
143
182
|
/** The path without its query string or fragment, which is where ids and tokens travel. */
|
|
144
183
|
function withoutQuery(path) {
|
|
145
184
|
var cut = String(path || '').split(/[?#]/)[0]; // no i18n
|
|
146
185
|
return cut === '' ? '/' : cut; // no i18n
|
|
147
186
|
}
|
|
148
|
-
function segmentsOf(path) {
|
|
149
|
-
var trimmed = path.length > 1 ? path.replace(/\/+$/, '') : path; // no i18n
|
|
150
|
-
return trimmed.split('/').slice(1); // no i18n
|
|
151
|
-
}
|
|
152
|
-
const ONE_SEGMENT = /^(\*|\{[^/{}]*\})$/;
|
|
153
|
-
/** Whether a path matches a pattern: `*` and `{name}` are one segment, a final `**` is the rest. */
|
|
154
|
-
function matches(pattern, path) {
|
|
155
|
-
var want = segmentsOf(pattern);
|
|
156
|
-
var have = segmentsOf(path);
|
|
157
|
-
for (var i = 0; i < want.length; i++) {
|
|
158
|
-
var part = want[i];
|
|
159
|
-
if (part === '**' && i === want.length - 1) { // no i18n
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
if (i >= have.length) {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
if (ONE_SEGMENT.test(part)) {
|
|
166
|
-
if (have[i] === '') { // no i18n
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
continue;
|
|
170
|
-
}
|
|
171
|
-
if (part !== have[i]) {
|
|
172
|
-
return false;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
return want.length === have.length;
|
|
176
|
-
}
|
|
177
|
-
/** Whether ignoreEndpoint leaves this path out. Matched on the path as called, query removed. */
|
|
178
|
-
function endpointIgnored(path) {
|
|
179
|
-
var bare = withoutQuery(path);
|
|
180
|
-
return current.ignoreEndpoints.some(function (pattern) { return matches(pattern, bare); });
|
|
181
|
-
}
|
|
182
187
|
const NUMERIC = /^\d+$/;
|
|
183
188
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
184
189
|
// three base64url parts, the first starting eyJ — the encoding of '{"' every JWT header opens
|
|
185
190
|
// with. Without that start, a file name such as jquery.min.js would pass for a token
|
|
186
191
|
const JWT = /^eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
187
|
-
/**
|
|
188
|
-
* The path a call is recorded under. The query goes; then the first addPattern that matches is
|
|
189
|
-
* used, with each `{name}` as `*` (the server's path rule has no room for braces); otherwise, with
|
|
190
|
-
* autoDetection on, numeric ids, UUIDs and JWTs become `*`, except preserveSegments.
|
|
191
|
-
*/
|
|
192
|
+
/** The path a call is recorded under: the query goes, and numeric ids, UUIDs and JWTs become `*`. */
|
|
192
193
|
function normalizeApiPath(path) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
var pattern = current.patterns[i];
|
|
196
|
-
if (matches(pattern, bare)) {
|
|
197
|
-
return pattern.replace(/\{[^/{}]*\}/g, '*'); // no i18n
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (!current.autoDetection) {
|
|
201
|
-
return bare;
|
|
202
|
-
}
|
|
203
|
-
return bare.split('/').map(function (segment) {
|
|
204
|
-
if (segment === '' || current.preserveSegments.indexOf(segment) !== -1) { // no i18n
|
|
205
|
-
return segment;
|
|
206
|
-
}
|
|
207
|
-
return NUMERIC.test(segment) || UUID.test(segment) || JWT.test(segment) ? '*' : segment; // no i18n
|
|
194
|
+
return withoutQuery(path).split('/').map(function (segment) {
|
|
195
|
+
return segment !== '' && (NUMERIC.test(segment) || UUID.test(segment) || JWT.test(segment)) ? '*' : segment; // no i18n
|
|
208
196
|
}).join('/'); // no i18n
|
|
209
197
|
}
|
|
210
198
|
|
|
211
|
-
export { ApiTrackingBuilder, configureApiTracking,
|
|
199
|
+
export { ApiTrackingBuilder, configureApiTracking, normalizeApiPath, ruleUrl, urlTracked, withoutQuery };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { _ZAP, getConfig } from '../core/state.js';
|
|
3
3
|
import { Console } from '../core/logger.js';
|
|
4
4
|
import { nowMs, isEmpty } from '../core/utils.js';
|
|
5
5
|
import { trackApi } from './track.js';
|
|
6
|
-
import {
|
|
6
|
+
import { urlTracked } from './api-rules.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Automatic API tracking: wraps XMLHttpRequest and fetch so calls the app makes are recorded
|
|
@@ -64,7 +64,7 @@ function shouldTrack(rawUrl) {
|
|
|
64
64
|
}
|
|
65
65
|
// configureApiTracking's rules: checked here too so a call left out is not even timed. trackApi checks them again, for the
|
|
66
66
|
// calls an app reports itself
|
|
67
|
-
return
|
|
67
|
+
return urlTracked(absolute.href);
|
|
68
68
|
}
|
|
69
69
|
/** One finished call. Never throws: a tracking failure must not surface in the app's call. */
|
|
70
70
|
function record(method, url, starttime, responsecode, responsemessage) {
|
|
@@ -1,3 +1,5 @@
|
|
|
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;
|
|
1
3
|
/**
|
|
2
4
|
* Starts or stops automatic screen tracking. Starting records the screen the page is on, unless
|
|
3
5
|
* the app has already called inScreen() for it. Safe to call repeatedly: wrapping history twice
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { Console } from '../core/logger.js';
|
|
3
3
|
import { hooks } from '../core/hooks.js';
|
|
4
4
|
import { _ZAP } from '../core/state.js';
|
|
@@ -30,9 +30,14 @@ import { recordScreen } from './track.js';
|
|
|
30
30
|
* On by default: most apps never call inScreen() themselves, and a screen-less session says
|
|
31
31
|
* little. An app that wants only its own calls sets `track_screen: false`.
|
|
32
32
|
*/
|
|
33
|
+
/** A path as screen_routes compares it: no query or fragment, no trailing slash but on the root. */
|
|
34
|
+
function routePath(path) {
|
|
35
|
+
var bare = String(path || '').split(/[?#]/)[0] || '/'; // no i18n
|
|
36
|
+
return bare.length > 1 ? bare.replace(/\/+$/, '') : bare; // no i18n
|
|
37
|
+
}
|
|
33
38
|
/** The most a screen name may be, and the characters it may hold (see validateScreenName). */
|
|
34
39
|
const NAME_MAX = 99;
|
|
35
|
-
const NAME_DISALLOWED = /[^\w\s
|
|
40
|
+
const NAME_DISALLOWED = /[^\w\s.\-&/:*]/g;
|
|
36
41
|
/** How long an automatic view waits for the app's own inScreen() of the same navigation. */
|
|
37
42
|
const SETTLE_MS = 300;
|
|
38
43
|
var installed = false;
|
|
@@ -80,13 +85,61 @@ function cleanName(raw) {
|
|
|
80
85
|
}
|
|
81
86
|
return name.replace(NAME_DISALLOWED, '_').trim().slice(0, NAME_MAX); // no i18n
|
|
82
87
|
}
|
|
88
|
+
/** Whether a pattern segment is a wildcard: `{id}`, `:id` or `*`. */
|
|
89
|
+
function isWildcard(segment) {
|
|
90
|
+
return segment === '*' || /^\{[^/]*\}$/.test(segment) || /^:[^/]+$/.test(segment); // no i18n
|
|
91
|
+
}
|
|
92
|
+
/** Whether a route pattern matches a path: every segment, `{id}`, `:id` and `*` matching any one. */
|
|
93
|
+
function patternMatches(pattern, path) {
|
|
94
|
+
var segments = routePath(pattern).split('/'); // no i18n
|
|
95
|
+
var actual = path.split('/'); // no i18n
|
|
96
|
+
if (segments.length !== actual.length) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
for (var j = 0; j < segments.length; j++) {
|
|
100
|
+
var seg = segments[j];
|
|
101
|
+
if (!isWildcard(seg) && seg !== actual[j]) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The name screen_routes gives a path, or the path itself when no entry matches. Tried in order:
|
|
109
|
+
* a route pattern records the path with its variable segments as `*` (`/users/{id}/orders`
|
|
110
|
+
* claims `/users/1/orders`, not `/users/1/orders/2`); an exact path or a regular expression
|
|
111
|
+
* records the name the entry gives. Matched on the path without its query.
|
|
112
|
+
*/
|
|
113
|
+
function applyRoutes(path) {
|
|
114
|
+
var routes = _ZAP._config.screen_routes;
|
|
115
|
+
if (!routes || routes.length === 0) {
|
|
116
|
+
return path;
|
|
117
|
+
}
|
|
118
|
+
var bare = routePath(path);
|
|
119
|
+
for (var i = 0; i < routes.length; i++) {
|
|
120
|
+
var route = routes[i];
|
|
121
|
+
if (route.kind === 'equals' && route.path === bare) { // no i18n
|
|
122
|
+
return route.name;
|
|
123
|
+
}
|
|
124
|
+
if (route.kind === 'regex') { // no i18n
|
|
125
|
+
route.regex.lastIndex = 0; // a /g regular expression keeps a position between tests
|
|
126
|
+
if (route.regex.test(bare)) {
|
|
127
|
+
return route.name;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (route.kind === 'pattern' && patternMatches(route.pattern, bare)) { // no i18n
|
|
131
|
+
return routePath(route.pattern).split('/').map((s) => (isWildcard(s) ? '*' : s)).join('/'); // no i18n
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return path;
|
|
135
|
+
}
|
|
83
136
|
/**
|
|
84
137
|
* The name for the page as it is now, from its url, or '' to record nothing. Never throws. An app
|
|
85
138
|
* that wants its own names calls inScreen() as the route changes, which replaces this view.
|
|
86
139
|
*/
|
|
87
140
|
function currentName() {
|
|
88
141
|
try {
|
|
89
|
-
return cleanName(nameFromUrl(new URL(window.location.href)));
|
|
142
|
+
return cleanName(applyRoutes(nameFromUrl(new URL(window.location.href))));
|
|
90
143
|
}
|
|
91
144
|
catch (err) {
|
|
92
145
|
return ''; // no i18n
|
|
@@ -138,8 +191,18 @@ function cancelPending() {
|
|
|
138
191
|
* Collects the history calls and events of one navigation into a single check. The first call of
|
|
139
192
|
* a navigation starts it afresh: a inScreen() from before it belonged to the previous screen,
|
|
140
193
|
* and a view still waiting is a page left too soon to count.
|
|
194
|
+
*
|
|
195
|
+
* A history call that leaves the screen where it is, is not a navigation. Next.js and other
|
|
196
|
+
* routers replaceState the same url right after a route change, to store their state or the
|
|
197
|
+
* scroll position. Treated as one, it cancelled the view still waiting to settle, and the check
|
|
198
|
+
* that followed found the url already seen and recorded nothing: the page went untracked and the
|
|
199
|
+
* view before it was never ended.
|
|
141
200
|
*/
|
|
142
201
|
function navigated() {
|
|
202
|
+
var name = currentName();
|
|
203
|
+
if (name !== '' && (name === pendingName || (pendingName === '' && name === lastName))) { // no i18n
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
143
206
|
if (timer === null) {
|
|
144
207
|
claimed = false;
|
|
145
208
|
cancelPending();
|
|
@@ -235,4 +298,4 @@ function automaticScreenName() {
|
|
|
235
298
|
}
|
|
236
299
|
hooks.screenTrackedByApp = screenTrackedByApp;
|
|
237
300
|
|
|
238
|
-
export { automaticScreenName, enableScreenTracking };
|
|
301
|
+
export { automaticScreenName, enableScreenTracking, routePath };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
/**
|
|
3
3
|
* Apptics' defined events, as Android's DefinedEvents: ready-made names in reserved `AP_` groups,
|
|
4
4
|
* logged with trackEvent like any other event and never tracked automatically.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { AP_LOG_MODULE, AP_CONSTANTS } from '../core/constants.js';
|
|
3
3
|
import { _ZAP } from '../core/state.js';
|
|
4
4
|
import { Console, logFailure, warnAlways } from '../core/logger.js';
|
|
@@ -56,6 +56,11 @@ function sendEngagementData(forget) {
|
|
|
56
56
|
sent.push(itemWithId);
|
|
57
57
|
delete item.id;
|
|
58
58
|
delete item.type;
|
|
59
|
+
// which tabs share a session, which tab shows a screen and when it was last seen: the
|
|
60
|
+
// SDK's own bookkeeping, not part of the payload
|
|
61
|
+
delete item.tabs;
|
|
62
|
+
delete item.tab;
|
|
63
|
+
delete item.lastseen;
|
|
59
64
|
});
|
|
60
65
|
if (eventObj.length > 0)
|
|
61
66
|
_payload.events = eventObj;
|
|
@@ -15,8 +15,16 @@ export declare function updateScreenWithEndtime(): Promise<void>;
|
|
|
15
15
|
* @param sessionData A set of properties belonging to the session.
|
|
16
16
|
*/
|
|
17
17
|
export declare function trackSession(sessionData: Dict): Promise<void>;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* This tab's session while it is open, otherwise the newest open session in storage: the one the
|
|
20
|
+
* site's other tabs are on, which this tab joins rather than starting a second session beside it.
|
|
21
|
+
*/
|
|
19
22
|
export declare function currentSession(): Promise<QueueItem | undefined>;
|
|
23
|
+
export declare function tabId(): string;
|
|
24
|
+
/** Takes this tab's lock, held until the page is gone. Once per page. */
|
|
25
|
+
export declare function holdTabLock(): void;
|
|
26
|
+
/** Ends the open screen views of tabs that are gone. Resolves right away without Web Locks. */
|
|
27
|
+
export declare function closeScreensOfGoneTabs(): Promise<void>;
|
|
20
28
|
/**
|
|
21
29
|
* Writes the waiting session, if there is one. Called by the timer and by anything that tracks an
|
|
22
30
|
* item, so an event in the first seconds takes its session with it.
|
|
@@ -28,8 +36,9 @@ export declare function dropPendingSession(): boolean;
|
|
|
28
36
|
export declare function hasPendingSession(): boolean;
|
|
29
37
|
export declare function startNewSession(time: number): Promise<void>;
|
|
30
38
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
39
|
+
* This tab is closing, or the page is being replaced. The session ends here when this is the last
|
|
40
|
+
* tab using it, so it can be sent; while another tab of the site is still on it, this tab only
|
|
41
|
+
* leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
|
|
33
42
|
*/
|
|
34
43
|
export declare function endCurrentSession(): Promise<void>;
|
|
35
44
|
/**
|