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