@zoho_apptics/apptics-js-sdk 2.0.0-beta.1 → 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 (183) hide show
  1. package/CHANGELOG.md +80 -16
  2. package/README.md +187 -100
  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 +1 -2
  13. package/dist/cjs/core/consent.d.ts +13 -3
  14. package/dist/cjs/core/consent.js +24 -10
  15. package/dist/cjs/core/constants.d.ts +22 -17
  16. package/dist/cjs/core/constants.js +22 -19
  17. package/dist/cjs/core/db.js +1 -1
  18. package/dist/cjs/core/debug.js +1 -1
  19. package/dist/cjs/core/env.d.ts +0 -3
  20. package/dist/cjs/core/env.js +1 -9
  21. package/dist/cjs/core/framework-id.d.ts +1 -1
  22. package/dist/cjs/core/framework-id.js +3 -3
  23. package/dist/cjs/core/guard.js +1 -1
  24. package/dist/cjs/core/hooks.d.ts +5 -0
  25. package/dist/cjs/core/hooks.js +2 -1
  26. package/dist/cjs/core/locks.js +1 -1
  27. package/dist/cjs/core/logger.js +1 -1
  28. package/dist/cjs/core/state.d.ts +6 -4
  29. package/dist/cjs/core/state.js +2 -4
  30. package/dist/cjs/core/storage.js +1 -4
  31. package/dist/cjs/core/stylesheet.js +1 -1
  32. package/dist/cjs/core/transport.js +13 -16
  33. package/dist/cjs/core/utils.js +1 -1
  34. package/dist/cjs/core/validate.js +1 -1
  35. package/dist/cjs/device/appversion.d.ts +10 -1
  36. package/dist/cjs/device/appversion.js +26 -3
  37. package/dist/cjs/device/index.d.ts +17 -2
  38. package/dist/cjs/device/index.js +195 -27
  39. package/dist/cjs/engagement/api-rules.js +1 -1
  40. package/dist/cjs/engagement/auto-api.js +1 -1
  41. package/dist/cjs/engagement/auto-screen.js +1 -1
  42. package/dist/cjs/engagement/defined-events.js +1 -1
  43. package/dist/cjs/engagement/send.js +7 -3
  44. package/dist/cjs/engagement/session.d.ts +17 -0
  45. package/dist/cjs/engagement/session.js +95 -4
  46. package/dist/cjs/engagement/track.d.ts +4 -4
  47. package/dist/cjs/engagement/track.js +15 -12
  48. package/dist/cjs/errors/send.js +2 -2
  49. package/dist/cjs/errors/track.d.ts +2 -1
  50. package/dist/cjs/errors/track.js +6 -4
  51. package/dist/cjs/feedback/controls.d.ts +6 -0
  52. package/dist/cjs/feedback/controls.js +74 -25
  53. package/dist/cjs/feedback/index.js +1 -1
  54. package/dist/cjs/feedback/logs.d.ts +3 -6
  55. package/dist/cjs/feedback/logs.js +8 -18
  56. package/dist/cjs/feedback/quartz.js +1 -1
  57. package/dist/cjs/feedback/send.d.ts +2 -2
  58. package/dist/cjs/feedback/send.js +4 -27
  59. package/dist/cjs/feedback/styles.js +2 -2
  60. package/dist/cjs/feedback/template.d.ts +1 -1
  61. package/dist/cjs/feedback/template.js +9 -1
  62. package/dist/cjs/feedback/ui.d.ts +7 -0
  63. package/dist/cjs/feedback/ui.js +160 -25
  64. package/dist/cjs/heatmap/send.d.ts +2 -0
  65. package/dist/cjs/heatmap/send.js +3 -5
  66. package/dist/cjs/heatmap/track.js +1 -1
  67. package/dist/cjs/index.d.ts +1 -2
  68. package/dist/cjs/index.js +54 -17
  69. package/dist/cjs/lifecycle.d.ts +13 -2
  70. package/dist/cjs/lifecycle.js +122 -54
  71. package/dist/cjs/remoteconfig/country.js +1 -1
  72. package/dist/cjs/remoteconfig/custom.js +1 -1
  73. package/dist/cjs/remoteconfig/evaluate.js +1 -1
  74. package/dist/cjs/remoteconfig/fetch.d.ts +17 -10
  75. package/dist/cjs/remoteconfig/fetch.js +126 -31
  76. package/dist/cjs/remoteconfig/index.d.ts +13 -4
  77. package/dist/cjs/remoteconfig/index.js +36 -20
  78. package/dist/cjs/remoteconfig/store.d.ts +15 -7
  79. package/dist/cjs/remoteconfig/store.js +68 -35
  80. package/dist/cjs/remoteconfig/tz-countries.js +1 -1
  81. package/dist/cjs/{core → remotelog}/callsite.d.ts +5 -4
  82. package/dist/cjs/{core → remotelog}/callsite.js +6 -5
  83. package/dist/cjs/remotelog/gate.js +1 -1
  84. package/dist/cjs/remotelog/logger.js +2 -2
  85. package/dist/cjs/remotelog/send.js +2 -2
  86. package/dist/cjs/remotelogger/index.js +1 -1
  87. package/dist/cjs/scheduler.d.ts +3 -1
  88. package/dist/cjs/scheduler.js +24 -21
  89. package/dist/cjs/types.d.ts +61 -16
  90. package/dist/cjs/user/index.js +6 -2
  91. package/dist/esm/core/attempts.js +1 -1
  92. package/dist/esm/core/batch.js +1 -2
  93. package/dist/esm/core/consent.d.ts +13 -3
  94. package/dist/esm/core/consent.js +24 -11
  95. package/dist/esm/core/constants.d.ts +22 -17
  96. package/dist/esm/core/constants.js +22 -19
  97. package/dist/esm/core/db.js +1 -1
  98. package/dist/esm/core/debug.js +1 -1
  99. package/dist/esm/core/env.d.ts +0 -3
  100. package/dist/esm/core/env.js +2 -9
  101. package/dist/esm/core/framework-id.d.ts +1 -1
  102. package/dist/esm/core/framework-id.js +3 -3
  103. package/dist/esm/core/guard.js +1 -1
  104. package/dist/esm/core/hooks.d.ts +5 -0
  105. package/dist/esm/core/hooks.js +2 -1
  106. package/dist/esm/core/locks.js +1 -1
  107. package/dist/esm/core/logger.js +1 -1
  108. package/dist/esm/core/state.d.ts +6 -4
  109. package/dist/esm/core/state.js +2 -4
  110. package/dist/esm/core/storage.js +1 -4
  111. package/dist/esm/core/stylesheet.js +1 -1
  112. package/dist/esm/core/transport.js +13 -16
  113. package/dist/esm/core/utils.js +1 -1
  114. package/dist/esm/core/validate.js +1 -1
  115. package/dist/esm/device/appversion.d.ts +10 -1
  116. package/dist/esm/device/appversion.js +26 -4
  117. package/dist/esm/device/index.d.ts +17 -2
  118. package/dist/esm/device/index.js +195 -30
  119. package/dist/esm/engagement/api-rules.js +1 -1
  120. package/dist/esm/engagement/auto-api.js +1 -1
  121. package/dist/esm/engagement/auto-screen.js +1 -1
  122. package/dist/esm/engagement/defined-events.js +1 -1
  123. package/dist/esm/engagement/send.js +7 -3
  124. package/dist/esm/engagement/session.d.ts +17 -0
  125. package/dist/esm/engagement/session.js +93 -5
  126. package/dist/esm/engagement/track.d.ts +4 -4
  127. package/dist/esm/engagement/track.js +16 -13
  128. package/dist/esm/errors/send.js +2 -2
  129. package/dist/esm/errors/track.d.ts +2 -1
  130. package/dist/esm/errors/track.js +6 -4
  131. package/dist/esm/feedback/controls.d.ts +6 -0
  132. package/dist/esm/feedback/controls.js +74 -26
  133. package/dist/esm/feedback/index.js +1 -1
  134. package/dist/esm/feedback/logs.d.ts +3 -6
  135. package/dist/esm/feedback/logs.js +8 -18
  136. package/dist/esm/feedback/quartz.js +1 -1
  137. package/dist/esm/feedback/send.d.ts +2 -2
  138. package/dist/esm/feedback/send.js +6 -29
  139. package/dist/esm/feedback/styles.js +2 -2
  140. package/dist/esm/feedback/template.d.ts +1 -1
  141. package/dist/esm/feedback/template.js +9 -1
  142. package/dist/esm/feedback/ui.d.ts +7 -0
  143. package/dist/esm/feedback/ui.js +161 -26
  144. package/dist/esm/heatmap/send.d.ts +2 -0
  145. package/dist/esm/heatmap/send.js +3 -5
  146. package/dist/esm/heatmap/track.js +1 -1
  147. package/dist/esm/index.d.ts +1 -2
  148. package/dist/esm/index.js +57 -20
  149. package/dist/esm/lifecycle.d.ts +13 -2
  150. package/dist/esm/lifecycle.js +126 -60
  151. package/dist/esm/remoteconfig/country.js +1 -1
  152. package/dist/esm/remoteconfig/custom.js +1 -1
  153. package/dist/esm/remoteconfig/evaluate.js +1 -1
  154. package/dist/esm/remoteconfig/fetch.d.ts +17 -10
  155. package/dist/esm/remoteconfig/fetch.js +127 -32
  156. package/dist/esm/remoteconfig/index.d.ts +13 -4
  157. package/dist/esm/remoteconfig/index.js +36 -20
  158. package/dist/esm/remoteconfig/store.d.ts +15 -7
  159. package/dist/esm/remoteconfig/store.js +68 -36
  160. package/dist/esm/remoteconfig/tz-countries.js +1 -1
  161. package/dist/esm/{core → remotelog}/callsite.d.ts +5 -4
  162. package/dist/esm/{core → remotelog}/callsite.js +6 -5
  163. package/dist/esm/remotelog/gate.js +1 -1
  164. package/dist/esm/remotelog/logger.js +2 -2
  165. package/dist/esm/remotelog/send.js +2 -2
  166. package/dist/esm/remotelogger/index.js +1 -1
  167. package/dist/esm/scheduler.d.ts +3 -1
  168. package/dist/esm/scheduler.js +25 -23
  169. package/dist/esm/types.d.ts +61 -16
  170. package/dist/esm/user/index.js +7 -3
  171. package/dist/feedback.esm.min.js +2 -2
  172. package/dist/feedback.esm.min.js.map +1 -1
  173. package/dist/remoteconfig.esm.min.js +3 -0
  174. package/dist/remoteconfig.esm.min.js.map +1 -0
  175. package/dist/remotelogger.esm.min.js +3 -0
  176. package/dist/remotelogger.esm.min.js.map +1 -0
  177. package/package.json +3 -2
  178. package/dist/chunks/lifecycle-VYr43KcB.min.js +0 -3
  179. package/dist/chunks/lifecycle-VYr43KcB.min.js.map +0 -1
  180. package/dist/cjs/auto-init.d.ts +0 -8
  181. package/dist/cjs/auto-init.js +0 -32
  182. package/dist/esm/auto-init.d.ts +0 -8
  183. package/dist/esm/auto-init.js +0 -29
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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, getConfig } from '../core/state.js';
3
3
  import { Console } from '../core/logger.js';
4
4
  import { nowMs, isEmpty } from '../core/utils.js';
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { Console } from '../core/logger.js';
3
3
  import { hooks } from '../core/hooks.js';
4
4
  import { _ZAP } from '../core/state.js';
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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
  /**
3
3
  * Apptics' defined events, as Android's DefinedEvents: ready-made names in reserved `AP_` groups,
4
4
  * logged with trackEvent like any other event and never tracked automatically.
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { AP_LOG_MODULE, AP_CONSTANTS } from '../core/constants.js';
3
3
  import { _ZAP } from '../core/state.js';
4
4
  import { Console, logFailure, warnAlways } from '../core/logger.js';
@@ -13,8 +13,7 @@ import { isPermanentRefusal } from '../core/transport.js';
13
13
  * Sends pending engagements to the Apptics API.
14
14
  */
15
15
  function sendEngagementData(forget) {
16
- // engagementtracking off (getupdates): queued items stay until tracking is back on or they expire
17
- if (is_apptics_disabled() || !shouldTrackOrSendData() || !_ZAP._engagementTracking) {
16
+ if (is_apptics_disabled() || !shouldTrackOrSendData()) {
18
17
  return Promise.resolve();
19
18
  }
20
19
  if (navigator.onLine == false) {
@@ -37,6 +36,11 @@ function sendEngagementData(forget) {
37
36
  var _payload = {};
38
37
  var sent = [];
39
38
  cloneObject(engagement_items).forEach(function (item) {
39
+ // a kind the server has turned off (getsdkconfig's event, api, screen or session
40
+ // module) stays queued until it is back on or the item expires
41
+ if (_ZAP._engagementTracking[item.type] === false) {
42
+ return;
43
+ }
40
44
  const itemWithId = { ...item };
41
45
  if (item.type == AP_CONSTANTS.EVENT) {
42
46
  eventObj.push(item);
@@ -35,10 +35,27 @@ export declare function dropPendingSession(): boolean;
35
35
  /** Whether a session is waiting to be written. Used by tests and by the page-closing path. */
36
36
  export declare function hasPendingSession(): boolean;
37
37
  export declare function startNewSession(time: number): Promise<void>;
38
+ /**
39
+ * The tab went inactive. That moment becomes the session's lastactivetime, the time it ends at if
40
+ * the user does not come back within inactiveduration. If they do not, the session is ended at it
41
+ * then, and `onEnded` sends it, rather than staying open until they return or the tab closes.
42
+ * Going inactive twice (a window blur, then the tab hidden) keeps the first moment.
43
+ */
44
+ export declare function markTabInactive(onEnded: () => void): Promise<void>;
45
+ /**
46
+ * The tab is active again. The caller then settles the session (checkAndUpdateSession): back
47
+ * within inactiveduration it continues, later it has ended and a new one starts.
48
+ */
49
+ export declare function markTabActive(): void;
50
+ /** reset(): no timer left running, and the tab counts as active. */
51
+ export declare function resetInactivity(): void;
38
52
  /**
39
53
  * This tab is closing, or the page is being replaced. The session ends here when this is the last
40
54
  * tab using it, so it can be sent; while another tab of the site is still on it, this tab only
41
55
  * leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
56
+ *
57
+ * A tab closed while inactive ends the session when the user left (its lastactivetime), not when
58
+ * the tab closed: the time it sat hidden or in the background is not session time.
42
59
  */
43
60
  export declare function endCurrentSession(): Promise<void>;
44
61
  /**
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { 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';
@@ -62,7 +62,7 @@ function closeScreensOf(session, time) {
62
62
  * @param sessionData A set of properties belonging to the session.
63
63
  */
64
64
  function trackSession(sessionData) {
65
- if (!canTrackEngagement('trackSession')) { // no i18n
65
+ if (!canTrackEngagement('trackSession', AP_CONSTANTS.SESSION)) { // no i18n
66
66
  return Promise.resolve();
67
67
  }
68
68
  if (sessionData && typeof sessionData !== 'object') { // no i18n
@@ -318,26 +318,114 @@ function commitNow(session) {
318
318
  }
319
319
  return trackSession(session);
320
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
+ }
321
400
  /**
322
401
  * This tab is closing, or the page is being replaced. The session ends here when this is the last
323
402
  * tab using it, so it can be sent; while another tab of the site is still on it, this tab only
324
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.
325
407
  */
326
408
  function endCurrentSession() {
327
409
  var time = nowMs();
410
+ var wasInactive = inactive;
411
+ markTabActive();
328
412
  // nothing was tracked and the minimum was never reached: this visit is not reported at all
329
413
  if (dropPendingSession()) {
330
414
  return Promise.resolve();
331
415
  }
332
416
  return withSessionLock(function () {
333
- return closeOrLeaveSession(time);
417
+ return closeOrLeaveSession(time, wasInactive);
334
418
  });
335
419
  }
336
- function closeOrLeaveSession(time) {
420
+ function closeOrLeaveSession(now, wasInactive) {
337
421
  return currentSession().then(function (_currentSession) {
338
422
  if (!_currentSession || _currentSession.endtime !== 0) {
339
423
  return;
340
424
  }
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;
341
429
  var others = tabsOf(_currentSession).filter(function (tab) { return tab !== tabId(); });
342
430
  if (others.length > 0) {
343
431
  var left = Object.assign({}, _currentSession, { tabs: others, lastactivetime: time });
@@ -445,4 +533,4 @@ function touchSession() {
445
533
  });
446
534
  }
447
535
 
448
- export { checkAndUpdateSession, closeAbandonedSessions, closeScreen, closeScreensOfGoneTabs, commitPendingSession, currentSession, dropPendingSession, endCurrentSession, holdTabLock, startNewSession, tabId, 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,5 +1,5 @@
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';
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
4
  import { logFailure, Console, warnAlways } from '../core/logger.js';
5
5
  import { isEmpty, nowMs, getNewUUID } from '../core/utils.js';
@@ -29,15 +29,15 @@ function refused(module, message) {
29
29
  }
30
30
  }
31
31
  /**
32
- * Records an event: `trackEvent(name, group?, properties?)`, or a defined event with
32
+ * Records an event: `trackEvent(name, group, properties?)`, or a defined event with
33
33
  * `trackEvent(DefinedEvents.AP_USER_LOGIN, properties?)`, which brings its own group.
34
34
  *
35
- * As on Android, an invalid name or group is not logged. A group left out is `default`, so the
36
- * one-argument form keeps working; a group given but invalid is a mistake worth being told about,
37
- * 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.
38
38
  */
39
39
  function trackEvent(eventName, groupName, jsonObj) {
40
- if (!canTrackEngagement('trackEvent')) { // no i18n
40
+ if (!canTrackEngagement('trackEvent', AP_CONSTANTS.EVENT)) { // no i18n
41
41
  return;
42
42
  }
43
43
  var defined = asDefinedEvent(eventName);
@@ -52,10 +52,13 @@ function trackEvent(eventName, groupName, jsonObj) {
52
52
  refused(AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: Invalid eventName: ' + String(eventName)); // no i18n
53
53
  return;
54
54
  }
55
- if (groupName === undefined || groupName === null || groupName === '') { // no i18n
56
- 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;
57
60
  }
58
- else if (typeof groupName !== 'string' || !validateEventOrGroup(groupName)) { // no i18n
61
+ if (typeof groupName !== 'string' || !validateEventOrGroup(groupName)) { // no i18n
59
62
  refused(AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: Invalid groupName for "' + eventName + '": ' + String(groupName)); // no i18n
60
63
  return;
61
64
  }
@@ -82,7 +85,7 @@ function trackEvent(eventName, groupName, jsonObj) {
82
85
  commitPendingSession();
83
86
  noteDebugActivity();
84
87
  engagement_transactions.add(eventData);
85
- Console.log('AppticsEvent.addEvent(\'' + eventName + '\',\'' + groupName + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
88
+ Console.log('AppticsEvent.addEvent(\'' + eventName + '\',' + '\'' + groupName + '\'' + ',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
86
89
  Console.dir(eventData);
87
90
  }
88
91
  /**
@@ -115,7 +118,7 @@ function outScreenAt(id, endedAt) {
115
118
  * An open view is not sent until it is closed, as an open session is not.
116
119
  */
117
120
  function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
118
- if (!canTrackEngagement('inScreen')) { // no i18n
121
+ if (!canTrackEngagement('inScreen', AP_CONSTANTS.SCREEN)) { // no i18n
119
122
  return;
120
123
  }
121
124
  if (!validateScreenName(screenName)) {
@@ -211,7 +214,7 @@ function withoutQueryForLog(path) {
211
214
  return String(path || '').split(/[?#]/)[0]; // no i18n
212
215
  }
213
216
  function trackApi(jsonObj) {
214
- if (!canTrackEngagement('trackApi')) { // no i18n
217
+ if (!canTrackEngagement('trackApi', AP_CONSTANTS.API)) { // no i18n
215
218
  return;
216
219
  }
217
220
  jsonObj = jsonObj || {};
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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
  }
@@ -1,4 +1,5 @@
1
1
  import type { Properties } from '../types.js';
2
2
  export declare function trackError(errorEvent: any, jsonObj?: Properties): void;
3
- export declare function trackGlobalErrorHandler(status?: boolean): void;
3
+ /** AppticsError.setErrorTracking: uncaught errors on or off, as the `track_error` config sets it. */
4
+ export declare function setErrorTracking(status?: boolean): void;
4
5
  export declare function extractMethodNameFromStackTrace(stackTrace: string): string;
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { AP_CONSTANTS } from '../core/constants.js';
3
3
  import { _ZAP } from '../core/state.js';
4
4
  import { Console } from '../core/logger.js';
@@ -117,8 +117,10 @@ function trackError(errorEvent, jsonObj) {
117
117
  Console.log('AppticsError.trackError(\'' + issuetype + '\',\'' + message + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
118
118
  Console.dir(errorData);
119
119
  }
120
- function trackGlobalErrorHandler(status) {
121
- _ZAP._isGlobalErrorTracking = typeof status === "boolean" ? status : true; // no i18n
120
+ /** AppticsError.setErrorTracking: uncaught errors on or off, as the `track_error` config sets it. */
121
+ function setErrorTracking(status) {
122
+ _ZAP._config.track_error = typeof status === "boolean" ? status : true; // no i18n
123
+ Console.log('automatic error tracking: ' + (_ZAP._config.track_error ? 'on' : 'off')); // no i18n
122
124
  }
123
125
  function extractMethodNameFromStackTrace(stackTrace) {
124
126
  let methodName = ''; // no i18n
@@ -160,4 +162,4 @@ function extractMethodNameFromStackTrace(stackTrace) {
160
162
  return methodName;
161
163
  }
162
164
 
163
- export { extractMethodNameFromStackTrace, trackError, trackGlobalErrorHandler };
165
+ export { extractMethodNameFromStackTrace, setErrorTracking, trackError };
@@ -35,3 +35,9 @@ export declare function wireLogControls(modal: HTMLElement, opts: Dict): void;
35
35
  * kept inside the viewport.
36
36
  */
37
37
  export declare function wireDrag(box: HTMLElement): void;
38
+ /**
39
+ * Lets the recording timer be dragged anywhere on the page. A press that moves it is a drag, and
40
+ * does not also stop the recording, as a plain click on it does. Call before the click listener
41
+ * is added, so a drag's click is stopped ahead of it.
42
+ */
43
+ export declare function wireTimerDrag(timer: HTMLElement): void;
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { Console } from '../core/logger.js';
3
3
  import { hooks } from '../core/hooks.js';
4
4
  import { user_defaults } from '../core/storage.js';
@@ -265,45 +265,93 @@ function wireDrag(box) {
265
265
  if (!header) {
266
266
  return;
267
267
  }
268
- var startX = 0, startY = 0, boxLeft = 0, boxTop = 0, dragging = false;
269
- header.addEventListener('mousedown', function (event) {
270
- if (event.target.closest('.zasdk-close')) { // no i18n
271
- return; // the close button is not a drag handle
272
- }
273
- if (!box.style.left || box.style.left === 'auto') { // no i18n
274
- var rect = box.getBoundingClientRect();
275
- box.style.right = 'auto'; // no i18n
276
- box.style.bottom = 'auto'; // no i18n
277
- box.style.left = rect.left + 'px'; // no i18n
278
- box.style.top = rect.top + 'px'; // no i18n
268
+ makeDraggable(box, header, function (target) {
269
+ // the close button is not a drag handle
270
+ return !!target.closest('.zasdk-close'); // no i18n
271
+ });
272
+ }
273
+ /** Where the recording timer was last dragged to, so the next recording's timer starts there. */
274
+ var timerSpot = null;
275
+ /**
276
+ * Lets the recording timer be dragged anywhere on the page. A press that moves it is a drag, and
277
+ * does not also stop the recording, as a plain click on it does. Call before the click listener
278
+ * is added, so a drag's click is stopped ahead of it.
279
+ */
280
+ function wireTimerDrag(timer) {
281
+ if (timerSpot) {
282
+ placeAt(timer, timerSpot.left, timerSpot.top);
283
+ }
284
+ makeDraggable(timer, timer, undefined, function (left, top) {
285
+ timerSpot = { left: left, top: top };
286
+ });
287
+ }
288
+ /** Puts a fixed box at left/top, inside the viewport. */
289
+ function placeAt(box, left, top) {
290
+ box.style.right = 'auto'; // no i18n
291
+ box.style.bottom = 'auto'; // no i18n
292
+ box.style.left = Math.max(0, Math.min(left, window.innerWidth - box.offsetWidth)) + 'px'; // no i18n
293
+ box.style.top = Math.max(0, Math.min(top, window.innerHeight - box.offsetHeight)) + 'px'; // no i18n
294
+ }
295
+ /**
296
+ * Moves `box` with the mouse or a finger pressed on `handle`. `skip` says which presses are not a
297
+ * drag; `moved` hears where the box ended up. A click that ends a drag goes no further.
298
+ */
299
+ function makeDraggable(box, handle, skip, moved) {
300
+ var startX = 0, startY = 0, boxLeft = 0, boxTop = 0, dragging = false, dragged = false;
301
+ handle.addEventListener('pointerdown', function (event) {
302
+ if (event.button !== 0 || (skip && skip(event.target))) {
303
+ return;
279
304
  }
305
+ var rect = box.getBoundingClientRect();
280
306
  dragging = true;
307
+ dragged = false;
281
308
  startX = event.clientX;
282
309
  startY = event.clientY;
283
- boxLeft = parseInt(box.style.left, 10);
284
- boxTop = parseInt(box.style.top, 10);
285
- header.style.cursor = 'grabbing'; // no i18n
310
+ boxLeft = rect.left;
311
+ boxTop = rect.top;
312
+ handle.style.cursor = 'move'; // no i18n
313
+ // the moves and the release come to the handle however far the pointer goes, so nothing
314
+ // is left listening on the document once the box is gone
315
+ try {
316
+ handle.setPointerCapture(event.pointerId);
317
+ }
318
+ catch (err) { /* a pointer already released */ }
286
319
  event.preventDefault();
287
320
  });
288
- document.addEventListener('mousemove', function (event) {
321
+ handle.addEventListener('pointermove', function (event) {
289
322
  if (!dragging) {
290
323
  return;
291
324
  }
292
- var left = boxLeft + (event.clientX - startX);
293
- var top = boxTop + (event.clientY - startY);
294
- box.style.left = Math.max(0, Math.min(left, window.innerWidth - box.offsetWidth)) + 'px'; // no i18n
295
- box.style.top = Math.max(0, Math.min(top, window.innerHeight - box.offsetHeight)) + 'px'; // no i18n
296
- });
297
- document.addEventListener('mouseup', function () {
298
- if (dragging) {
299
- dragging = false;
300
- header.style.cursor = 'grab'; // no i18n
325
+ var dx = event.clientX - startX, dy = event.clientY - startY;
326
+ if (!dragged && Math.abs(dx) < 4 && Math.abs(dy) < 4) {
327
+ return; // a press that wobbles is still a click
301
328
  }
329
+ dragged = true;
330
+ placeAt(box, boxLeft + dx, boxTop + dy);
302
331
  });
332
+ function stop() {
333
+ if (!dragging) {
334
+ return;
335
+ }
336
+ dragging = false;
337
+ handle.style.cursor = ''; // no i18n
338
+ if (dragged && moved) {
339
+ moved(parseInt(box.style.left, 10), parseInt(box.style.top, 10));
340
+ }
341
+ }
342
+ handle.addEventListener('pointerup', stop); // no i18n
343
+ handle.addEventListener('pointercancel', stop); // no i18n
344
+ box.addEventListener('click', function (event) {
345
+ if (dragged) {
346
+ dragged = false;
347
+ event.stopImmediatePropagation();
348
+ event.preventDefault();
349
+ }
350
+ }, true);
303
351
  }
304
352
  // core/transport asks for the picked user when it builds the feedback request's parameters
305
353
  hooks.feedbackUser = function () {
306
354
  return userChoice.anonymous || isEmpty(userChoice.userid) ? null : userChoice;
307
355
  };
308
356
 
309
- export { chooseUserForSend, logChoice, userChoice, wireDrag, wireLogControls, wireUserPicker };
357
+ export { chooseUserForSend, logChoice, userChoice, wireDrag, wireLogControls, wireTimerDrag, wireUserPicker };
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { forward, guard } from '../core/guard.js';
3
3
  import { AP_FEEDBACK_TYPE } from '../core/constants.js';
4
4
  import { Console } from '../core/logger.js';
@@ -32,14 +32,11 @@ export declare function toLogFile(): Promise<RenderedFile | null>;
32
32
  * that parses it are unaffected.
33
33
  */
34
34
  export declare function logText(): Promise<string>;
35
- /** The diagnostics entries as dyninfo.txt would carry them, without the device info block. */
35
+ /** The diagnostics entries as dyninfo.txt carries them. */
36
36
  export declare function diagnosticsText(): Promise<string>;
37
37
  /** True when any log line is waiting, so the modal knows whether to offer the toggle. */
38
38
  export declare function hasLogLines(): Promise<boolean>;
39
- /**
40
- * dyninfo.txt: the stored diagnostics entries followed by a [Device Info] block holding
41
- * `deviceInfo`. Returns null only when there is nothing at all to put in it.
42
- */
43
- export declare function toDyninfoFile(deviceInfo: string | null): Promise<RenderedFile | null>;
39
+ /** dyninfo.txt: the stored diagnostics entries. Returns null when there are none. */
40
+ export declare function toDyninfoFile(): Promise<RenderedFile | null>;
44
41
  /** True when any diagnostics entry is waiting, so a submission knows to send dyninfo unasked. */
45
42
  export declare function hasDiagnostics(): Promise<boolean>;
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { warnAlways, Console } from '../core/logger.js';
3
3
  import { FEEDBACK_LOG, FEEDBACK_DIAGNOSTICS } from '../core/db.js';
4
4
  import { feedbacklog_transactions } from '../core/storage.js';
@@ -166,7 +166,7 @@ function toLogFile() {
166
166
  return asFile(LOG_FILE_NAME, renderLogLines(items), items);
167
167
  });
168
168
  }
169
- /** `16 Oct 2026, 11:53:48 PM IST`: a log line's time in the visitor's own zone, for the viewer. */
169
+ /** `16 Oct 2026, 11:53:48 PM`: a log line's time in the visitor's own zone, for the viewer. */
170
170
  function readableStamp(item) {
171
171
  var iso = stamp(item);
172
172
  var d = new Date(iso);
@@ -174,7 +174,7 @@ function readableStamp(item) {
174
174
  return iso;
175
175
  }
176
176
  return d.toLocaleDateString(undefined, { day: '2-digit', month: 'short', year: 'numeric' }) + ', ' + // no i18n
177
- d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit', timeZoneName: 'short' }); // no i18n
177
+ d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' }); // no i18n
178
178
  }
179
179
  /**
180
180
  * The log lines for showing in the modal before sending, each time in the visitor's local zone.
@@ -188,7 +188,7 @@ function logText() {
188
188
  }).join('\n'); // no i18n
189
189
  });
190
190
  }
191
- /** The diagnostics entries as dyninfo.txt would carry them, without the device info block. */
191
+ /** The diagnostics entries as dyninfo.txt carries them. */
192
192
  function diagnosticsText() {
193
193
  return entriesOfType(DIAGNOSTICS).then(groupDiagnostics);
194
194
  }
@@ -198,23 +198,13 @@ function hasLogLines() {
198
198
  return items.length > 0;
199
199
  });
200
200
  }
201
- /**
202
- * dyninfo.txt: the stored diagnostics entries followed by a [Device Info] block holding
203
- * `deviceInfo`. Returns null only when there is nothing at all to put in it.
204
- */
205
- function toDyninfoFile(deviceInfo) {
201
+ /** dyninfo.txt: the stored diagnostics entries. Returns null when there are none. */
202
+ function toDyninfoFile() {
206
203
  return entriesOfType(DIAGNOSTICS).then(function (items) {
207
- const blocks = [];
208
- if (items.length > 0) {
209
- blocks.push(groupDiagnostics(items));
210
- }
211
- if (deviceInfo) {
212
- blocks.push('[Device Info]\n' + deviceInfo); // no i18n
213
- }
214
- if (blocks.length === 0) {
204
+ if (items.length === 0) {
215
205
  return null;
216
206
  }
217
- return asFile(DYNINFO_FILE_NAME, blocks.join('\n\n'), items); // no i18n
207
+ return asFile(DYNINFO_FILE_NAME, groupDiagnostics(items), items);
218
208
  });
219
209
  }
220
210
  /** True when any diagnostics entry is waiting, so a submission knows to send dyninfo unasked. */
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (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 { warnAlways, Console } from '../core/logger.js';
3
3
 
4
4
  /**
@@ -7,8 +7,8 @@ export interface FeedbackRequest {
7
7
  /** Stored addFeedbackLog() lines: true or unset sends them when there are any, false never does. */
8
8
  includeLog?: boolean;
9
9
  /**
10
- * dyninfo.txt, holding addDiagnosticsInfo() entries and the device info dump.
11
- * True always sends it, false never does, unset sends it when diagnostics are waiting.
10
+ * dyninfo.txt, holding addDiagnosticsInfo() entries: true or unset sends it when there are
11
+ * any, false never does.
12
12
  */
13
13
  includeDyninfo?: boolean;
14
14
  quartzid: string;