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