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