@zoho_apptics/apptics-js-sdk 2.0.0-beta → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +111 -5
- package/README.md +399 -300
- package/dist/apptics.esm.min.js +2 -2
- package/dist/apptics.esm.min.js.map +1 -1
- package/dist/apptics.min.js +2 -2
- package/dist/apptics.min.js.map +1 -1
- package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
- package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
- package/dist/cjs/auto-init.d.ts +3 -3
- package/dist/cjs/auto-init.js +1 -1
- package/dist/cjs/core/attempts.js +1 -1
- package/dist/cjs/core/batch.js +2 -1
- package/dist/cjs/core/callsite.d.ts +6 -0
- package/dist/cjs/core/callsite.js +25 -3
- package/dist/cjs/core/consent.d.ts +5 -1
- package/dist/cjs/core/consent.js +8 -6
- package/dist/cjs/core/constants.d.ts +35 -10
- package/dist/cjs/core/constants.js +31 -15
- package/dist/cjs/core/db.d.ts +12 -4
- package/dist/cjs/core/db.js +167 -34
- package/dist/cjs/core/debug.js +1 -1
- package/dist/cjs/core/env.js +1 -1
- package/dist/cjs/core/framework-id.d.ts +1 -1
- package/dist/cjs/core/framework-id.js +3 -3
- package/dist/cjs/core/guard.d.ts +9 -0
- package/dist/cjs/core/guard.js +27 -13
- package/dist/cjs/core/hooks.d.ts +2 -4
- package/dist/cjs/core/hooks.js +2 -3
- package/dist/cjs/core/locks.d.ts +9 -0
- package/dist/cjs/core/locks.js +22 -0
- package/dist/cjs/core/logger.d.ts +3 -4
- package/dist/cjs/core/logger.js +4 -40
- package/dist/cjs/core/state.d.ts +3 -2
- package/dist/cjs/core/state.js +2 -2
- package/dist/cjs/core/storage.d.ts +20 -5
- package/dist/cjs/core/storage.js +115 -17
- package/dist/cjs/core/stylesheet.d.ts +8 -0
- package/dist/cjs/core/stylesheet.js +29 -0
- package/dist/cjs/core/transport.d.ts +2 -0
- package/dist/cjs/core/transport.js +19 -4
- package/dist/cjs/core/utils.js +1 -1
- package/dist/cjs/core/validate.js +2 -2
- package/dist/cjs/device/appversion.d.ts +1 -6
- package/dist/cjs/device/appversion.js +13 -1
- package/dist/cjs/device/index.d.ts +11 -0
- package/dist/cjs/device/index.js +120 -20
- package/dist/cjs/engagement/api-rules.d.ts +32 -67
- package/dist/cjs/engagement/api-rules.js +144 -157
- package/dist/cjs/engagement/auto-api.js +2 -2
- package/dist/cjs/engagement/auto-screen.d.ts +2 -0
- package/dist/cjs/engagement/auto-screen.js +67 -3
- package/dist/cjs/engagement/defined-events.js +1 -1
- package/dist/cjs/engagement/send.js +6 -1
- package/dist/cjs/engagement/session.d.ts +12 -3
- package/dist/cjs/engagement/session.js +161 -25
- package/dist/cjs/engagement/track.js +45 -20
- package/dist/cjs/errors/send.js +1 -1
- package/dist/cjs/errors/track.d.ts +1 -1
- package/dist/cjs/errors/track.js +46 -6
- package/dist/cjs/feedback/controls.d.ts +6 -0
- package/dist/cjs/feedback/controls.js +31 -1
- package/dist/cjs/feedback/index.d.ts +8 -7
- package/dist/cjs/feedback/index.js +32 -13
- package/dist/cjs/feedback/logs.d.ts +10 -5
- package/dist/cjs/feedback/logs.js +52 -13
- package/dist/cjs/feedback/quartz.d.ts +18 -0
- package/dist/cjs/feedback/quartz.js +105 -0
- package/dist/cjs/feedback/send.d.ts +3 -0
- package/dist/cjs/feedback/send.js +28 -3
- package/dist/cjs/feedback/styles.d.ts +10 -0
- package/dist/cjs/feedback/styles.js +8 -0
- package/dist/cjs/feedback/template.d.ts +1 -1
- package/dist/cjs/feedback/template.js +12 -12
- package/dist/cjs/feedback/ui.d.ts +14 -6
- package/dist/cjs/feedback/ui.js +126 -48
- package/dist/cjs/heatmap/send.js +1 -1
- package/dist/cjs/heatmap/track.d.ts +1 -1
- package/dist/cjs/heatmap/track.js +87 -16
- package/dist/cjs/index.d.ts +17 -63
- package/dist/cjs/index.js +104 -92
- package/dist/cjs/lifecycle.d.ts +8 -1
- package/dist/cjs/lifecycle.js +258 -62
- package/dist/cjs/remoteconfig/country.d.ts +4 -0
- package/dist/cjs/remoteconfig/country.js +73 -0
- package/dist/cjs/remoteconfig/custom.d.ts +7 -7
- package/dist/cjs/remoteconfig/custom.js +31 -51
- package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
- package/dist/cjs/remoteconfig/evaluate.js +56 -43
- package/dist/cjs/remoteconfig/fetch.d.ts +3 -3
- package/dist/cjs/remoteconfig/fetch.js +9 -23
- package/dist/cjs/remoteconfig/index.d.ts +10 -8
- package/dist/cjs/remoteconfig/index.js +65 -39
- package/dist/cjs/remoteconfig/store.js +1 -1
- package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/cjs/remoteconfig/tz-countries.js +13 -0
- package/dist/cjs/remotelog/gate.d.ts +2 -11
- package/dist/cjs/remotelog/gate.js +9 -35
- package/dist/cjs/remotelog/index.d.ts +4 -21
- package/dist/cjs/remotelog/logger.d.ts +11 -17
- package/dist/cjs/remotelog/logger.js +30 -58
- package/dist/cjs/remotelog/send.js +10 -10
- package/dist/cjs/remotelogger/index.d.ts +12 -0
- package/dist/cjs/remotelogger/index.js +18 -0
- package/dist/cjs/scheduler.js +2 -2
- package/dist/cjs/types.d.ts +155 -82
- package/dist/cjs/user/index.js +11 -6
- package/dist/esm/auto-init.d.ts +3 -3
- package/dist/esm/auto-init.js +1 -1
- package/dist/esm/core/attempts.js +1 -1
- package/dist/esm/core/batch.js +2 -1
- package/dist/esm/core/callsite.d.ts +6 -0
- package/dist/esm/core/callsite.js +25 -4
- package/dist/esm/core/consent.d.ts +5 -1
- package/dist/esm/core/consent.js +9 -7
- package/dist/esm/core/constants.d.ts +35 -10
- package/dist/esm/core/constants.js +30 -15
- package/dist/esm/core/db.d.ts +12 -4
- package/dist/esm/core/db.js +164 -35
- package/dist/esm/core/debug.js +1 -1
- package/dist/esm/core/env.js +1 -1
- package/dist/esm/core/framework-id.d.ts +1 -1
- package/dist/esm/core/framework-id.js +3 -3
- package/dist/esm/core/guard.d.ts +9 -0
- package/dist/esm/core/guard.js +27 -14
- package/dist/esm/core/hooks.d.ts +2 -4
- package/dist/esm/core/hooks.js +2 -3
- package/dist/esm/core/locks.d.ts +9 -0
- package/dist/esm/core/locks.js +20 -0
- package/dist/esm/core/logger.d.ts +3 -4
- package/dist/esm/core/logger.js +4 -40
- package/dist/esm/core/state.d.ts +3 -2
- package/dist/esm/core/state.js +2 -2
- package/dist/esm/core/storage.d.ts +20 -5
- package/dist/esm/core/storage.js +113 -18
- package/dist/esm/core/stylesheet.d.ts +8 -0
- package/dist/esm/core/stylesheet.js +27 -0
- package/dist/esm/core/transport.d.ts +2 -0
- package/dist/esm/core/transport.js +19 -5
- package/dist/esm/core/utils.js +1 -1
- package/dist/esm/core/validate.js +2 -2
- package/dist/esm/device/appversion.d.ts +1 -6
- package/dist/esm/device/appversion.js +13 -2
- package/dist/esm/device/index.d.ts +11 -0
- package/dist/esm/device/index.js +123 -24
- package/dist/esm/engagement/api-rules.d.ts +32 -67
- package/dist/esm/engagement/api-rules.js +143 -155
- package/dist/esm/engagement/auto-api.js +3 -3
- package/dist/esm/engagement/auto-screen.d.ts +2 -0
- package/dist/esm/engagement/auto-screen.js +67 -4
- package/dist/esm/engagement/defined-events.js +1 -1
- package/dist/esm/engagement/send.js +6 -1
- package/dist/esm/engagement/session.d.ts +12 -3
- package/dist/esm/engagement/session.js +162 -29
- package/dist/esm/engagement/track.js +52 -27
- package/dist/esm/errors/send.js +1 -1
- package/dist/esm/errors/track.d.ts +1 -1
- package/dist/esm/errors/track.js +46 -6
- package/dist/esm/feedback/controls.d.ts +6 -0
- package/dist/esm/feedback/controls.js +31 -2
- package/dist/esm/feedback/index.d.ts +8 -7
- package/dist/esm/feedback/index.js +34 -13
- package/dist/esm/feedback/logs.d.ts +10 -5
- package/dist/esm/feedback/logs.js +54 -15
- package/dist/esm/feedback/quartz.d.ts +18 -0
- package/dist/esm/feedback/quartz.js +99 -0
- package/dist/esm/feedback/send.d.ts +3 -0
- package/dist/esm/feedback/send.js +28 -5
- package/dist/esm/feedback/styles.d.ts +10 -0
- package/dist/esm/feedback/styles.js +4 -0
- package/dist/esm/feedback/template.d.ts +1 -1
- package/dist/esm/feedback/template.js +12 -12
- package/dist/esm/feedback/ui.d.ts +14 -6
- package/dist/esm/feedback/ui.js +126 -49
- package/dist/esm/heatmap/send.js +1 -1
- package/dist/esm/heatmap/track.d.ts +1 -1
- package/dist/esm/heatmap/track.js +87 -16
- package/dist/esm/index.d.ts +17 -63
- package/dist/esm/index.js +108 -80
- package/dist/esm/lifecycle.d.ts +8 -1
- package/dist/esm/lifecycle.js +266 -71
- package/dist/esm/remoteconfig/country.d.ts +4 -0
- package/dist/esm/remoteconfig/country.js +70 -0
- package/dist/esm/remoteconfig/custom.d.ts +7 -7
- package/dist/esm/remoteconfig/custom.js +32 -52
- package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
- package/dist/esm/remoteconfig/evaluate.js +57 -44
- package/dist/esm/remoteconfig/fetch.d.ts +3 -3
- package/dist/esm/remoteconfig/fetch.js +9 -23
- package/dist/esm/remoteconfig/index.d.ts +10 -8
- package/dist/esm/remoteconfig/index.js +66 -40
- package/dist/esm/remoteconfig/store.js +1 -1
- package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
- package/dist/esm/remoteconfig/tz-countries.js +11 -0
- package/dist/esm/remotelog/gate.d.ts +2 -11
- package/dist/esm/remotelog/gate.js +10 -32
- package/dist/esm/remotelog/index.d.ts +4 -21
- package/dist/esm/remotelog/logger.d.ts +11 -17
- package/dist/esm/remotelog/logger.js +32 -60
- package/dist/esm/remotelog/send.js +12 -12
- package/dist/esm/remotelogger/index.d.ts +12 -0
- package/dist/esm/remotelogger/index.js +13 -0
- package/dist/esm/scheduler.js +2 -2
- package/dist/esm/types.d.ts +155 -82
- package/dist/esm/user/index.js +12 -6
- package/dist/feedback.esm.min.js +2 -2
- package/dist/feedback.esm.min.js.map +1 -1
- package/package.json +14 -16
- package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
- package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
- package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
- package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
- package/dist/cjs/heatmap/index.d.ts +0 -16
- package/dist/cjs/heatmap/index.js +0 -22
- package/dist/cjs/remotelog/index.js +0 -55
- package/dist/esm/heatmap/index.d.ts +0 -16
- package/dist/esm/heatmap/index.js +0 -17
- package/dist/esm/remotelog/index.js +0 -51
- package/dist/heatmap.esm.min.js +0 -3
- package/dist/heatmap.esm.min.js.map +0 -1
- package/dist/style.css +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'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,10 +59,6 @@ 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.
|
|
@@ -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,13 +316,14 @@ 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
|
}
|
|
209
323
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
324
|
+
* This tab is closing, or the page is being replaced. The session ends here when this is the last
|
|
325
|
+
* tab using it, so it can be sent; while another tab of the site is still on it, this tab only
|
|
326
|
+
* leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
|
|
212
327
|
*/
|
|
213
328
|
function endCurrentSession() {
|
|
214
329
|
var time = utils.nowMs();
|
|
@@ -216,11 +331,23 @@ function endCurrentSession() {
|
|
|
216
331
|
if (dropPendingSession()) {
|
|
217
332
|
return Promise.resolve();
|
|
218
333
|
}
|
|
334
|
+
return withSessionLock(function () {
|
|
335
|
+
return closeOrLeaveSession(time);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
function closeOrLeaveSession(time) {
|
|
219
339
|
return currentSession().then(function (_currentSession) {
|
|
220
340
|
if (!_currentSession || _currentSession.endtime !== 0) {
|
|
221
341
|
return;
|
|
222
342
|
}
|
|
223
|
-
var
|
|
343
|
+
var others = tabsOf(_currentSession).filter(function (tab) { return tab !== tabId(); });
|
|
344
|
+
if (others.length > 0) {
|
|
345
|
+
var left = Object.assign({}, _currentSession, { tabs: others, lastactivetime: time });
|
|
346
|
+
state._ZAP._current_session = left;
|
|
347
|
+
logger.Console.log('endCurrentSession: ' + others.length + ' other tab(s) still on this session, so it stays open'); // no i18n
|
|
348
|
+
return storage.engagement_transactions.update(left);
|
|
349
|
+
}
|
|
350
|
+
var closed = Object.assign({}, _currentSession, { endtime: time, lastactivetime: time, tabs: [] });
|
|
224
351
|
state._ZAP._current_session = closed;
|
|
225
352
|
logger.Console.log('endCurrentSession: closing the session opened at ' + closed.starttime); // no i18n
|
|
226
353
|
return storage.engagement_transactions.update(closed).then(function () {
|
|
@@ -257,18 +384,21 @@ function closeAbandonedSessions() {
|
|
|
257
384
|
}
|
|
258
385
|
function checkAndUpdateSession() {
|
|
259
386
|
if (!state._ZAP._token) {
|
|
260
|
-
logger.logFailure(constants.AP_LOG_MODULE.SESSION, 'checkAndUpdateSession: You must call
|
|
387
|
+
logger.logFailure(constants.AP_LOG_MODULE.SESSION, 'checkAndUpdateSession: You must call Apptics.init() first'); // no i18n
|
|
261
388
|
return Promise.resolve();
|
|
262
389
|
}
|
|
263
390
|
if (env.is_apptics_disabled()) {
|
|
264
391
|
return Promise.resolve();
|
|
265
392
|
}
|
|
266
|
-
if (sessionsOff()) {
|
|
267
|
-
return Promise.resolve();
|
|
268
|
-
}
|
|
269
393
|
var time = utils.nowMs();
|
|
270
|
-
|
|
271
|
-
|
|
394
|
+
return withSessionLock(function () {
|
|
395
|
+
return settleSession(time);
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function settleSession(time) {
|
|
399
|
+
// a tab that was killed before it could close its session: close it so it gets sent; and the
|
|
400
|
+
// screen views left open by tabs that are gone, so they end now rather than with the session
|
|
401
|
+
return closeAbandonedSessions().then(closeScreensOfGoneTabs).then(currentSession).then(function (_currentSession) {
|
|
272
402
|
// a closed session belongs to a tab that has gone: this tab starts its own
|
|
273
403
|
if (!_currentSession || _currentSession.endtime !== 0) {
|
|
274
404
|
return startNewSession(time);
|
|
@@ -289,13 +419,14 @@ function checkAndUpdateSession() {
|
|
|
289
419
|
});
|
|
290
420
|
}
|
|
291
421
|
logger.Console.log('checkAndUpdateSession: User is back before ' + state._ZAP.inactiveduration + ' minutes so updating the lastactivitytime - ' + time); // no i18n
|
|
292
|
-
return
|
|
422
|
+
return touchSession();
|
|
293
423
|
});
|
|
294
424
|
}
|
|
295
425
|
function updateLastActiveTimeOfSession() {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
426
|
+
return withSessionLock(touchSession);
|
|
427
|
+
}
|
|
428
|
+
/** Marks the session active now, with this tab among its tabs. Callers hold the session lock. */
|
|
429
|
+
function touchSession() {
|
|
299
430
|
var time = utils.nowMs();
|
|
300
431
|
// a pending session is not in the store yet, so it is updated in place instead
|
|
301
432
|
if (pendingSession) {
|
|
@@ -308,20 +439,25 @@ function updateLastActiveTimeOfSession() {
|
|
|
308
439
|
return;
|
|
309
440
|
}
|
|
310
441
|
logger.Console.log('updateLastActiveTimeOfSession: Updating the lastactivitytime - ' + time); // no i18n
|
|
311
|
-
var _newSession = Object.assign({}, _currentSession, { lastactivetime: time });
|
|
442
|
+
var _newSession = Object.assign({}, _currentSession, { lastactivetime: time, tabs: withThisTab(_currentSession) });
|
|
312
443
|
state._ZAP._current_session = _newSession;
|
|
313
444
|
return storage.engagement_transactions.update(_newSession);
|
|
445
|
+
}).then(function () {
|
|
446
|
+
return markScreenSeen(time);
|
|
314
447
|
});
|
|
315
448
|
}
|
|
316
449
|
|
|
317
450
|
exports.checkAndUpdateSession = checkAndUpdateSession;
|
|
318
451
|
exports.closeAbandonedSessions = closeAbandonedSessions;
|
|
319
452
|
exports.closeScreen = closeScreen;
|
|
453
|
+
exports.closeScreensOfGoneTabs = closeScreensOfGoneTabs;
|
|
320
454
|
exports.commitPendingSession = commitPendingSession;
|
|
321
455
|
exports.currentSession = currentSession;
|
|
322
456
|
exports.dropPendingSession = dropPendingSession;
|
|
323
457
|
exports.endCurrentSession = endCurrentSession;
|
|
458
|
+
exports.holdTabLock = holdTabLock;
|
|
324
459
|
exports.startNewSession = startNewSession;
|
|
460
|
+
exports.tabId = tabId;
|
|
325
461
|
exports.trackSession = trackSession;
|
|
326
462
|
exports.updateLastActiveTimeOfSession = updateLastActiveTimeOfSession;
|
|
327
463
|
exports.updateScreenWithEndtime = updateScreenWithEndtime;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var constants = require('../core/constants.js');
|
|
@@ -16,6 +16,20 @@ 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 = {};
|
|
21
|
+
/**
|
|
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
|
+
}
|
|
19
33
|
/**
|
|
20
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.
|
|
@@ -37,14 +51,14 @@ 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
57
|
if (groupName === undefined || groupName === null || groupName === '') { // no i18n
|
|
44
58
|
groupName = constants.AP_CONFIG.default_group_name;
|
|
45
59
|
}
|
|
46
60
|
else if (typeof groupName !== 'string' || !validate.validateEventOrGroup(groupName)) { // no i18n
|
|
47
|
-
|
|
61
|
+
refused(constants.AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: Invalid groupName for "' + eventName + '": ' + String(groupName)); // no i18n
|
|
48
62
|
return;
|
|
49
63
|
}
|
|
50
64
|
}
|
|
@@ -70,7 +84,7 @@ function trackEvent(eventName, groupName, jsonObj) {
|
|
|
70
84
|
session.commitPendingSession();
|
|
71
85
|
debug.noteDebugActivity();
|
|
72
86
|
storage.engagement_transactions.add(eventData);
|
|
73
|
-
logger.Console.log('
|
|
87
|
+
logger.Console.log('AppticsEvent.addEvent(\'' + eventName + '\',\'' + groupName + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
|
|
74
88
|
logger.Console.dir(eventData);
|
|
75
89
|
}
|
|
76
90
|
/**
|
|
@@ -80,7 +94,7 @@ function trackEvent(eventName, groupName, jsonObj) {
|
|
|
80
94
|
*/
|
|
81
95
|
function inScreenAt(screenName, jsonObj, id, startedAt) {
|
|
82
96
|
if (!validate.validateScreenName(screenName)) {
|
|
83
|
-
|
|
97
|
+
refused(constants.AP_LOG_MODULE.SCREEN, 'AppticsScreen.inScreen: Invalid screenName: ' + String(screenName)); // no i18n
|
|
84
98
|
return;
|
|
85
99
|
}
|
|
86
100
|
// the app named this screen itself: the automatic view of the same navigation gives way to it
|
|
@@ -90,7 +104,7 @@ function inScreenAt(screenName, jsonObj, id, startedAt) {
|
|
|
90
104
|
/** Ends the screen view inScreen() began, as Android's AppticsScreenTracker.outScreen(id). */
|
|
91
105
|
function outScreenAt(id, endedAt) {
|
|
92
106
|
if (utils.isEmpty(id)) {
|
|
93
|
-
logger.logFailure(constants.AP_LOG_MODULE.SCREEN, '
|
|
107
|
+
logger.logFailure(constants.AP_LOG_MODULE.SCREEN, 'AppticsScreen.outScreen: no screen id given'); // no i18n
|
|
94
108
|
return;
|
|
95
109
|
}
|
|
96
110
|
session.closeScreen(id, endedAt);
|
|
@@ -107,7 +121,7 @@ function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
|
|
|
107
121
|
return;
|
|
108
122
|
}
|
|
109
123
|
if (!validate.validateScreenName(screenName)) {
|
|
110
|
-
|
|
124
|
+
refused(constants.AP_LOG_MODULE.SCREEN, 'AppticsScreen.inScreen: Invalid screenName: ' + String(screenName)); // no i18n
|
|
111
125
|
return;
|
|
112
126
|
}
|
|
113
127
|
jsonObj = jsonObj || {};
|
|
@@ -124,7 +138,10 @@ function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
|
|
|
124
138
|
networkstatus: env.getNetworkStatus(),
|
|
125
139
|
orientation: env.getOrientation(),
|
|
126
140
|
sessionstarttime: state._ZAP._current_session.starttime || time,
|
|
127
|
-
customproperties: _jsonObj
|
|
141
|
+
customproperties: _jsonObj,
|
|
142
|
+
// the tab showing it, so a tab that is gone can have its view ended (closeScreensOfGoneTabs).
|
|
143
|
+
// SDK bookkeeping, removed before sending, as lastseen is
|
|
144
|
+
tab: session.tabId(),
|
|
128
145
|
};
|
|
129
146
|
// the screen being left ends where this one starts
|
|
130
147
|
var previous = state._ZAP._current_screen && state._ZAP._current_screen.id;
|
|
@@ -137,7 +154,7 @@ function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
|
|
|
137
154
|
// `implicit`: a view recorded only so a heatmap record has one to belong to. Kept on the
|
|
138
155
|
// in-memory copy alone, never on the stored item, which goes to the server as it is
|
|
139
156
|
state._ZAP._current_screen = implicit ? Object.assign({}, screenData, { implicit: true }) : screenData;
|
|
140
|
-
logger.Console.log('
|
|
157
|
+
logger.Console.log('AppticsScreen.inScreen(\'' + screenName + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
|
|
141
158
|
logger.Console.dir(screenData);
|
|
142
159
|
}
|
|
143
160
|
/**
|
|
@@ -226,16 +243,19 @@ function trackApi(jsonObj) {
|
|
|
226
243
|
var hostname = jsonObj.hostname || '';
|
|
227
244
|
var port = (typeof jsonObj.port === 'number') ? jsonObj.port : 0;
|
|
228
245
|
var path = jsonObj.path || '';
|
|
246
|
+
// a call reported by its parts, a hostname and a path with no url: its scheme and port are its
|
|
247
|
+
// own (https and 443 unless given), not the page's, which belong to another host
|
|
248
|
+
var partsOnly = utils.isEmpty(jsonObj.url) && !utils.isEmpty(jsonObj.hostname);
|
|
229
249
|
if (!utils.isEmpty(rawUrl)) {
|
|
230
250
|
try {
|
|
231
251
|
var urlObj = new URL(rawUrl, window.location.origin);
|
|
232
|
-
if (utils.isEmpty(scheme)) {
|
|
252
|
+
if (utils.isEmpty(scheme) && !partsOnly) {
|
|
233
253
|
scheme = urlObj.protocol.replace(':', ''); // "https:"
|
|
234
254
|
}
|
|
235
255
|
if (utils.isEmpty(hostname)) {
|
|
236
256
|
hostname = urlObj.hostname;
|
|
237
257
|
}
|
|
238
|
-
if (!port) {
|
|
258
|
+
if (!port && !partsOnly) {
|
|
239
259
|
if (urlObj.port) {
|
|
240
260
|
port = parseInt(urlObj.port, 10);
|
|
241
261
|
}
|
|
@@ -256,22 +276,27 @@ function trackApi(jsonObj) {
|
|
|
256
276
|
}
|
|
257
277
|
// only http and https exist as far as the proxy's api_scheme is concerned
|
|
258
278
|
scheme = (scheme === 'https' || scheme === 'http') ? scheme : 'https'; // no i18n
|
|
279
|
+
if (!port) {
|
|
280
|
+
port = scheme === 'https' ? 443 : 80; // no i18n
|
|
281
|
+
}
|
|
259
282
|
// a hostname the proxy refuses cannot be repaired without inventing one, so the call is
|
|
260
283
|
// dropped here and said out loud. localhost and bare IPs land here: api_hostname wants at
|
|
261
284
|
// least one dot and an alphabetic suffix, which is worth knowing when nothing shows up locally
|
|
262
285
|
if (!API_HOSTNAME.test(hostname)) {
|
|
263
|
-
logger.logFailure(constants.AP_LOG_MODULE.API, '
|
|
286
|
+
logger.logFailure(constants.AP_LOG_MODULE.API, 'AppticsApi: hostname not accepted by the server: ' + String(hostname)); // no i18n
|
|
264
287
|
return;
|
|
265
288
|
}
|
|
266
|
-
// configureApiTracking: the app's rules decide whether this call is recorded at all
|
|
267
|
-
|
|
268
|
-
|
|
289
|
+
// configureApiTracking: the app's rules decide whether this call is recorded at all, tried on
|
|
290
|
+
// the URL as it was called, without its query
|
|
291
|
+
var defaultPort = (scheme === 'https' && port === 443) || (scheme === 'http' && port === 80); // no i18n
|
|
292
|
+
var calledUrl = scheme + '://' + hostname + (port && !defaultPort ? ':' + port : '') + (String(path).charAt(0) === '/' ? '' : '/') + path; // no i18n
|
|
293
|
+
if (!apiRules.urlTracked(calledUrl)) {
|
|
294
|
+
logger.Console.log('AppticsApi: ' + hostname + withoutQueryForLog(path) + ' is left out by configureApiTracking'); // no i18n
|
|
269
295
|
return;
|
|
270
296
|
}
|
|
271
|
-
// and the name it is recorded under: ids become *,
|
|
272
|
-
//
|
|
297
|
+
// and the name it is recorded under: ids become *, so the console groups calls by endpoint
|
|
298
|
+
// rather than by every id that went through it
|
|
273
299
|
path = sanitizeApiPath(apiRules.normalizeApiPath(path));
|
|
274
|
-
hostname = apiRules.groupedHost(hostname);
|
|
275
300
|
var sessionStart = state._ZAP._current_session.starttime || starttime;
|
|
276
301
|
// no customproperties: apiWebSDK does not declare the key, unlike the screen and event
|
|
277
302
|
// templates, so anything extra the caller passes is context for the log line only
|
|
@@ -296,7 +321,7 @@ function trackApi(jsonObj) {
|
|
|
296
321
|
session.commitPendingSession();
|
|
297
322
|
debug.noteDebugActivity();
|
|
298
323
|
storage.engagement_transactions.add(apiData);
|
|
299
|
-
logger.Console.log('
|
|
324
|
+
logger.Console.log('AppticsApi(\'' + method + ' ' + scheme + '://' + hostname + path + '\')'); // no i18n
|
|
300
325
|
logger.Console.dir(apiData);
|
|
301
326
|
}
|
|
302
327
|
/** Begun calls by id. Bounded, so an app that starts calls it never ends can't grow it forever. */
|
|
@@ -324,7 +349,7 @@ function startTrackApi(url, method) {
|
|
|
324
349
|
function endTrackApiAt(id, responseCode, responseMessage, endedAt) {
|
|
325
350
|
var open = openCalls[id];
|
|
326
351
|
if (!open) {
|
|
327
|
-
logger.logFailure(constants.AP_LOG_MODULE.API, '
|
|
352
|
+
logger.logFailure(constants.AP_LOG_MODULE.API, 'AppticsApi.endTrackApi: no call started with id ' + String(id)); // no i18n
|
|
328
353
|
return;
|
|
329
354
|
}
|
|
330
355
|
delete openCalls[id];
|
package/dist/cjs/errors/send.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Properties } from '../types.js';
|
|
2
2
|
export declare function trackError(errorEvent: any, jsonObj?: Properties): void;
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function trackGlobalErrorHandler(status?: boolean): void;
|
|
4
4
|
export declare function extractMethodNameFromStackTrace(stackTrace: string): string;
|
package/dist/cjs/errors/track.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.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var constants = require('../core/constants.js');
|
|
@@ -38,19 +38,59 @@ function stackOf(errorEvent) {
|
|
|
38
38
|
}
|
|
39
39
|
/** Said in place of a stack when there is none, so `message` is never sent empty. */
|
|
40
40
|
const NO_STACK = 'no stack available'; // no i18n
|
|
41
|
+
/** The element an event came from, as `<script src="…">`, so a failed load says what failed. */
|
|
42
|
+
function describeTarget(target) {
|
|
43
|
+
var el = target;
|
|
44
|
+
if (!el || typeof el.tagName !== 'string') { // no i18n
|
|
45
|
+
return ''; // no i18n
|
|
46
|
+
}
|
|
47
|
+
var tag = el.tagName.toLowerCase();
|
|
48
|
+
var url = el.currentSrc || el.src || el.href || ''; // no i18n
|
|
49
|
+
return ' on <' + tag + (url ? (el.href && !el.src ? ' href="' : ' src="') + url + '"' : '') + '>'; // no i18n
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* What was thrown or rejected, as text an issue can be named by. An Error reads as itself. A
|
|
53
|
+
* promise rejected with an Event - what a failed script or image load hands over - used to be
|
|
54
|
+
* recorded as "[object Event]"; it is named by its type and where it came from instead. Anything
|
|
55
|
+
* else that is an object is shown as its JSON, cut short.
|
|
56
|
+
*/
|
|
57
|
+
function describeThrown(thrown) {
|
|
58
|
+
if (thrown === null || thrown === undefined) {
|
|
59
|
+
return ''; // no i18n
|
|
60
|
+
}
|
|
61
|
+
if (typeof thrown !== 'object') { // no i18n
|
|
62
|
+
return String(thrown);
|
|
63
|
+
}
|
|
64
|
+
if (thrown instanceof Error || typeof thrown.message === 'string' && typeof thrown.name === 'string') { // no i18n
|
|
65
|
+
return String(thrown);
|
|
66
|
+
}
|
|
67
|
+
if (typeof Event !== 'undefined' && thrown instanceof Event) { // no i18n
|
|
68
|
+
return 'Event "' + thrown.type + '"' + describeTarget(thrown.target); // no i18n
|
|
69
|
+
}
|
|
70
|
+
var text = String(thrown);
|
|
71
|
+
if (text !== '[object Object]') { // no i18n
|
|
72
|
+
return text;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return JSON.stringify(thrown).slice(0, 300);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return text;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
41
81
|
function trackError(errorEvent, jsonObj) {
|
|
42
82
|
if (!consent.canTrackErrors('trackError')) { // no i18n
|
|
43
83
|
return;
|
|
44
84
|
}
|
|
45
85
|
if (utils.isEmpty(errorEvent)) {
|
|
46
|
-
logger.Console.warn('
|
|
86
|
+
logger.Console.warn('AppticsError.trackError: Invalid error'); // no i18n
|
|
47
87
|
return;
|
|
48
88
|
}
|
|
49
89
|
var _jsonObj = validate.validateJSON(jsonObj);
|
|
50
90
|
var stack = stackOf(errorEvent);
|
|
51
91
|
var methodName = extractMethodNameFromStackTrace(stack) || "";
|
|
52
92
|
var issuetype = errorEvent.type || "";
|
|
53
|
-
var thrown = errorEvent.reason || errorEvent.error || errorEvent.message || "";
|
|
93
|
+
var thrown = describeThrown(errorEvent.reason || errorEvent.error || errorEvent.message || "");
|
|
54
94
|
// "Error: x at handler", or just "Error: x" when the stack names no method. issuename is
|
|
55
95
|
// required too, so something that carried no message at all is still named rather than blank
|
|
56
96
|
var described = !utils.isEmpty(String(thrown)) ? String(thrown) : (!utils.isEmpty(issuetype) ? String(issuetype) : 'Unknown error'); // no i18n
|
|
@@ -76,10 +116,10 @@ function trackError(errorEvent, jsonObj) {
|
|
|
76
116
|
session.commitPendingSession();
|
|
77
117
|
debug.noteDebugActivity();
|
|
78
118
|
storage.error_transactions.add(errorData);
|
|
79
|
-
logger.Console.log('
|
|
119
|
+
logger.Console.log('AppticsError.trackError(\'' + issuetype + '\',\'' + message + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
|
|
80
120
|
logger.Console.dir(errorData);
|
|
81
121
|
}
|
|
82
|
-
function
|
|
122
|
+
function trackGlobalErrorHandler(status) {
|
|
83
123
|
state._ZAP._isGlobalErrorTracking = typeof status === "boolean" ? status : true; // no i18n
|
|
84
124
|
}
|
|
85
125
|
function extractMethodNameFromStackTrace(stackTrace) {
|
|
@@ -122,6 +162,6 @@ function extractMethodNameFromStackTrace(stackTrace) {
|
|
|
122
162
|
return methodName;
|
|
123
163
|
}
|
|
124
164
|
|
|
125
|
-
exports.enableGlobalErrorHandler = enableGlobalErrorHandler;
|
|
126
165
|
exports.extractMethodNameFromStackTrace = extractMethodNameFromStackTrace;
|
|
127
166
|
exports.trackError = trackError;
|
|
167
|
+
exports.trackGlobalErrorHandler = trackGlobalErrorHandler;
|
|
@@ -12,6 +12,12 @@ export declare const logChoice: {
|
|
|
12
12
|
includeLog?: boolean;
|
|
13
13
|
includeDyninfo?: boolean;
|
|
14
14
|
};
|
|
15
|
+
/**
|
|
16
|
+
* Who a sendFeedback() report, made without the form, is attributed to: nobody when `anonymous`,
|
|
17
|
+
* else the user AppticsUser.setUser set (or the active registered one, status 1). Returns a function
|
|
18
|
+
* that puts back the choice that was there, so a form left open keeps its own.
|
|
19
|
+
*/
|
|
20
|
+
export declare function chooseUserForSend(anonymous: boolean): () => void;
|
|
15
21
|
/**
|
|
16
22
|
* The "User" dropdown above the description. It lists Anonymous plus every registered user, and
|
|
17
23
|
* starts on the active one (status 1) unless the caller asked for anonymous.
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
1
|
+
/*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var logger = require('../core/logger.js');
|
|
5
5
|
var hooks = require('../core/hooks.js');
|
|
6
6
|
var storage = require('../core/storage.js');
|
|
7
7
|
var utils = require('../core/utils.js');
|
|
8
|
+
var state = require('../core/state.js');
|
|
8
9
|
var logs = require('./logs.js');
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -25,6 +26,34 @@ function resetUserChoice() {
|
|
|
25
26
|
userChoice.userid = ''; // no i18n
|
|
26
27
|
userChoice.groupid = ''; // no i18n
|
|
27
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Who a sendFeedback() report, made without the form, is attributed to: nobody when `anonymous`,
|
|
31
|
+
* else the user AppticsUser.setUser set (or the active registered one, status 1). Returns a function
|
|
32
|
+
* that puts back the choice that was there, so a form left open keeps its own.
|
|
33
|
+
*/
|
|
34
|
+
function chooseUserForSend(anonymous) {
|
|
35
|
+
var before = { anonymous: userChoice.anonymous, userid: userChoice.userid, groupid: userChoice.groupid };
|
|
36
|
+
resetUserChoice();
|
|
37
|
+
if (!anonymous) {
|
|
38
|
+
// the user setUser set on this page, else the active one of those registered before
|
|
39
|
+
var active = !utils.isEmpty(state._ZAP._user_info.userid) ? state._ZAP._user_info : null;
|
|
40
|
+
storedUsers().forEach(function (user) {
|
|
41
|
+
if (!active && user && user.status == 1 && !utils.isEmpty(user.userid)) {
|
|
42
|
+
active = user;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
if (active) {
|
|
46
|
+
userChoice.anonymous = false;
|
|
47
|
+
userChoice.userid = String(active.userid);
|
|
48
|
+
userChoice.groupid = String(active.groupid || ''); // no i18n
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return function restore() {
|
|
52
|
+
userChoice.anonymous = before.anonymous;
|
|
53
|
+
userChoice.userid = before.userid;
|
|
54
|
+
userChoice.groupid = before.groupid;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
28
57
|
/** Every distinct registered user, by the address the picker shows. */
|
|
29
58
|
function storedUsers() {
|
|
30
59
|
var users = storage.user_defaults.item('users'); // no i18n
|
|
@@ -279,6 +308,7 @@ hooks.hooks.feedbackUser = function () {
|
|
|
279
308
|
return userChoice.anonymous || utils.isEmpty(userChoice.userid) ? null : userChoice;
|
|
280
309
|
};
|
|
281
310
|
|
|
311
|
+
exports.chooseUserForSend = chooseUserForSend;
|
|
282
312
|
exports.logChoice = logChoice;
|
|
283
313
|
exports.userChoice = userChoice;
|
|
284
314
|
exports.wireDrag = wireDrag;
|