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