@zoho_apptics/apptics-js-sdk 2.0.0-beta → 2.0.0-beta.2
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 +191 -21
- package/README.md +519 -333
- 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-C6m1W7jo.min.js +3 -0
- package/dist/chunks/lifecycle-C6m1W7jo.min.js.map +1 -0
- package/dist/chunks/send-CgUO82ne.min.js +3 -0
- package/dist/chunks/send-CgUO82ne.min.js.map +1 -0
- package/dist/cjs/core/attempts.js +1 -1
- package/dist/cjs/core/batch.js +2 -2
- package/dist/cjs/core/consent.d.ts +18 -4
- package/dist/cjs/core/consent.js +31 -15
- package/dist/cjs/core/constants.d.ts +57 -27
- package/dist/cjs/core/constants.js +52 -33
- 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.d.ts +0 -3
- package/dist/cjs/core/env.js +1 -9
- 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 +7 -4
- package/dist/cjs/core/hooks.js +3 -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 +9 -6
- package/dist/cjs/core/state.js +3 -5
- package/dist/cjs/core/storage.d.ts +20 -5
- package/dist/cjs/core/storage.js +115 -20
- 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 +30 -18
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +2 -2
- package/dist/cjs/device/appversion.d.ts +10 -6
- package/dist/cjs/device/appversion.js +38 -3
- package/dist/cjs/device/index.d.ts +28 -2
- package/dist/cjs/device/index.js +312 -44
- 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 +12 -3
- package/dist/cjs/engagement/session.d.ts +29 -3
- package/dist/cjs/engagement/session.js +253 -26
- package/dist/cjs/engagement/track.d.ts +4 -4
- package/dist/cjs/engagement/track.js +58 -30
- package/dist/cjs/errors/send.js +2 -2
- package/dist/cjs/errors/track.d.ts +2 -1
- package/dist/cjs/errors/track.js +49 -7
- package/dist/cjs/feedback/controls.d.ts +12 -0
- package/dist/cjs/feedback/controls.js +104 -25
- package/dist/cjs/feedback/index.d.ts +8 -7
- package/dist/cjs/feedback/index.js +32 -13
- package/dist/cjs/feedback/logs.d.ts +13 -11
- package/dist/cjs/feedback/logs.js +57 -28
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +5 -2
- package/dist/cjs/feedback/send.js +30 -28
- 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 +20 -12
- package/dist/cjs/feedback/ui.d.ts +21 -6
- package/dist/cjs/feedback/ui.js +283 -70
- package/dist/cjs/heatmap/send.d.ts +2 -0
- package/dist/cjs/heatmap/send.js +3 -5
- package/dist/cjs/heatmap/track.d.ts +1 -1
- package/dist/cjs/heatmap/track.js +87 -16
- package/dist/cjs/index.d.ts +16 -63
- package/dist/cjs/index.js +144 -95
- package/dist/cjs/lifecycle.d.ts +21 -3
- package/dist/cjs/lifecycle.js +353 -89
- 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 +16 -9
- package/dist/cjs/remoteconfig/fetch.js +120 -39
- package/dist/cjs/remoteconfig/index.d.ts +19 -8
- package/dist/cjs/remoteconfig/index.js +81 -39
- package/dist/cjs/remoteconfig/store.d.ts +15 -7
- package/dist/cjs/remoteconfig/store.js +68 -35
- package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/cjs/remoteconfig/tz-countries.js +13 -0
- package/dist/{esm/core → cjs/remotelog}/callsite.d.ts +11 -4
- package/dist/cjs/{core → remotelog}/callsite.js +30 -7
- 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 +31 -59
- package/dist/cjs/remotelog/send.js +11 -11
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.d.ts +3 -1
- package/dist/cjs/scheduler.js +25 -22
- package/dist/cjs/types.d.ts +208 -90
- package/dist/cjs/user/index.js +16 -7
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +2 -2
- package/dist/esm/core/consent.d.ts +18 -4
- package/dist/esm/core/consent.js +32 -17
- package/dist/esm/core/constants.d.ts +57 -27
- package/dist/esm/core/constants.js +51 -33
- 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.d.ts +0 -3
- package/dist/esm/core/env.js +2 -9
- 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 +7 -4
- package/dist/esm/core/hooks.js +3 -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 +9 -6
- package/dist/esm/core/state.js +3 -5
- package/dist/esm/core/storage.d.ts +20 -5
- package/dist/esm/core/storage.js +113 -21
- 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 +30 -19
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +2 -2
- package/dist/esm/device/appversion.d.ts +10 -6
- package/dist/esm/device/appversion.js +37 -4
- package/dist/esm/device/index.d.ts +28 -2
- package/dist/esm/device/index.js +313 -49
- 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 +12 -3
- package/dist/esm/engagement/session.d.ts +29 -3
- package/dist/esm/engagement/session.js +251 -30
- package/dist/esm/engagement/track.d.ts +4 -4
- package/dist/esm/engagement/track.js +65 -37
- package/dist/esm/errors/send.js +2 -2
- package/dist/esm/errors/track.d.ts +2 -1
- package/dist/esm/errors/track.js +49 -7
- package/dist/esm/feedback/controls.d.ts +12 -0
- package/dist/esm/feedback/controls.js +103 -26
- package/dist/esm/feedback/index.d.ts +8 -7
- package/dist/esm/feedback/index.js +34 -13
- package/dist/esm/feedback/logs.d.ts +13 -11
- package/dist/esm/feedback/logs.js +59 -30
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +5 -2
- package/dist/esm/feedback/send.js +31 -31
- 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 +20 -12
- package/dist/esm/feedback/ui.d.ts +21 -6
- package/dist/esm/feedback/ui.js +283 -71
- package/dist/esm/heatmap/send.d.ts +2 -0
- package/dist/esm/heatmap/send.js +3 -5
- package/dist/esm/heatmap/track.d.ts +1 -1
- package/dist/esm/heatmap/track.js +87 -16
- package/dist/esm/index.d.ts +16 -63
- package/dist/esm/index.js +148 -83
- package/dist/esm/lifecycle.d.ts +21 -3
- package/dist/esm/lifecycle.js +359 -98
- 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 +16 -9
- package/dist/esm/remoteconfig/fetch.js +120 -39
- package/dist/esm/remoteconfig/index.d.ts +19 -8
- package/dist/esm/remoteconfig/index.js +82 -40
- package/dist/esm/remoteconfig/store.d.ts +15 -7
- package/dist/esm/remoteconfig/store.js +68 -36
- package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/esm/remoteconfig/tz-countries.js +11 -0
- package/dist/{cjs/core → esm/remotelog}/callsite.d.ts +11 -4
- package/dist/esm/{core → remotelog}/callsite.js +30 -8
- 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 +33 -61
- package/dist/esm/remotelog/send.js +13 -13
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.d.ts +3 -1
- package/dist/esm/scheduler.js +26 -24
- package/dist/esm/types.d.ts +208 -90
- package/dist/esm/user/index.js +18 -8
- package/dist/feedback.esm.min.js +2 -2
- package/dist/feedback.esm.min.js.map +1 -1
- package/dist/remoteconfig.esm.min.js +3 -0
- package/dist/remoteconfig.esm.min.js.map +1 -0
- package/dist/remotelogger.esm.min.js +3 -0
- package/dist/remotelogger.esm.min.js.map +1 -0
- package/package.json +16 -17
- 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/auto-init.d.ts +0 -8
- package/dist/cjs/auto-init.js +0 -32
- 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/auto-init.d.ts +0 -8
- package/dist/esm/auto-init.js +0 -29
- 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,11 +1,12 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
-
import {
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { AP_CONSTANTS, AP_LOG_MODULE } from '../core/constants.js';
|
|
3
3
|
import { _ZAP, getConfig } from '../core/state.js';
|
|
4
4
|
import { logFailure, Console } from '../core/logger.js';
|
|
5
|
-
import {
|
|
6
|
-
import { engagement_transactions,
|
|
5
|
+
import { isEmpty, getNewUUID, nowMs } from '../core/utils.js';
|
|
6
|
+
import { engagement_transactions, getKeyFromDeviceInfo, setDeviceInfoKey } from '../core/storage.js';
|
|
7
7
|
import { is_apptics_disabled, getNetworkStatus, getOrientation, getConnectionType, getRam } from '../core/env.js';
|
|
8
8
|
import { canTrackEngagement } from '../core/consent.js';
|
|
9
|
+
import { withLock } from '../core/locks.js';
|
|
9
10
|
|
|
10
11
|
/** The most recently started item of `type` that is still open (endtime 0). */
|
|
11
12
|
function latestOpen(type, items) {
|
|
@@ -56,16 +57,12 @@ function closeScreensOf(session, time) {
|
|
|
56
57
|
}, Promise.resolve());
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
|
-
/** `track_session: false`, Android's automaticSessionsTrackingStatus: no sessions are recorded. */
|
|
60
|
-
function sessionsOff() {
|
|
61
|
-
return getConfig('track_session') === false; // no i18n
|
|
62
|
-
}
|
|
63
60
|
/**
|
|
64
61
|
* Track the session.
|
|
65
62
|
* @param sessionData A set of properties belonging to the session.
|
|
66
63
|
*/
|
|
67
64
|
function trackSession(sessionData) {
|
|
68
|
-
if (!canTrackEngagement('trackSession')) { // no i18n
|
|
65
|
+
if (!canTrackEngagement('trackSession', AP_CONSTANTS.SESSION)) { // no i18n
|
|
69
66
|
return Promise.resolve();
|
|
70
67
|
}
|
|
71
68
|
if (sessionData && typeof sessionData !== 'object') { // no i18n
|
|
@@ -77,13 +74,16 @@ function trackSession(sessionData) {
|
|
|
77
74
|
Console.dir(sessionData);
|
|
78
75
|
return written;
|
|
79
76
|
}
|
|
80
|
-
/**
|
|
77
|
+
/**
|
|
78
|
+
* This tab's session while it is open, otherwise the newest open session in storage: the one the
|
|
79
|
+
* site's other tabs are on, which this tab joins rather than starting a second session beside it.
|
|
80
|
+
*/
|
|
81
81
|
function currentSession() {
|
|
82
82
|
var mine = tabSessionId();
|
|
83
83
|
return engagement_transactions.all().then(function (items) {
|
|
84
84
|
if (mine) {
|
|
85
85
|
var ours = items.filter(function (item) {
|
|
86
|
-
return item.type == AP_CONSTANTS.SESSION && item.id === mine;
|
|
86
|
+
return item.type == AP_CONSTANTS.SESSION && item.id === mine && item.endtime === 0;
|
|
87
87
|
})[0];
|
|
88
88
|
if (ours) {
|
|
89
89
|
return ours;
|
|
@@ -92,6 +92,119 @@ function currentSession() {
|
|
|
92
92
|
return latestOpen(AP_CONSTANTS.SESSION, items);
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* This page's own id, among the tabs sharing a session. New on every page load, so a reload or a
|
|
97
|
+
* duplicated tab counts as a tab of its own rather than as the one it came from. Made on first use,
|
|
98
|
+
* not when the module loads: getNewUUID reads `screen`, which a server-side render doesn't have.
|
|
99
|
+
*/
|
|
100
|
+
var tabIdValue = ''; // no i18n
|
|
101
|
+
function tabId() {
|
|
102
|
+
if (tabIdValue === '') {
|
|
103
|
+
tabIdValue = getNewUUID();
|
|
104
|
+
}
|
|
105
|
+
return tabIdValue;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The tabs of this site using a session. All of them share one session, which ends when the last
|
|
109
|
+
* of them closes (see endCurrentSession). Kept on the stored session, never sent: see
|
|
110
|
+
* sendEngagementData. A session stored by an older SDK has none, and counts as this tab's alone.
|
|
111
|
+
*/
|
|
112
|
+
function tabsOf(session) {
|
|
113
|
+
return Array.isArray(session.tabs) ? session.tabs.slice() : [];
|
|
114
|
+
}
|
|
115
|
+
function withThisTab(session) {
|
|
116
|
+
var tabs = tabsOf(session);
|
|
117
|
+
if (tabs.indexOf(tabId()) === -1) {
|
|
118
|
+
tabs.push(tabId());
|
|
119
|
+
}
|
|
120
|
+
return tabs;
|
|
121
|
+
}
|
|
122
|
+
/* ---------------- screen views left open by tabs that are gone ----------------
|
|
123
|
+
* A screen view is ended by the tab that opened it: by the next screen, or as the page hides or
|
|
124
|
+
* closes. A tab that reloads, closes or crashes before that write lands leaves its view open, and
|
|
125
|
+
* nothing ended it until the whole session did, which with tabs sharing one session can be hours.
|
|
126
|
+
*
|
|
127
|
+
* So each tab holds a Web Lock, `apptics-tab-<its id>`, for as long as it is open: the browser
|
|
128
|
+
* lets go of it the moment the tab is gone, however it went. A tab that starts, or comes back into
|
|
129
|
+
* view, ends every open view whose tab no longer holds its lock, at the time that tab was last seen
|
|
130
|
+
* active. Without the Web Locks API, views are left as before and end with their session. */
|
|
131
|
+
const TAB_LOCK_PREFIX = 'apptics-tab-'; // no i18n
|
|
132
|
+
/** A view this young may belong to a tab whose lock is not granted yet: left alone. */
|
|
133
|
+
const YOUNG_VIEW_MS = 5000;
|
|
134
|
+
function tabLocks() {
|
|
135
|
+
var locks = typeof navigator !== 'undefined' ? navigator.locks : undefined; // no i18n
|
|
136
|
+
return locks && typeof locks.request === 'function' && typeof locks.query === 'function' ? locks : undefined; // no i18n
|
|
137
|
+
}
|
|
138
|
+
var holdingTabLock = false;
|
|
139
|
+
/** Takes this tab's lock, held until the page is gone. Once per page. */
|
|
140
|
+
function holdTabLock() {
|
|
141
|
+
var locks = tabLocks();
|
|
142
|
+
if (!locks || holdingTabLock) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
holdingTabLock = true;
|
|
146
|
+
locks.request(TAB_LOCK_PREFIX + tabId(), function () {
|
|
147
|
+
return new Promise(function () { });
|
|
148
|
+
}).catch(function () {
|
|
149
|
+
holdingTabLock = false;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/** Ends the open screen views of tabs that are gone. Resolves right away without Web Locks. */
|
|
153
|
+
function closeScreensOfGoneTabs() {
|
|
154
|
+
var locks = tabLocks();
|
|
155
|
+
if (!locks) {
|
|
156
|
+
return Promise.resolve();
|
|
157
|
+
}
|
|
158
|
+
var mine = tabId();
|
|
159
|
+
return locks.query().then(function (state) {
|
|
160
|
+
var alive = {};
|
|
161
|
+
(state.held || []).concat(state.pending || []).forEach(function (lock) {
|
|
162
|
+
if (lock.name && lock.name.indexOf(TAB_LOCK_PREFIX) === 0) {
|
|
163
|
+
alive[lock.name.slice(TAB_LOCK_PREFIX.length)] = true;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
var now = nowMs();
|
|
167
|
+
return engagement_transactions.all().then(function (items) {
|
|
168
|
+
var gone = items.filter(function (item) {
|
|
169
|
+
return item.type == AP_CONSTANTS.SCREEN && item.endtime === 0 && typeof item.tab === 'string' // no i18n
|
|
170
|
+
&& item.tab !== mine && !alive[item.tab] && now - item.starttime > YOUNG_VIEW_MS;
|
|
171
|
+
});
|
|
172
|
+
if (gone.length) {
|
|
173
|
+
Console.log('closeScreensOfGoneTabs: ending ' + gone.length + ' screen view(s) left open by a tab that is gone'); // no i18n
|
|
174
|
+
}
|
|
175
|
+
return gone.reduce(function (done, item) {
|
|
176
|
+
// when that tab was last seen with the view open, not now: the time since is not time spent
|
|
177
|
+
var end = Math.max(item.starttime, Number(item.lastseen) || item.starttime);
|
|
178
|
+
return done.then(function () {
|
|
179
|
+
return engagement_transactions.update(Object.assign({}, item, { endtime: end }));
|
|
180
|
+
});
|
|
181
|
+
}, Promise.resolve());
|
|
182
|
+
});
|
|
183
|
+
}).catch(function (err) {
|
|
184
|
+
Console.log('closeScreensOfGoneTabs: ' + err); // no i18n
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
/** Notes on this tab's open view that the tab is still here, the end a gone tab's view is given. */
|
|
188
|
+
function markScreenSeen(time) {
|
|
189
|
+
var current = _ZAP._current_screen;
|
|
190
|
+
if (!current || !current.id || current.endtime) {
|
|
191
|
+
return Promise.resolve();
|
|
192
|
+
}
|
|
193
|
+
return engagement_transactions.all().then(function (items) {
|
|
194
|
+
var open = items.filter(function (item) {
|
|
195
|
+
return item.type == AP_CONSTANTS.SCREEN && item.id === current.id && item.endtime === 0;
|
|
196
|
+
})[0];
|
|
197
|
+
return open ? engagement_transactions.update(Object.assign({}, open, { lastseen: time })) : undefined;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Every change to the session runs under a lock shared by the site's tabs: each one reads the
|
|
202
|
+
* stored session, changes it and writes it back, and two tabs doing that at once would each write
|
|
203
|
+
* over the other's change.
|
|
204
|
+
*/
|
|
205
|
+
function withSessionLock(run) {
|
|
206
|
+
return withLock('session', run); // no i18n
|
|
207
|
+
}
|
|
95
208
|
/** The id of this tab's session. sessionStorage is per tab and survives a reload. */
|
|
96
209
|
const TAB_KEY = 'apptics-session'; // no i18n
|
|
97
210
|
function tabSessionId() {
|
|
@@ -110,9 +223,9 @@ function rememberTabSession(id) {
|
|
|
110
223
|
// the session still works, it just can't be recognised after a reload
|
|
111
224
|
}
|
|
112
225
|
}
|
|
113
|
-
/** True until the first session on this device has been recorded. */
|
|
226
|
+
/** True until the first session on this device has been recorded: device_info's hadsession. */
|
|
114
227
|
function isFirstEverSession() {
|
|
115
|
-
return
|
|
228
|
+
return getKeyFromDeviceInfo(HAD_SESSION_KEY) !== true;
|
|
116
229
|
}
|
|
117
230
|
const HAD_SESSION_KEY = 'hadsession'; // no i18n
|
|
118
231
|
/**
|
|
@@ -149,7 +262,7 @@ function commitPendingSession() {
|
|
|
149
262
|
// the flag is set here rather than at the start, so a dropped session does not consume it and
|
|
150
263
|
// leave the next real session reporting firstsession false
|
|
151
264
|
if (session.firstsession) {
|
|
152
|
-
|
|
265
|
+
setDeviceInfoKey(HAD_SESSION_KEY, true);
|
|
153
266
|
}
|
|
154
267
|
return trackSession(session);
|
|
155
268
|
}
|
|
@@ -174,6 +287,7 @@ function startNewSession(time) {
|
|
|
174
287
|
firstsession: isFirstEverSession(),
|
|
175
288
|
networkstatus: getNetworkStatus(),
|
|
176
289
|
lastactivetime: time,
|
|
290
|
+
tabs: [tabId()],
|
|
177
291
|
};
|
|
178
292
|
// ram is optional in sessionWebSDK but checked against ram_rom ('4 GB'), which an empty
|
|
179
293
|
// string cannot match. navigator.deviceMemory is Chromium-only, so Firefox and Safari have
|
|
@@ -200,25 +314,126 @@ function startNewSession(time) {
|
|
|
200
314
|
/** The un-deferred path, for a minimum of 0. */
|
|
201
315
|
function commitNow(session) {
|
|
202
316
|
if (session.firstsession) {
|
|
203
|
-
|
|
317
|
+
setDeviceInfoKey(HAD_SESSION_KEY, true);
|
|
204
318
|
}
|
|
205
319
|
return trackSession(session);
|
|
206
320
|
}
|
|
321
|
+
/** Whether this tab is inactive: hidden, minimised, or its window has lost focus. */
|
|
322
|
+
var inactive = false;
|
|
323
|
+
var inactivityTimer = null;
|
|
324
|
+
/**
|
|
325
|
+
* The tab went inactive. That moment becomes the session's lastactivetime, the time it ends at if
|
|
326
|
+
* the user does not come back within inactiveduration. If they do not, the session is ended at it
|
|
327
|
+
* then, and `onEnded` sends it, rather than staying open until they return or the tab closes.
|
|
328
|
+
* Going inactive twice (a window blur, then the tab hidden) keeps the first moment.
|
|
329
|
+
*/
|
|
330
|
+
function markTabInactive(onEnded) {
|
|
331
|
+
var first = !inactive;
|
|
332
|
+
inactive = true;
|
|
333
|
+
if (inactivityTimer !== null) {
|
|
334
|
+
clearTimeout(inactivityTimer);
|
|
335
|
+
}
|
|
336
|
+
inactivityTimer = setTimeout(function () {
|
|
337
|
+
inactivityTimer = null;
|
|
338
|
+
endInactiveSession().then(function (ended) {
|
|
339
|
+
if (ended) {
|
|
340
|
+
onEnded();
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}, _ZAP.inactiveduration * 60 * 1000 + 1000);
|
|
344
|
+
return first ? updateLastActiveTimeOfSession() : Promise.resolve();
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* The tab is active again. The caller then settles the session (checkAndUpdateSession): back
|
|
348
|
+
* within inactiveduration it continues, later it has ended and a new one starts.
|
|
349
|
+
*/
|
|
350
|
+
function markTabActive() {
|
|
351
|
+
inactive = false;
|
|
352
|
+
if (inactivityTimer !== null) {
|
|
353
|
+
clearTimeout(inactivityTimer);
|
|
354
|
+
inactivityTimer = null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/** reset(): no timer left running, and the tab counts as active. */
|
|
358
|
+
function resetInactivity() {
|
|
359
|
+
markTabActive();
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* inactiveduration has passed with the tab inactive: the session ends at its lastactivetime, when
|
|
363
|
+
* the user left, so the time away is not counted. Resolves true when it ended one. While another
|
|
364
|
+
* tab of the site is still on the session, this tab only leaves it; one whose lastactivetime is
|
|
365
|
+
* recent is in use elsewhere and is left alone.
|
|
366
|
+
*/
|
|
367
|
+
function endInactiveSession() {
|
|
368
|
+
if (!_ZAP._token || is_apptics_disabled()) {
|
|
369
|
+
return Promise.resolve(false);
|
|
370
|
+
}
|
|
371
|
+
var ended = false;
|
|
372
|
+
return withSessionLock(function () {
|
|
373
|
+
return currentSession().then(function (_currentSession) {
|
|
374
|
+
if (!_currentSession || _currentSession.endtime !== 0) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
var lastActive = Number(_currentSession.lastactivetime) || nowMs();
|
|
378
|
+
if (nowMs() - lastActive < _ZAP.inactiveduration * 60 * 1000) {
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
var others = tabsOf(_currentSession).filter(function (tab) { return tab !== tabId(); });
|
|
382
|
+
if (others.length > 0) {
|
|
383
|
+
var left = Object.assign({}, _currentSession, { tabs: others });
|
|
384
|
+
_ZAP._current_session = left;
|
|
385
|
+
return engagement_transactions.update(left);
|
|
386
|
+
}
|
|
387
|
+
var endAt = Math.max(lastActive, Number(_currentSession.starttime) || lastActive);
|
|
388
|
+
var closed = Object.assign({}, _currentSession, { endtime: endAt, tabs: [] });
|
|
389
|
+
_ZAP._current_session = closed;
|
|
390
|
+
ended = true;
|
|
391
|
+
Console.log('session: inactive for ' + _ZAP.inactiveduration + ' minutes, so it ends when the user left, ' + endAt); // no i18n
|
|
392
|
+
return engagement_transactions.update(closed).then(function () {
|
|
393
|
+
return closeScreensOf(closed, endAt);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
}).then(function () {
|
|
397
|
+
return ended;
|
|
398
|
+
});
|
|
399
|
+
}
|
|
207
400
|
/**
|
|
208
|
-
*
|
|
209
|
-
*
|
|
401
|
+
* This tab is closing, or the page is being replaced. The session ends here when this is the last
|
|
402
|
+
* tab using it, so it can be sent; while another tab of the site is still on it, this tab only
|
|
403
|
+
* leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
|
|
404
|
+
*
|
|
405
|
+
* A tab closed while inactive ends the session when the user left (its lastactivetime), not when
|
|
406
|
+
* the tab closed: the time it sat hidden or in the background is not session time.
|
|
210
407
|
*/
|
|
211
408
|
function endCurrentSession() {
|
|
212
409
|
var time = nowMs();
|
|
410
|
+
var wasInactive = inactive;
|
|
411
|
+
markTabActive();
|
|
213
412
|
// nothing was tracked and the minimum was never reached: this visit is not reported at all
|
|
214
413
|
if (dropPendingSession()) {
|
|
215
414
|
return Promise.resolve();
|
|
216
415
|
}
|
|
416
|
+
return withSessionLock(function () {
|
|
417
|
+
return closeOrLeaveSession(time, wasInactive);
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
function closeOrLeaveSession(now, wasInactive) {
|
|
217
421
|
return currentSession().then(function (_currentSession) {
|
|
218
422
|
if (!_currentSession || _currentSession.endtime !== 0) {
|
|
219
423
|
return;
|
|
220
424
|
}
|
|
221
|
-
var
|
|
425
|
+
var lastActive = Number(_currentSession.lastactivetime) || 0;
|
|
426
|
+
var time = wasInactive && lastActive > 0
|
|
427
|
+
? Math.max(Math.min(lastActive, now), Number(_currentSession.starttime) || 0)
|
|
428
|
+
: now;
|
|
429
|
+
var others = tabsOf(_currentSession).filter(function (tab) { return tab !== tabId(); });
|
|
430
|
+
if (others.length > 0) {
|
|
431
|
+
var left = Object.assign({}, _currentSession, { tabs: others, lastactivetime: time });
|
|
432
|
+
_ZAP._current_session = left;
|
|
433
|
+
Console.log('endCurrentSession: ' + others.length + ' other tab(s) still on this session, so it stays open'); // no i18n
|
|
434
|
+
return engagement_transactions.update(left);
|
|
435
|
+
}
|
|
436
|
+
var closed = Object.assign({}, _currentSession, { endtime: time, lastactivetime: time, tabs: [] });
|
|
222
437
|
_ZAP._current_session = closed;
|
|
223
438
|
Console.log('endCurrentSession: closing the session opened at ' + closed.starttime); // no i18n
|
|
224
439
|
return engagement_transactions.update(closed).then(function () {
|
|
@@ -255,18 +470,21 @@ function closeAbandonedSessions() {
|
|
|
255
470
|
}
|
|
256
471
|
function checkAndUpdateSession() {
|
|
257
472
|
if (!_ZAP._token) {
|
|
258
|
-
logFailure(AP_LOG_MODULE.SESSION, 'checkAndUpdateSession: You must call
|
|
473
|
+
logFailure(AP_LOG_MODULE.SESSION, 'checkAndUpdateSession: You must call Apptics.init() first'); // no i18n
|
|
259
474
|
return Promise.resolve();
|
|
260
475
|
}
|
|
261
476
|
if (is_apptics_disabled()) {
|
|
262
477
|
return Promise.resolve();
|
|
263
478
|
}
|
|
264
|
-
if (sessionsOff()) {
|
|
265
|
-
return Promise.resolve();
|
|
266
|
-
}
|
|
267
479
|
var time = nowMs();
|
|
268
|
-
|
|
269
|
-
|
|
480
|
+
return withSessionLock(function () {
|
|
481
|
+
return settleSession(time);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
function settleSession(time) {
|
|
485
|
+
// a tab that was killed before it could close its session: close it so it gets sent; and the
|
|
486
|
+
// screen views left open by tabs that are gone, so they end now rather than with the session
|
|
487
|
+
return closeAbandonedSessions().then(closeScreensOfGoneTabs).then(currentSession).then(function (_currentSession) {
|
|
270
488
|
// a closed session belongs to a tab that has gone: this tab starts its own
|
|
271
489
|
if (!_currentSession || _currentSession.endtime !== 0) {
|
|
272
490
|
return startNewSession(time);
|
|
@@ -287,13 +505,14 @@ function checkAndUpdateSession() {
|
|
|
287
505
|
});
|
|
288
506
|
}
|
|
289
507
|
Console.log('checkAndUpdateSession: User is back before ' + _ZAP.inactiveduration + ' minutes so updating the lastactivitytime - ' + time); // no i18n
|
|
290
|
-
return
|
|
508
|
+
return touchSession();
|
|
291
509
|
});
|
|
292
510
|
}
|
|
293
511
|
function updateLastActiveTimeOfSession() {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
512
|
+
return withSessionLock(touchSession);
|
|
513
|
+
}
|
|
514
|
+
/** Marks the session active now, with this tab among its tabs. Callers hold the session lock. */
|
|
515
|
+
function touchSession() {
|
|
297
516
|
var time = nowMs();
|
|
298
517
|
// a pending session is not in the store yet, so it is updated in place instead
|
|
299
518
|
if (pendingSession) {
|
|
@@ -306,10 +525,12 @@ function updateLastActiveTimeOfSession() {
|
|
|
306
525
|
return;
|
|
307
526
|
}
|
|
308
527
|
Console.log('updateLastActiveTimeOfSession: Updating the lastactivitytime - ' + time); // no i18n
|
|
309
|
-
var _newSession = Object.assign({}, _currentSession, { lastactivetime: time });
|
|
528
|
+
var _newSession = Object.assign({}, _currentSession, { lastactivetime: time, tabs: withThisTab(_currentSession) });
|
|
310
529
|
_ZAP._current_session = _newSession;
|
|
311
530
|
return engagement_transactions.update(_newSession);
|
|
531
|
+
}).then(function () {
|
|
532
|
+
return markScreenSeen(time);
|
|
312
533
|
});
|
|
313
534
|
}
|
|
314
535
|
|
|
315
|
-
export { checkAndUpdateSession, closeAbandonedSessions, closeScreen, commitPendingSession, currentSession, dropPendingSession, endCurrentSession, startNewSession, trackSession, updateLastActiveTimeOfSession, updateScreenWithEndtime };
|
|
536
|
+
export { checkAndUpdateSession, closeAbandonedSessions, closeScreen, closeScreensOfGoneTabs, commitPendingSession, currentSession, dropPendingSession, endCurrentSession, holdTabLock, markTabActive, markTabInactive, resetInactivity, startNewSession, tabId, trackSession, updateLastActiveTimeOfSession, updateScreenWithEndtime };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Dict, Properties } from '../types.js';
|
|
2
2
|
import { type DefinedEvent } from './defined-events.js';
|
|
3
3
|
/**
|
|
4
|
-
* Records an event: `trackEvent(name, group
|
|
4
|
+
* Records an event: `trackEvent(name, group, properties?)`, or a defined event with
|
|
5
5
|
* `trackEvent(DefinedEvents.AP_USER_LOGIN, properties?)`, which brings its own group.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* The group is required. An event without one, or with an invalid name or group, is not logged,
|
|
8
|
+
* and the console says why (debug or not): filing it under a group the app never named would hide
|
|
9
|
+
* the mistake.
|
|
10
10
|
*/
|
|
11
11
|
export declare function trackEvent(eventName: string | DefinedEvent, groupName?: string | Properties, jsonObj?: Properties): void;
|
|
12
12
|
/** The app's own screen view. Automatic screen tracking calls recordScreen() instead. */
|
|
@@ -1,29 +1,43 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
-
import { AP_LOG_MODULE,
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
|
+
import { AP_LOG_MODULE, AP_CONSTANTS } from '../core/constants.js';
|
|
3
3
|
import { _ZAP } from '../core/state.js';
|
|
4
|
-
import { logFailure, Console } from '../core/logger.js';
|
|
5
|
-
import {
|
|
4
|
+
import { logFailure, Console, warnAlways } from '../core/logger.js';
|
|
5
|
+
import { isEmpty, nowMs, getNewUUID } from '../core/utils.js';
|
|
6
6
|
import { engagement_transactions } from '../core/storage.js';
|
|
7
|
-
import { commitPendingSession,
|
|
8
|
-
import { getConnectionType,
|
|
7
|
+
import { closeScreen, commitPendingSession, tabId } from './session.js';
|
|
8
|
+
import { getConnectionType, getOrientation, getNetworkStatus } from '../core/env.js';
|
|
9
9
|
import { canTrackEngagement } from '../core/consent.js';
|
|
10
|
-
import { validateEventOrGroup, validateJSON
|
|
10
|
+
import { validateScreenName, validateEventOrGroup, validateJSON } from '../core/validate.js';
|
|
11
11
|
import { noteDebugActivity } from '../core/debug.js';
|
|
12
12
|
import { hooks } from '../core/hooks.js';
|
|
13
|
-
import {
|
|
13
|
+
import { urlTracked, normalizeApiPath } from './api-rules.js';
|
|
14
14
|
import { asDefinedEvent } from './defined-events.js';
|
|
15
15
|
import { automaticScreenName } from './auto-screen.js';
|
|
16
16
|
|
|
17
|
+
/** Messages already printed, so a name refused in a loop says so once. */
|
|
18
|
+
var refusedSaid = {};
|
|
17
19
|
/**
|
|
18
|
-
*
|
|
20
|
+
* A call the SDK refused because of what the app passed it (an invalid event, group or screen
|
|
21
|
+
* name): reported as every failure is, and said in the console too, debug or not, once per
|
|
22
|
+
* message. Nothing else tells an integrator why the event or screen never shows up.
|
|
23
|
+
*/
|
|
24
|
+
function refused(module, message) {
|
|
25
|
+
logFailure(module, message);
|
|
26
|
+
if (!refusedSaid[message]) {
|
|
27
|
+
refusedSaid[message] = true;
|
|
28
|
+
warnAlways(message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Records an event: `trackEvent(name, group, properties?)`, or a defined event with
|
|
19
33
|
* `trackEvent(DefinedEvents.AP_USER_LOGIN, properties?)`, which brings its own group.
|
|
20
34
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
35
|
+
* The group is required. An event without one, or with an invalid name or group, is not logged,
|
|
36
|
+
* and the console says why (debug or not): filing it under a group the app never named would hide
|
|
37
|
+
* the mistake.
|
|
24
38
|
*/
|
|
25
39
|
function trackEvent(eventName, groupName, jsonObj) {
|
|
26
|
-
if (!canTrackEngagement('trackEvent')) { // no i18n
|
|
40
|
+
if (!canTrackEngagement('trackEvent', AP_CONSTANTS.EVENT)) { // no i18n
|
|
27
41
|
return;
|
|
28
42
|
}
|
|
29
43
|
var defined = asDefinedEvent(eventName);
|
|
@@ -35,14 +49,17 @@ function trackEvent(eventName, groupName, jsonObj) {
|
|
|
35
49
|
}
|
|
36
50
|
else {
|
|
37
51
|
if (typeof eventName !== 'string' || !validateEventOrGroup(eventName)) { // no i18n
|
|
38
|
-
|
|
52
|
+
refused(AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: Invalid eventName: ' + String(eventName)); // no i18n
|
|
39
53
|
return;
|
|
40
54
|
}
|
|
41
|
-
|
|
42
|
-
|
|
55
|
+
// the group is required: an event without one (or with its properties in the group's
|
|
56
|
+
// place) is not logged, and the console says why
|
|
57
|
+
if (groupName === undefined || groupName === null || groupName === '' || typeof groupName === 'object') { // no i18n
|
|
58
|
+
refused(AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: groupName is required for "' + eventName + '": addEvent(eventName, groupName, properties?)'); // no i18n
|
|
59
|
+
return;
|
|
43
60
|
}
|
|
44
|
-
|
|
45
|
-
|
|
61
|
+
if (typeof groupName !== 'string' || !validateEventOrGroup(groupName)) { // no i18n
|
|
62
|
+
refused(AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: Invalid groupName for "' + eventName + '": ' + String(groupName)); // no i18n
|
|
46
63
|
return;
|
|
47
64
|
}
|
|
48
65
|
}
|
|
@@ -68,7 +85,7 @@ function trackEvent(eventName, groupName, jsonObj) {
|
|
|
68
85
|
commitPendingSession();
|
|
69
86
|
noteDebugActivity();
|
|
70
87
|
engagement_transactions.add(eventData);
|
|
71
|
-
Console.log('
|
|
88
|
+
Console.log('AppticsEvent.addEvent(\'' + eventName + '\',' + '\'' + groupName + '\'' + ',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
|
|
72
89
|
Console.dir(eventData);
|
|
73
90
|
}
|
|
74
91
|
/**
|
|
@@ -78,7 +95,7 @@ function trackEvent(eventName, groupName, jsonObj) {
|
|
|
78
95
|
*/
|
|
79
96
|
function inScreenAt(screenName, jsonObj, id, startedAt) {
|
|
80
97
|
if (!validateScreenName(screenName)) {
|
|
81
|
-
|
|
98
|
+
refused(AP_LOG_MODULE.SCREEN, 'AppticsScreen.inScreen: Invalid screenName: ' + String(screenName)); // no i18n
|
|
82
99
|
return;
|
|
83
100
|
}
|
|
84
101
|
// the app named this screen itself: the automatic view of the same navigation gives way to it
|
|
@@ -88,7 +105,7 @@ function inScreenAt(screenName, jsonObj, id, startedAt) {
|
|
|
88
105
|
/** Ends the screen view inScreen() began, as Android's AppticsScreenTracker.outScreen(id). */
|
|
89
106
|
function outScreenAt(id, endedAt) {
|
|
90
107
|
if (isEmpty(id)) {
|
|
91
|
-
logFailure(AP_LOG_MODULE.SCREEN, '
|
|
108
|
+
logFailure(AP_LOG_MODULE.SCREEN, 'AppticsScreen.outScreen: no screen id given'); // no i18n
|
|
92
109
|
return;
|
|
93
110
|
}
|
|
94
111
|
closeScreen(id, endedAt);
|
|
@@ -101,11 +118,11 @@ function outScreenAt(id, endedAt) {
|
|
|
101
118
|
* An open view is not sent until it is closed, as an open session is not.
|
|
102
119
|
*/
|
|
103
120
|
function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
|
|
104
|
-
if (!canTrackEngagement('inScreen')) { // no i18n
|
|
121
|
+
if (!canTrackEngagement('inScreen', AP_CONSTANTS.SCREEN)) { // no i18n
|
|
105
122
|
return;
|
|
106
123
|
}
|
|
107
124
|
if (!validateScreenName(screenName)) {
|
|
108
|
-
|
|
125
|
+
refused(AP_LOG_MODULE.SCREEN, 'AppticsScreen.inScreen: Invalid screenName: ' + String(screenName)); // no i18n
|
|
109
126
|
return;
|
|
110
127
|
}
|
|
111
128
|
jsonObj = jsonObj || {};
|
|
@@ -122,7 +139,10 @@ function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
|
|
|
122
139
|
networkstatus: getNetworkStatus(),
|
|
123
140
|
orientation: getOrientation(),
|
|
124
141
|
sessionstarttime: _ZAP._current_session.starttime || time,
|
|
125
|
-
customproperties: _jsonObj
|
|
142
|
+
customproperties: _jsonObj,
|
|
143
|
+
// the tab showing it, so a tab that is gone can have its view ended (closeScreensOfGoneTabs).
|
|
144
|
+
// SDK bookkeeping, removed before sending, as lastseen is
|
|
145
|
+
tab: tabId(),
|
|
126
146
|
};
|
|
127
147
|
// the screen being left ends where this one starts
|
|
128
148
|
var previous = _ZAP._current_screen && _ZAP._current_screen.id;
|
|
@@ -135,7 +155,7 @@ function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
|
|
|
135
155
|
// `implicit`: a view recorded only so a heatmap record has one to belong to. Kept on the
|
|
136
156
|
// in-memory copy alone, never on the stored item, which goes to the server as it is
|
|
137
157
|
_ZAP._current_screen = implicit ? Object.assign({}, screenData, { implicit: true }) : screenData;
|
|
138
|
-
Console.log('
|
|
158
|
+
Console.log('AppticsScreen.inScreen(\'' + screenName + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
|
|
139
159
|
Console.dir(screenData);
|
|
140
160
|
}
|
|
141
161
|
/**
|
|
@@ -194,7 +214,7 @@ function withoutQueryForLog(path) {
|
|
|
194
214
|
return String(path || '').split(/[?#]/)[0]; // no i18n
|
|
195
215
|
}
|
|
196
216
|
function trackApi(jsonObj) {
|
|
197
|
-
if (!canTrackEngagement('trackApi')) { // no i18n
|
|
217
|
+
if (!canTrackEngagement('trackApi', AP_CONSTANTS.API)) { // no i18n
|
|
198
218
|
return;
|
|
199
219
|
}
|
|
200
220
|
jsonObj = jsonObj || {};
|
|
@@ -224,16 +244,19 @@ function trackApi(jsonObj) {
|
|
|
224
244
|
var hostname = jsonObj.hostname || '';
|
|
225
245
|
var port = (typeof jsonObj.port === 'number') ? jsonObj.port : 0;
|
|
226
246
|
var path = jsonObj.path || '';
|
|
247
|
+
// a call reported by its parts, a hostname and a path with no url: its scheme and port are its
|
|
248
|
+
// own (https and 443 unless given), not the page's, which belong to another host
|
|
249
|
+
var partsOnly = isEmpty(jsonObj.url) && !isEmpty(jsonObj.hostname);
|
|
227
250
|
if (!isEmpty(rawUrl)) {
|
|
228
251
|
try {
|
|
229
252
|
var urlObj = new URL(rawUrl, window.location.origin);
|
|
230
|
-
if (isEmpty(scheme)) {
|
|
253
|
+
if (isEmpty(scheme) && !partsOnly) {
|
|
231
254
|
scheme = urlObj.protocol.replace(':', ''); // "https:"
|
|
232
255
|
}
|
|
233
256
|
if (isEmpty(hostname)) {
|
|
234
257
|
hostname = urlObj.hostname;
|
|
235
258
|
}
|
|
236
|
-
if (!port) {
|
|
259
|
+
if (!port && !partsOnly) {
|
|
237
260
|
if (urlObj.port) {
|
|
238
261
|
port = parseInt(urlObj.port, 10);
|
|
239
262
|
}
|
|
@@ -254,22 +277,27 @@ function trackApi(jsonObj) {
|
|
|
254
277
|
}
|
|
255
278
|
// only http and https exist as far as the proxy's api_scheme is concerned
|
|
256
279
|
scheme = (scheme === 'https' || scheme === 'http') ? scheme : 'https'; // no i18n
|
|
280
|
+
if (!port) {
|
|
281
|
+
port = scheme === 'https' ? 443 : 80; // no i18n
|
|
282
|
+
}
|
|
257
283
|
// a hostname the proxy refuses cannot be repaired without inventing one, so the call is
|
|
258
284
|
// dropped here and said out loud. localhost and bare IPs land here: api_hostname wants at
|
|
259
285
|
// least one dot and an alphabetic suffix, which is worth knowing when nothing shows up locally
|
|
260
286
|
if (!API_HOSTNAME.test(hostname)) {
|
|
261
|
-
logFailure(AP_LOG_MODULE.API, '
|
|
287
|
+
logFailure(AP_LOG_MODULE.API, 'AppticsApi: hostname not accepted by the server: ' + String(hostname)); // no i18n
|
|
262
288
|
return;
|
|
263
289
|
}
|
|
264
|
-
// configureApiTracking: the app's rules decide whether this call is recorded at all
|
|
265
|
-
|
|
266
|
-
|
|
290
|
+
// configureApiTracking: the app's rules decide whether this call is recorded at all, tried on
|
|
291
|
+
// the URL as it was called, without its query
|
|
292
|
+
var defaultPort = (scheme === 'https' && port === 443) || (scheme === 'http' && port === 80); // no i18n
|
|
293
|
+
var calledUrl = scheme + '://' + hostname + (port && !defaultPort ? ':' + port : '') + (String(path).charAt(0) === '/' ? '' : '/') + path; // no i18n
|
|
294
|
+
if (!urlTracked(calledUrl)) {
|
|
295
|
+
Console.log('AppticsApi: ' + hostname + withoutQueryForLog(path) + ' is left out by configureApiTracking'); // no i18n
|
|
267
296
|
return;
|
|
268
297
|
}
|
|
269
|
-
// and the name it is recorded under: ids become *,
|
|
270
|
-
//
|
|
298
|
+
// and the name it is recorded under: ids become *, so the console groups calls by endpoint
|
|
299
|
+
// rather than by every id that went through it
|
|
271
300
|
path = sanitizeApiPath(normalizeApiPath(path));
|
|
272
|
-
hostname = groupedHost(hostname);
|
|
273
301
|
var sessionStart = _ZAP._current_session.starttime || starttime;
|
|
274
302
|
// no customproperties: apiWebSDK does not declare the key, unlike the screen and event
|
|
275
303
|
// templates, so anything extra the caller passes is context for the log line only
|
|
@@ -294,7 +322,7 @@ function trackApi(jsonObj) {
|
|
|
294
322
|
commitPendingSession();
|
|
295
323
|
noteDebugActivity();
|
|
296
324
|
engagement_transactions.add(apiData);
|
|
297
|
-
Console.log('
|
|
325
|
+
Console.log('AppticsApi(\'' + method + ' ' + scheme + '://' + hostname + path + '\')'); // no i18n
|
|
298
326
|
Console.dir(apiData);
|
|
299
327
|
}
|
|
300
328
|
/** Begun calls by id. Bounded, so an app that starts calls it never ends can't grow it forever. */
|
|
@@ -322,7 +350,7 @@ function startTrackApi(url, method) {
|
|
|
322
350
|
function endTrackApiAt(id, responseCode, responseMessage, endedAt) {
|
|
323
351
|
var open = openCalls[id];
|
|
324
352
|
if (!open) {
|
|
325
|
-
logFailure(AP_LOG_MODULE.API, '
|
|
353
|
+
logFailure(AP_LOG_MODULE.API, 'AppticsApi.endTrackApi: no call started with id ' + String(id)); // no i18n
|
|
326
354
|
return;
|
|
327
355
|
}
|
|
328
356
|
delete openCalls[id];
|
package/dist/esm/errors/send.js
CHANGED
|
@@ -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.2 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
import { _ZAP } from '../core/state.js';
|
|
3
3
|
import { Console, warnAlways } from '../core/logger.js';
|
|
4
4
|
import { cloneObject } from '../core/utils.js';
|
|
@@ -12,7 +12,7 @@ import { isPermanentRefusal } from '../core/transport.js';
|
|
|
12
12
|
* Sends pending errors to the Apptics API.
|
|
13
13
|
*/
|
|
14
14
|
function sendErrorData(forget) {
|
|
15
|
-
//
|
|
15
|
+
// js_error off (getsdkconfig): queued errors stay until tracking is back on or they expire
|
|
16
16
|
if (is_apptics_disabled() || !shouldTrackOrSendData() || !_ZAP._errorTracking) {
|
|
17
17
|
return Promise.resolve();
|
|
18
18
|
}
|