@zoho_apptics/apptics-js-sdk 1.0.2 → 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.
Files changed (243) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/LICENSE +13 -0
  3. package/README.md +846 -11
  4. package/dist/apptics.esm.min.js +3 -0
  5. package/dist/apptics.esm.min.js.map +1 -0
  6. package/dist/apptics.min.js +3 -0
  7. package/dist/apptics.min.js.map +1 -0
  8. package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
  9. package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
  10. package/dist/cjs/auto-init.d.ts +8 -0
  11. package/dist/cjs/auto-init.js +32 -0
  12. package/dist/cjs/core/attempts.d.ts +28 -0
  13. package/dist/cjs/core/attempts.js +113 -0
  14. package/dist/cjs/core/batch.d.ts +32 -0
  15. package/dist/cjs/core/batch.js +116 -0
  16. package/dist/cjs/core/callsite.d.ts +34 -0
  17. package/dist/cjs/core/callsite.js +115 -0
  18. package/dist/cjs/core/consent.d.ts +27 -0
  19. package/dist/cjs/core/consent.js +132 -0
  20. package/dist/cjs/core/constants.d.ts +237 -0
  21. package/dist/cjs/core/constants.js +315 -0
  22. package/dist/cjs/core/db.d.ts +48 -0
  23. package/dist/cjs/core/db.js +467 -0
  24. package/dist/cjs/core/debug.d.ts +22 -0
  25. package/dist/cjs/core/debug.js +88 -0
  26. package/dist/cjs/core/env.d.ts +28 -0
  27. package/dist/cjs/core/env.js +267 -0
  28. package/dist/cjs/core/framework-id.d.ts +1 -0
  29. package/dist/cjs/core/framework-id.js +9 -0
  30. package/dist/cjs/core/guard.d.ts +11 -0
  31. package/dist/cjs/core/guard.js +86 -0
  32. package/dist/cjs/core/hooks.d.ts +39 -0
  33. package/dist/cjs/core/hooks.js +22 -0
  34. package/dist/cjs/core/locks.d.ts +9 -0
  35. package/dist/cjs/core/locks.js +22 -0
  36. package/dist/cjs/core/logger.d.ts +15 -0
  37. package/dist/cjs/core/logger.js +50 -0
  38. package/dist/cjs/core/state.d.ts +70 -0
  39. package/dist/cjs/core/state.js +78 -0
  40. package/dist/cjs/core/storage.d.ts +61 -0
  41. package/dist/cjs/core/storage.js +327 -0
  42. package/dist/cjs/core/stylesheet.d.ts +8 -0
  43. package/dist/cjs/core/stylesheet.js +29 -0
  44. package/dist/cjs/core/transport.d.ts +85 -0
  45. package/dist/cjs/core/transport.js +456 -0
  46. package/dist/cjs/core/utils.d.ts +26 -0
  47. package/dist/cjs/core/utils.js +175 -0
  48. package/dist/cjs/core/validate.d.ts +18 -0
  49. package/dist/cjs/core/validate.js +233 -0
  50. package/dist/cjs/device/appversion.d.ts +15 -0
  51. package/dist/cjs/device/appversion.js +170 -0
  52. package/dist/cjs/device/index.d.ts +34 -0
  53. package/dist/cjs/device/index.js +472 -0
  54. package/dist/cjs/engagement/api-rules.d.ts +49 -0
  55. package/dist/cjs/engagement/api-rules.js +206 -0
  56. package/dist/cjs/engagement/auto-api.d.ts +7 -0
  57. package/dist/cjs/engagement/auto-api.js +200 -0
  58. package/dist/cjs/engagement/auto-screen.d.ts +18 -0
  59. package/dist/cjs/engagement/auto-screen.js +305 -0
  60. package/dist/cjs/engagement/defined-events.d.ts +23 -0
  61. package/dist/cjs/engagement/defined-events.js +58 -0
  62. package/dist/cjs/engagement/send.d.ts +6 -0
  63. package/dist/cjs/engagement/send.js +104 -0
  64. package/dist/cjs/engagement/session.d.ts +51 -0
  65. package/dist/cjs/engagement/session.js +463 -0
  66. package/dist/cjs/engagement/track.d.ts +56 -0
  67. package/dist/cjs/engagement/track.js +375 -0
  68. package/dist/cjs/errors/send.d.ts +6 -0
  69. package/dist/cjs/errors/send.js +73 -0
  70. package/dist/cjs/errors/track.d.ts +4 -0
  71. package/dist/cjs/errors/track.js +167 -0
  72. package/dist/cjs/feedback/controls.d.ts +37 -0
  73. package/dist/cjs/feedback/controls.js +316 -0
  74. package/dist/cjs/feedback/index.d.ts +9 -0
  75. package/dist/cjs/feedback/index.js +56 -0
  76. package/dist/cjs/feedback/logs.d.ts +45 -0
  77. package/dist/cjs/feedback/logs.js +241 -0
  78. package/dist/cjs/feedback/quartz.d.ts +18 -0
  79. package/dist/cjs/feedback/quartz.js +105 -0
  80. package/dist/cjs/feedback/send.d.ts +28 -0
  81. package/dist/cjs/feedback/send.js +203 -0
  82. package/dist/cjs/feedback/styles.d.ts +10 -0
  83. package/dist/cjs/feedback/styles.js +8 -0
  84. package/dist/cjs/feedback/template.d.ts +2 -0
  85. package/dist/cjs/feedback/template.js +162 -0
  86. package/dist/cjs/feedback/ui.d.ts +63 -0
  87. package/dist/cjs/feedback/ui.js +709 -0
  88. package/dist/cjs/heatmap/send.d.ts +13 -0
  89. package/dist/cjs/heatmap/send.js +82 -0
  90. package/dist/cjs/heatmap/track.d.ts +4 -0
  91. package/dist/cjs/heatmap/track.js +448 -0
  92. package/dist/cjs/index.d.ts +22 -0
  93. package/dist/cjs/index.js +162 -0
  94. package/dist/cjs/lifecycle.d.ts +21 -0
  95. package/dist/cjs/lifecycle.js +734 -0
  96. package/dist/cjs/package.json +3 -0
  97. package/dist/cjs/remoteconfig/country.d.ts +4 -0
  98. package/dist/cjs/remoteconfig/country.js +73 -0
  99. package/dist/cjs/remoteconfig/custom.d.ts +10 -0
  100. package/dist/cjs/remoteconfig/custom.js +58 -0
  101. package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
  102. package/dist/cjs/remoteconfig/evaluate.js +184 -0
  103. package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
  104. package/dist/cjs/remoteconfig/fetch.js +200 -0
  105. package/dist/cjs/remoteconfig/index.d.ts +15 -0
  106. package/dist/cjs/remoteconfig/index.js +116 -0
  107. package/dist/cjs/remoteconfig/store.d.ts +18 -0
  108. package/dist/cjs/remoteconfig/store.js +98 -0
  109. package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
  110. package/dist/cjs/remoteconfig/tz-countries.js +13 -0
  111. package/dist/cjs/remotelog/gate.d.ts +2 -0
  112. package/dist/cjs/remotelog/gate.js +28 -0
  113. package/dist/cjs/remotelog/index.d.ts +8 -0
  114. package/dist/cjs/remotelog/logger.d.ts +22 -0
  115. package/dist/cjs/remotelog/logger.js +111 -0
  116. package/dist/cjs/remotelog/send.d.ts +13 -0
  117. package/dist/cjs/remotelog/send.js +153 -0
  118. package/dist/cjs/remotelogger/index.d.ts +12 -0
  119. package/dist/cjs/remotelogger/index.js +18 -0
  120. package/dist/cjs/scheduler.d.ts +8 -0
  121. package/dist/cjs/scheduler.js +78 -0
  122. package/dist/cjs/types.d.ts +385 -0
  123. package/dist/cjs/user/index.d.ts +33 -0
  124. package/dist/cjs/user/index.js +183 -0
  125. package/dist/esm/auto-init.d.ts +8 -0
  126. package/dist/esm/auto-init.js +29 -0
  127. package/dist/esm/core/attempts.d.ts +28 -0
  128. package/dist/esm/core/attempts.js +106 -0
  129. package/dist/esm/core/batch.d.ts +32 -0
  130. package/dist/esm/core/batch.js +109 -0
  131. package/dist/esm/core/callsite.d.ts +34 -0
  132. package/dist/esm/core/callsite.js +110 -0
  133. package/dist/esm/core/consent.d.ts +27 -0
  134. package/dist/esm/core/consent.js +122 -0
  135. package/dist/esm/core/constants.d.ts +237 -0
  136. package/dist/esm/core/constants.js +292 -0
  137. package/dist/esm/core/db.d.ts +48 -0
  138. package/dist/esm/core/db.js +453 -0
  139. package/dist/esm/core/debug.d.ts +22 -0
  140. package/dist/esm/core/debug.js +81 -0
  141. package/dist/esm/core/env.d.ts +28 -0
  142. package/dist/esm/core/env.js +251 -0
  143. package/dist/esm/core/framework-id.d.ts +1 -0
  144. package/dist/esm/core/framework-id.js +7 -0
  145. package/dist/esm/core/guard.d.ts +11 -0
  146. package/dist/esm/core/guard.js +83 -0
  147. package/dist/esm/core/hooks.d.ts +39 -0
  148. package/dist/esm/core/hooks.js +20 -0
  149. package/dist/esm/core/locks.d.ts +9 -0
  150. package/dist/esm/core/locks.js +20 -0
  151. package/dist/esm/core/logger.d.ts +15 -0
  152. package/dist/esm/core/logger.js +46 -0
  153. package/dist/esm/core/state.d.ts +70 -0
  154. package/dist/esm/core/state.js +73 -0
  155. package/dist/esm/core/storage.d.ts +61 -0
  156. package/dist/esm/core/storage.js +314 -0
  157. package/dist/esm/core/stylesheet.d.ts +8 -0
  158. package/dist/esm/core/stylesheet.js +27 -0
  159. package/dist/esm/core/transport.d.ts +85 -0
  160. package/dist/esm/core/transport.js +439 -0
  161. package/dist/esm/core/utils.d.ts +26 -0
  162. package/dist/esm/core/utils.js +161 -0
  163. package/dist/esm/core/validate.d.ts +18 -0
  164. package/dist/esm/core/validate.js +226 -0
  165. package/dist/esm/device/appversion.d.ts +15 -0
  166. package/dist/esm/device/appversion.js +164 -0
  167. package/dist/esm/device/index.d.ts +34 -0
  168. package/dist/esm/device/index.js +461 -0
  169. package/dist/esm/engagement/api-rules.d.ts +49 -0
  170. package/dist/esm/engagement/api-rules.js +199 -0
  171. package/dist/esm/engagement/auto-api.d.ts +7 -0
  172. package/dist/esm/engagement/auto-api.js +198 -0
  173. package/dist/esm/engagement/auto-screen.d.ts +18 -0
  174. package/dist/esm/engagement/auto-screen.js +301 -0
  175. package/dist/esm/engagement/defined-events.d.ts +23 -0
  176. package/dist/esm/engagement/defined-events.js +55 -0
  177. package/dist/esm/engagement/send.d.ts +6 -0
  178. package/dist/esm/engagement/send.js +101 -0
  179. package/dist/esm/engagement/session.d.ts +51 -0
  180. package/dist/esm/engagement/session.js +448 -0
  181. package/dist/esm/engagement/track.d.ts +56 -0
  182. package/dist/esm/engagement/track.js +365 -0
  183. package/dist/esm/errors/send.d.ts +6 -0
  184. package/dist/esm/errors/send.js +70 -0
  185. package/dist/esm/errors/track.d.ts +4 -0
  186. package/dist/esm/errors/track.js +163 -0
  187. package/dist/esm/feedback/controls.d.ts +37 -0
  188. package/dist/esm/feedback/controls.js +309 -0
  189. package/dist/esm/feedback/index.d.ts +9 -0
  190. package/dist/esm/feedback/index.js +51 -0
  191. package/dist/esm/feedback/logs.d.ts +45 -0
  192. package/dist/esm/feedback/logs.js +227 -0
  193. package/dist/esm/feedback/quartz.d.ts +18 -0
  194. package/dist/esm/feedback/quartz.js +99 -0
  195. package/dist/esm/feedback/send.d.ts +28 -0
  196. package/dist/esm/feedback/send.js +198 -0
  197. package/dist/esm/feedback/styles.d.ts +10 -0
  198. package/dist/esm/feedback/styles.js +4 -0
  199. package/dist/esm/feedback/template.d.ts +2 -0
  200. package/dist/esm/feedback/template.js +159 -0
  201. package/dist/esm/feedback/ui.d.ts +63 -0
  202. package/dist/esm/feedback/ui.js +703 -0
  203. package/dist/esm/heatmap/send.d.ts +13 -0
  204. package/dist/esm/heatmap/send.js +79 -0
  205. package/dist/esm/heatmap/track.d.ts +4 -0
  206. package/dist/esm/heatmap/track.js +445 -0
  207. package/dist/esm/index.d.ts +22 -0
  208. package/dist/esm/index.js +150 -0
  209. package/dist/esm/lifecycle.d.ts +21 -0
  210. package/dist/esm/lifecycle.js +729 -0
  211. package/dist/esm/package.json +3 -0
  212. package/dist/esm/remoteconfig/country.d.ts +4 -0
  213. package/dist/esm/remoteconfig/country.js +70 -0
  214. package/dist/esm/remoteconfig/custom.d.ts +10 -0
  215. package/dist/esm/remoteconfig/custom.js +54 -0
  216. package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
  217. package/dist/esm/remoteconfig/evaluate.js +179 -0
  218. package/dist/esm/remoteconfig/fetch.d.ts +24 -0
  219. package/dist/esm/remoteconfig/fetch.js +196 -0
  220. package/dist/esm/remoteconfig/index.d.ts +15 -0
  221. package/dist/esm/remoteconfig/index.js +111 -0
  222. package/dist/esm/remoteconfig/store.d.ts +18 -0
  223. package/dist/esm/remoteconfig/store.js +91 -0
  224. package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
  225. package/dist/esm/remoteconfig/tz-countries.js +11 -0
  226. package/dist/esm/remotelog/gate.d.ts +2 -0
  227. package/dist/esm/remotelog/gate.js +26 -0
  228. package/dist/esm/remotelog/index.d.ts +8 -0
  229. package/dist/esm/remotelog/logger.d.ts +22 -0
  230. package/dist/esm/remotelog/logger.js +108 -0
  231. package/dist/esm/remotelog/send.d.ts +13 -0
  232. package/dist/esm/remotelog/send.js +149 -0
  233. package/dist/esm/remotelogger/index.d.ts +12 -0
  234. package/dist/esm/remotelogger/index.js +13 -0
  235. package/dist/esm/scheduler.d.ts +8 -0
  236. package/dist/esm/scheduler.js +74 -0
  237. package/dist/esm/types.d.ts +385 -0
  238. package/dist/esm/user/index.d.ts +33 -0
  239. package/dist/esm/user/index.js +176 -0
  240. package/dist/feedback.esm.min.js +3 -0
  241. package/dist/feedback.esm.min.js.map +1 -0
  242. package/package.json +122 -7
  243. package/src/appticssdk.js +0 -1
@@ -0,0 +1,365 @@
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
+ import { _ZAP } from '../core/state.js';
4
+ import { logFailure, Console, warnAlways } from '../core/logger.js';
5
+ import { isEmpty, nowMs, getNewUUID } from '../core/utils.js';
6
+ import { engagement_transactions } from '../core/storage.js';
7
+ import { closeScreen, commitPendingSession, tabId } from './session.js';
8
+ import { getConnectionType, getOrientation, getNetworkStatus } from '../core/env.js';
9
+ import { canTrackEngagement } from '../core/consent.js';
10
+ import { validateScreenName, validateEventOrGroup, validateJSON } from '../core/validate.js';
11
+ import { noteDebugActivity } from '../core/debug.js';
12
+ import { hooks } from '../core/hooks.js';
13
+ import { urlTracked, normalizeApiPath } from './api-rules.js';
14
+ import { asDefinedEvent } from './defined-events.js';
15
+ import { automaticScreenName } from './auto-screen.js';
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
+ }
31
+ /**
32
+ * Records an event: `trackEvent(name, group?, properties?)`, or a defined event with
33
+ * `trackEvent(DefinedEvents.AP_USER_LOGIN, properties?)`, which brings its own group.
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`.
38
+ */
39
+ function trackEvent(eventName, groupName, jsonObj) {
40
+ if (!canTrackEngagement('trackEvent')) { // no i18n
41
+ return;
42
+ }
43
+ var defined = asDefinedEvent(eventName);
44
+ if (defined) {
45
+ // the defined event's own name and group, which alone may carry the reserved AP_ prefix
46
+ jsonObj = (groupName && typeof groupName === 'object' ? groupName : jsonObj); // no i18n
47
+ groupName = defined.group;
48
+ eventName = defined.event;
49
+ }
50
+ else {
51
+ if (typeof eventName !== 'string' || !validateEventOrGroup(eventName)) { // no i18n
52
+ refused(AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: Invalid eventName: ' + String(eventName)); // no i18n
53
+ return;
54
+ }
55
+ if (groupName === undefined || groupName === null || groupName === '') { // no i18n
56
+ groupName = AP_CONFIG.default_group_name;
57
+ }
58
+ else if (typeof groupName !== 'string' || !validateEventOrGroup(groupName)) { // no i18n
59
+ refused(AP_LOG_MODULE.EVENT, 'AppticsEvent.addEvent: Invalid groupName for "' + eventName + '": ' + String(groupName)); // no i18n
60
+ return;
61
+ }
62
+ }
63
+ jsonObj = jsonObj || {};
64
+ var _jsonObj = validateJSON(jsonObj);
65
+ // epoch time in seconds
66
+ var time = nowMs();
67
+ var eventData = {
68
+ id: getNewUUID(),
69
+ type: AP_CONSTANTS.EVENT,
70
+ group: groupName,
71
+ event: eventName,
72
+ starttime: time,
73
+ endtime: time,
74
+ edge: _ZAP._device_info.connectionType || getConnectionType(),
75
+ networkstatus: getNetworkStatus(),
76
+ screen: _ZAP._current_screen.screen || '', // no i18n
77
+ sessionstarttime: _ZAP._current_session.starttime || time,
78
+ customproperties: _jsonObj
79
+ };
80
+ // save the event
81
+ // the session may still be waiting out its minimum; this event is what earns it
82
+ commitPendingSession();
83
+ noteDebugActivity();
84
+ engagement_transactions.add(eventData);
85
+ Console.log('AppticsEvent.addEvent(\'' + eventName + '\',\'' + groupName + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
86
+ Console.dir(eventData);
87
+ }
88
+ /**
89
+ * The app's own screen view with a known id and start. The public inScreen takes the id and the
90
+ * time when the app calls it, so the id it hands back is the view's even when start-up is still
91
+ * running and this runs a moment later.
92
+ */
93
+ function inScreenAt(screenName, jsonObj, id, startedAt) {
94
+ if (!validateScreenName(screenName)) {
95
+ refused(AP_LOG_MODULE.SCREEN, 'AppticsScreen.inScreen: Invalid screenName: ' + String(screenName)); // no i18n
96
+ return;
97
+ }
98
+ // the app named this screen itself: the automatic view of the same navigation gives way to it
99
+ hooks.screenTrackedByApp();
100
+ recordScreen(screenName, jsonObj, id, startedAt);
101
+ }
102
+ /** Ends the screen view inScreen() began, as Android's AppticsScreenTracker.outScreen(id). */
103
+ function outScreenAt(id, endedAt) {
104
+ if (isEmpty(id)) {
105
+ logFailure(AP_LOG_MODULE.SCREEN, 'AppticsScreen.outScreen: no screen id given'); // no i18n
106
+ return;
107
+ }
108
+ closeScreen(id, endedAt);
109
+ }
110
+ /**
111
+ * Queues a screen view and makes it the current screen.
112
+ *
113
+ * The view is stored open (endtime 0) and closed when the next screen starts, when the page is
114
+ * hidden or closed, when its session ends, or by outScreen() — which is what gives it a duration.
115
+ * An open view is not sent until it is closed, as an open session is not.
116
+ */
117
+ function recordScreen(screenName, jsonObj, id, startedAt, implicit) {
118
+ if (!canTrackEngagement('inScreen')) { // no i18n
119
+ return;
120
+ }
121
+ if (!validateScreenName(screenName)) {
122
+ refused(AP_LOG_MODULE.SCREEN, 'AppticsScreen.inScreen: Invalid screenName: ' + String(screenName)); // no i18n
123
+ return;
124
+ }
125
+ jsonObj = jsonObj || {};
126
+ var _jsonObj = validateJSON(jsonObj);
127
+ // epoch time in milliseconds
128
+ var time = typeof startedAt === 'number' ? startedAt : nowMs(); // no i18n
129
+ var screenData = {
130
+ id: id || getNewUUID(),
131
+ type: AP_CONSTANTS.SCREEN,
132
+ screen: screenName,
133
+ starttime: time,
134
+ endtime: 0,
135
+ edge: _ZAP._device_info.connectionType || getConnectionType(),
136
+ networkstatus: getNetworkStatus(),
137
+ orientation: getOrientation(),
138
+ sessionstarttime: _ZAP._current_session.starttime || time,
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(),
143
+ };
144
+ // the screen being left ends where this one starts
145
+ var previous = _ZAP._current_screen && _ZAP._current_screen.id;
146
+ if (previous && previous !== screenData.id) {
147
+ closeScreen(previous, time);
148
+ }
149
+ commitPendingSession();
150
+ noteDebugActivity();
151
+ engagement_transactions.add(screenData);
152
+ // `implicit`: a view recorded only so a heatmap record has one to belong to. Kept on the
153
+ // in-memory copy alone, never on the stored item, which goes to the server as it is
154
+ _ZAP._current_screen = implicit ? Object.assign({}, screenData, { implicit: true }) : screenData;
155
+ Console.log('AppticsScreen.inScreen(\'' + screenName + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
156
+ Console.dir(screenData);
157
+ }
158
+ /**
159
+ * The screen a heatmap record belongs to, making sure engagement/add has a view of it: the console
160
+ * places a record by its screen, and a screen that was never sent as a view leaves it nowhere.
161
+ *
162
+ * A screen the app or automatic screen tracking named is used as it is — that one already has its
163
+ * view. Otherwise (automatic tracking off and no inScreen() call) the page is named the way
164
+ * automatic tracking would name it, and a view is recorded for it: once per screen, a new one when
165
+ * the path changes, and given way as soon as anything else names the screen.
166
+ */
167
+ function screenForRecord() {
168
+ var current = _ZAP._current_screen;
169
+ if (current && !isEmpty(current.screen) && !current.implicit) {
170
+ return String(current.screen);
171
+ }
172
+ var name = automaticScreenName() || '/'; // no i18n
173
+ if (!current || !current.id || current.screen !== name || current.endtime) {
174
+ recordScreen(name, undefined, undefined, undefined, true);
175
+ }
176
+ return name;
177
+ }
178
+ /**
179
+ * Back in view after being hidden: the hide ended the screen view, so the screen the user is on
180
+ * starts a new one, as an Android activity coming back to the foreground does.
181
+ */
182
+ function resumeScreen() {
183
+ var current = _ZAP._current_screen;
184
+ if (!current || !current.id || !current.endtime || isEmpty(current.screen)) {
185
+ return;
186
+ }
187
+ recordScreen(String(current.screen));
188
+ }
189
+ /**
190
+ * What the proxy accepts as an api item's hostname and path (security-regex.xml: api_hostname,
191
+ * api_urlpath). It validates these before the server sees them, and a rejected item is dropped
192
+ * there — nothing reaches the console and nothing says why. Checking here means trackApi can
193
+ * repair or refuse the value while there is still a caller to tell.
194
+ */
195
+ const API_HOSTNAME = /^([a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
196
+ const API_PATH_DISALLOWED = /[^a-zA-Z0-9\-._*\/]+/g;
197
+ /**
198
+ * Rewrites a path to the characters the proxy allows, one `*` per run of anything else, so an id
199
+ * that happens to hold an email or an encoded character costs that segment's detail rather than
200
+ * the whole call. Segment boundaries survive, which is what the console groups by.
201
+ */
202
+ function sanitizeApiPath(path) {
203
+ if (isEmpty(path)) {
204
+ return '/'; // no i18n
205
+ }
206
+ var cleaned = path.replace(API_PATH_DISALLOWED, '*'); // no i18n
207
+ return cleaned.charAt(0) === '/' ? cleaned : '/' + cleaned; // no i18n
208
+ }
209
+ /** A path for a log line, without the query, which can carry tokens. */
210
+ function withoutQueryForLog(path) {
211
+ return String(path || '').split(/[?#]/)[0]; // no i18n
212
+ }
213
+ function trackApi(jsonObj) {
214
+ if (!canTrackEngagement('trackApi')) { // no i18n
215
+ return;
216
+ }
217
+ jsonObj = jsonObj || {};
218
+ // Extract raw values from jsonObj (from XHR/fetch hook or manual calls)
219
+ var rawUrl = jsonObj.url || jsonObj.path || '';
220
+ var method = (jsonObj.method || 'GET').toString();
221
+ var responsecode = (typeof jsonObj.responsecode === 'number') ? jsonObj.responsecode :
222
+ (typeof jsonObj.status === 'number') ? jsonObj.status : 0;
223
+ // the template requires this key, and fetch leaves statusText empty on HTTP/2, so a
224
+ // placeholder stands in rather than risking the item being refused for an empty value
225
+ var responsemessage = jsonObj.responsemessage || jsonObj.statusText || jsonObj.error || '-';
226
+ // Time fields (ms since epoch, consistent with other trackers)
227
+ var now = nowMs();
228
+ var starttime = (typeof jsonObj.starttime === 'number') ? jsonObj.starttime : now;
229
+ var endtime;
230
+ if (typeof jsonObj.endtime === 'number') {
231
+ endtime = jsonObj.endtime;
232
+ }
233
+ else if (typeof jsonObj.duration === 'number') {
234
+ endtime = starttime + jsonObj.duration;
235
+ }
236
+ else {
237
+ endtime = now;
238
+ }
239
+ // URL breakdown: scheme, hostname, port, path
240
+ var scheme = jsonObj.scheme || '';
241
+ var hostname = jsonObj.hostname || '';
242
+ var port = (typeof jsonObj.port === 'number') ? jsonObj.port : 0;
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);
247
+ if (!isEmpty(rawUrl)) {
248
+ try {
249
+ var urlObj = new URL(rawUrl, window.location.origin);
250
+ if (isEmpty(scheme) && !partsOnly) {
251
+ scheme = urlObj.protocol.replace(':', ''); // "https:"
252
+ }
253
+ if (isEmpty(hostname)) {
254
+ hostname = urlObj.hostname;
255
+ }
256
+ if (!port && !partsOnly) {
257
+ if (urlObj.port) {
258
+ port = parseInt(urlObj.port, 10);
259
+ }
260
+ else {
261
+ port = (urlObj.protocol === 'https:') ? 443 : 80;
262
+ }
263
+ }
264
+ if (isEmpty(path)) {
265
+ path = urlObj.pathname;
266
+ }
267
+ }
268
+ catch (e) {
269
+ // Fallback: treat rawUrl as path if parsing fails
270
+ if (isEmpty(path)) {
271
+ path = rawUrl;
272
+ }
273
+ }
274
+ }
275
+ // only http and https exist as far as the proxy's api_scheme is concerned
276
+ scheme = (scheme === 'https' || scheme === 'http') ? scheme : 'https'; // no i18n
277
+ if (!port) {
278
+ port = scheme === 'https' ? 443 : 80; // no i18n
279
+ }
280
+ // a hostname the proxy refuses cannot be repaired without inventing one, so the call is
281
+ // dropped here and said out loud. localhost and bare IPs land here: api_hostname wants at
282
+ // least one dot and an alphabetic suffix, which is worth knowing when nothing shows up locally
283
+ if (!API_HOSTNAME.test(hostname)) {
284
+ logFailure(AP_LOG_MODULE.API, 'AppticsApi: hostname not accepted by the server: ' + String(hostname)); // no i18n
285
+ return;
286
+ }
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
293
+ return;
294
+ }
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
297
+ path = sanitizeApiPath(normalizeApiPath(path));
298
+ var sessionStart = _ZAP._current_session.starttime || starttime;
299
+ // no customproperties: apiWebSDK does not declare the key, unlike the screen and event
300
+ // templates, so anything extra the caller passes is context for the log line only
301
+ var apiData = {
302
+ id: getNewUUID(),
303
+ type: AP_CONSTANTS.API,
304
+ // Required by backend schema
305
+ scheme: scheme,
306
+ port: port,
307
+ hostname: hostname,
308
+ path: path,
309
+ method: method,
310
+ responsecode: responsecode,
311
+ responsemessage: responsemessage,
312
+ starttime: starttime,
313
+ endtime: endtime,
314
+ sessionstarttime: sessionStart,
315
+ networkstatus: getNetworkStatus(),
316
+ edge: _ZAP._device_info.connectionType || getConnectionType()
317
+ };
318
+ // save the api
319
+ commitPendingSession();
320
+ noteDebugActivity();
321
+ engagement_transactions.add(apiData);
322
+ Console.log('AppticsApi(\'' + method + ' ' + scheme + '://' + hostname + path + '\')'); // no i18n
323
+ Console.dir(apiData);
324
+ }
325
+ /** Begun calls by id. Bounded, so an app that starts calls it never ends can't grow it forever. */
326
+ var openCalls = {};
327
+ var openOrder = [];
328
+ const MAX_OPEN_CALLS = 500;
329
+ /**
330
+ * Begins timing one API call, as Android's AppticsApiTracker.startTrackApi(url, method): returns
331
+ * the id to hand to endTrackApi once the response is in. Records nothing by itself.
332
+ */
333
+ function startTrackApi(url, method) {
334
+ var id = getNewUUID();
335
+ openCalls[id] = { url: String(url || ''), method: String(method || 'GET').toUpperCase(), starttime: nowMs() }; // no i18n
336
+ openOrder.push(id);
337
+ if (openOrder.length > MAX_OPEN_CALLS) {
338
+ delete openCalls[openOrder.shift()];
339
+ }
340
+ return id;
341
+ }
342
+ /**
343
+ * Finishes a call startTrackApi began and records it, timed from start to `endedAt`. The public
344
+ * wrapper takes `endedAt` when the app calls it, so a call ended while start-up is still running
345
+ * keeps its real duration even though it is recorded a moment later.
346
+ */
347
+ function endTrackApiAt(id, responseCode, responseMessage, endedAt) {
348
+ var open = openCalls[id];
349
+ if (!open) {
350
+ logFailure(AP_LOG_MODULE.API, 'AppticsApi.endTrackApi: no call started with id ' + String(id)); // no i18n
351
+ return;
352
+ }
353
+ delete openCalls[id];
354
+ openOrder = openOrder.filter(function (one) { return one !== id; });
355
+ trackApi({
356
+ url: open.url,
357
+ method: open.method,
358
+ responsecode: typeof responseCode === 'number' ? responseCode : 0, // no i18n
359
+ responsemessage: responseMessage,
360
+ starttime: open.starttime,
361
+ endtime: endedAt,
362
+ });
363
+ }
364
+
365
+ export { endTrackApiAt, inScreenAt, outScreenAt, recordScreen, resumeScreen, screenForRecord, startTrackApi, trackApi, trackEvent };
@@ -0,0 +1,6 @@
1
+ import type { Dict, ServerResult } from '../types.js';
2
+ /**
3
+ * Sends pending errors to the Apptics API.
4
+ */
5
+ export declare function sendErrorData(forget?: boolean): Promise<void>;
6
+ export declare function requestToSendErrors(_payload: Dict, forget?: boolean): Promise<ServerResult>;
@@ -0,0 +1,70 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ import { _ZAP } from '../core/state.js';
3
+ import { Console, warnAlways } from '../core/logger.js';
4
+ import { cloneObject } from '../core/utils.js';
5
+ import { error_transactions } from '../core/storage.js';
6
+ import { is_apptics_disabled } from '../core/env.js';
7
+ import { shouldTrackOrSendData } from '../core/consent.js';
8
+ import { defer, clearDefer, withSendLock, freshItems, awaitUnlessClosing, sendBatch } from '../core/batch.js';
9
+ import { isPermanentRefusal } from '../core/transport.js';
10
+
11
+ /**
12
+ * Sends pending errors to the Apptics API.
13
+ */
14
+ function sendErrorData(forget) {
15
+ // errortracking off (getupdates): queued errors stay until tracking is back on or they expire
16
+ if (is_apptics_disabled() || !shouldTrackOrSendData() || !_ZAP._errorTracking) {
17
+ return Promise.resolve();
18
+ }
19
+ if (navigator.onLine == false) {
20
+ Console.warn('sendErrorData: No Network'); // no i18n
21
+ return Promise.resolve();
22
+ }
23
+ // if we're busy sending, retry in .5s
24
+ if (_ZAP._sending) {
25
+ defer(sendErrorData);
26
+ return Promise.resolve();
27
+ }
28
+ _ZAP._sending = true;
29
+ clearDefer();
30
+ return withSendLock('errors', function () {
31
+ return freshItems(error_transactions, function (item) { return item.happenedat != 0; }, function (item) { return item.happenedat; }).then(function (error_items) {
32
+ if (error_items.length == 0) {
33
+ return;
34
+ }
35
+ let exceptiondetails = [];
36
+ var sent = [];
37
+ cloneObject(error_items).forEach(function (item) {
38
+ sent.push({ ...item });
39
+ if (!item.hasOwnProperty('screenname')) { // no i18n
40
+ item.screenname = ''; // no i18n
41
+ }
42
+ delete item.id;
43
+ delete item.type;
44
+ exceptiondetails.push(item);
45
+ });
46
+ return awaitUnlessClosing(requestToSendErrors({ exceptiondetails: exceptiondetails }, forget).then(function () {
47
+ return error_transactions.remove(sent);
48
+ }, function (err) {
49
+ // the server will never accept this batch. Keeping it would block every later
50
+ // batch behind it for good, so it is dropped and said out loud
51
+ if (isPermanentRefusal(err)) {
52
+ warnAlways('apptics: the server refused these errors, so they were dropped: ' + String(err && err.message)); // no i18n
53
+ return error_transactions.remove(sent);
54
+ }
55
+ return Promise.reject(err);
56
+ }), forget, 'sendErrorData'); // no i18n
57
+ });
58
+ }).catch(function (err) {
59
+ Console.log('sendErrorData:' + err); // no i18n
60
+ }).then(function () {
61
+ _ZAP._sending = false;
62
+ });
63
+ }
64
+ // Function to send error data to server
65
+ function requestToSendErrors(_payload, forget) {
66
+ var label = 'requestToSendErrors'; // no i18n
67
+ return sendBatch('error', label, _payload, forget); // no i18n
68
+ }
69
+
70
+ export { requestToSendErrors, sendErrorData };
@@ -0,0 +1,4 @@
1
+ import type { Properties } from '../types.js';
2
+ export declare function trackError(errorEvent: any, jsonObj?: Properties): void;
3
+ export declare function trackGlobalErrorHandler(status?: boolean): void;
4
+ export declare function extractMethodNameFromStackTrace(stackTrace: string): string;
@@ -0,0 +1,163 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ import { AP_CONSTANTS } from '../core/constants.js';
3
+ import { _ZAP } from '../core/state.js';
4
+ import { Console } from '../core/logger.js';
5
+ import { isEmpty, nowMs, getNewUUID } from '../core/utils.js';
6
+ import { error_transactions } from '../core/storage.js';
7
+ import { commitPendingSession } from '../engagement/session.js';
8
+ import { getConnectionType, getNetworkStatus } from '../core/env.js';
9
+ import { canTrackErrors } from '../core/consent.js';
10
+ import { validateJSON } from '../core/validate.js';
11
+ import { noteDebugActivity } from '../core/debug.js';
12
+
13
+ /**
14
+ * The stack to report. Three sources, in order of how much they tell us:
15
+ *
16
+ * `reason.stack` and `error.stack` are where a promise rejection and a window error event keep it.
17
+ * `errorEvent.stack` covers an Error handed straight to trackError — `trackError(new Error('x'))`
18
+ * is the first thing anyone writes, and reading only the wrapped forms lost its trace silently.
19
+ *
20
+ * Failing all of those, a cross-origin script error or a non-Error throw carries no stack at all,
21
+ * and the event's own filename, line and column are the only detail there is, so they are written
22
+ * as a one-frame stack rather than discarded.
23
+ */
24
+ function stackOf(errorEvent) {
25
+ var stack = errorEvent.reason?.stack || errorEvent.error?.stack || errorEvent.stack || ''; // no i18n
26
+ if (!isEmpty(stack)) {
27
+ return stack;
28
+ }
29
+ var file = errorEvent.filename || errorEvent.fileName || ''; // no i18n
30
+ if (isEmpty(file)) {
31
+ return ''; // no i18n
32
+ }
33
+ var line = errorEvent.lineno !== undefined ? errorEvent.lineno : errorEvent.lineNumber;
34
+ var column = errorEvent.colno !== undefined ? errorEvent.colno : errorEvent.columnNumber;
35
+ return 'at ' + file + (isEmpty(line) ? '' : ':' + line) + (isEmpty(column) ? '' : ':' + column); // no i18n
36
+ }
37
+ /** Said in place of a stack when there is none, so `message` is never sent empty. */
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
+ }
79
+ function trackError(errorEvent, jsonObj) {
80
+ if (!canTrackErrors('trackError')) { // no i18n
81
+ return;
82
+ }
83
+ if (isEmpty(errorEvent)) {
84
+ Console.warn('AppticsError.trackError: Invalid error'); // no i18n
85
+ return;
86
+ }
87
+ var _jsonObj = validateJSON(jsonObj);
88
+ var stack = stackOf(errorEvent);
89
+ var methodName = extractMethodNameFromStackTrace(stack) || "";
90
+ var issuetype = errorEvent.type || "";
91
+ var thrown = describeThrown(errorEvent.reason || errorEvent.error || errorEvent.message || "");
92
+ // "Error: x at handler", or just "Error: x" when the stack names no method. issuename is
93
+ // required too, so something that carried no message at all is still named rather than blank
94
+ var described = !isEmpty(String(thrown)) ? String(thrown) : (!isEmpty(issuetype) ? String(issuetype) : 'Unknown error'); // no i18n
95
+ var message = described + (isEmpty(methodName) ? "" : ' at ' + methodName);
96
+ // epoch time in seconds
97
+ var time = nowMs();
98
+ var errorData = {
99
+ id: getNewUUID(),
100
+ type: AP_CONSTANTS.ERROR,
101
+ edge: _ZAP._device_info.connectionType || getConnectionType(),
102
+ networkstatus: getNetworkStatus(),
103
+ happenedat: time,
104
+ issuename: message,
105
+ // the template requires this key, so a stackless error still says something: an empty value
106
+ // risks the proxy refusing the item, and errors travel in batches
107
+ message: !isEmpty(stack) ? stack : (!isEmpty(String(thrown)) ? String(thrown) : NO_STACK),
108
+ screenname: _ZAP._current_screen.screen || window.location.pathname,
109
+ sessionstarttime: _ZAP._current_session.starttime || time,
110
+ customproperties: _jsonObj
111
+ };
112
+ // save the error
113
+ // an error is worth a session even in the first seconds of a visit
114
+ commitPendingSession();
115
+ noteDebugActivity();
116
+ error_transactions.add(errorData);
117
+ Console.log('AppticsError.trackError(\'' + issuetype + '\',\'' + message + '\',' + JSON.stringify(_jsonObj || {}) + ')'); // no i18n
118
+ Console.dir(errorData);
119
+ }
120
+ function trackGlobalErrorHandler(status) {
121
+ _ZAP._isGlobalErrorTracking = typeof status === "boolean" ? status : true; // no i18n
122
+ }
123
+ function extractMethodNameFromStackTrace(stackTrace) {
124
+ let methodName = ''; // no i18n
125
+ if (isEmpty(stackTrace)) {
126
+ return methodName;
127
+ }
128
+ const lines = stackTrace.split('\n'); // no i18n
129
+ if (lines.length < 2) {
130
+ return methodName; // Not enough information in the stack trace
131
+ }
132
+ // The method name is typically included in the second line
133
+ const methodNamePattern1 = /at (\S+) \(/;
134
+ const methodNamePattern2 = /^(\w+)@/;
135
+ for (let line of lines) {
136
+ const match1 = line.match(methodNamePattern1);
137
+ const match2 = line.match(methodNamePattern2);
138
+ // For browsers like Chrome, Ula
139
+ if (match1 && match1[1]) {
140
+ // Return the method name
141
+ const urlRegex = /\((.*)\)/;
142
+ // Extract the URL from the error string
143
+ const urlMatch = urlRegex.exec(line);
144
+ // Extracted URL
145
+ const url = urlMatch ? urlMatch[1] : 'Unknown URL'; // no i18n
146
+ methodName = match1[1];
147
+ url.split('/').pop(); // no i18n
148
+ methodName = methodName; // + '@'+ fileNameAndLine;
149
+ break;
150
+ }
151
+ // For browsers like Safari
152
+ if (match2 && match2[1]) {
153
+ // Return the method name
154
+ methodName = match2[1];
155
+ line.split('@')[1].split('/').pop(); // no i18n
156
+ methodName = methodName; // + '@'+ fileNameAndLine; // no i18n
157
+ break;
158
+ }
159
+ }
160
+ return methodName;
161
+ }
162
+
163
+ export { extractMethodNameFromStackTrace, trackError, trackGlobalErrorHandler };
@@ -0,0 +1,37 @@
1
+ import type { Dict } from '../types.js';
2
+ /** Who the next submission is attributed to: the picked user, or nobody. */
3
+ export interface UserChoice {
4
+ anonymous: boolean;
5
+ userid: string;
6
+ groupid: string;
7
+ }
8
+ /** Read by core/transport when it builds the feedback request's parameters. */
9
+ export declare const userChoice: UserChoice;
10
+ /** Whether the next submission carries the stored logs and diagnostics. Unset means "decide from what is stored". */
11
+ export declare const logChoice: {
12
+ includeLog?: boolean;
13
+ includeDyninfo?: boolean;
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;
21
+ /**
22
+ * The "User" dropdown above the description. It lists Anonymous plus every registered user, and
23
+ * starts on the active one (status 1) unless the caller asked for anonymous.
24
+ */
25
+ export declare function wireUserPicker(modal: HTMLElement, forceAnonymous: boolean): void;
26
+ /**
27
+ * The two "Include …" switches and the viewer behind their View links. A row is shown only when
28
+ * there is something to include, and is then on by default, so a submission carries what the app
29
+ * recorded unless the user turns it off.
30
+ */
31
+ export declare function wireLogControls(modal: HTMLElement, opts: Dict): void;
32
+ /**
33
+ * Drags the dialog by its header. The right/bottom placement from the stylesheet is converted to
34
+ * an explicit left/top on the first drag, so the offsets have something to add to, and the box is
35
+ * kept inside the viewport.
36
+ */
37
+ export declare function wireDrag(box: HTMLElement): void;