@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,472 @@
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 locks = require('../core/locks.js');
10
+ var env = require('../core/env.js');
11
+ var consent = require('../core/consent.js');
12
+ var transport = require('../core/transport.js');
13
+ var hooks = require('../core/hooks.js');
14
+ var appversion = require('./appversion.js');
15
+
16
+ /** Server-assigned ids, never sent back in device payloads. */
17
+ /**
18
+ * Exactly the keys deviceInfoWebSDKV2 declares, which is exactly what device/add accepts.
19
+ *
20
+ * An allow-list rather than an exclusion list: the template is a closed set of eleven mandatory
21
+ * keys, so anything else on device_info is refused as EXTRA_PARAM_FOUND and takes the whole
22
+ * registration with it. v1 carried `frameworkid` in the body; v2 moved it to a query param, and
23
+ * `platformid` — which v1 left out — became mandatory here. It comes from app/addversion, which
24
+ * is why that call has to land first.
25
+ */
26
+ const DEVICE_BODY_KEYS = ['devicetype', 'osversion', 'timezone', 'screenheight', 'screenwidth', 'referrer', 'referringdomain', 'browser', 'browserversion', 'donottrackenabled', 'platformid']; // no i18n
27
+ /** The ones the template types as int, so an empty value has to become 0 rather than ''. */
28
+ const DEVICE_BODY_NUMERIC = ['screenheight', 'screenwidth', 'donottrackenabled', 'platformid']; // no i18n
29
+ /**
30
+ * The device/add body: all eleven declared keys, always.
31
+ *
32
+ * Every one is min-occurrences="1", so a key is sent even when its value is empty — `referrer` is
33
+ * empty on any direct visit, and leaving it out would refuse the registration over a blank the
34
+ * server is happy to receive.
35
+ */
36
+ function deviceBody(info) {
37
+ var out = {};
38
+ DEVICE_BODY_KEYS.forEach(function (key) {
39
+ var value = info[key];
40
+ var numeric = DEVICE_BODY_NUMERIC.indexOf(key) !== -1;
41
+ out[key] = utils.isEmpty(value) ? (numeric ? 0 : '') : value; // no i18n
42
+ });
43
+ return JSON.stringify(out);
44
+ }
45
+ /**
46
+ * The fields that describe the device itself, and so the only ones worth a device/add when they
47
+ * change: a browser or OS upgrade, a new screen, a move between timezones, a do-not-track switch.
48
+ *
49
+ * `referrer` and `connectionType` are deliberately absent. Both change constantly — the referrer on
50
+ * most navigations, the connection on every network switch — so including them would send a
51
+ * device/add on nearly every page load. The server-assigned ids are absent for the opposite reason:
52
+ * they arrive from the server, so comparing them would report a change the moment registration
53
+ * fills them in.
54
+ */
55
+ const DEVICE_IDENTITY_KEYS = ['osversion', 'browser', 'browserversion', 'devicetype', 'screenheight', 'screenwidth', 'timezone', 'donottrackenabled',
56
+ // the SDK build this device runs: an upgrade to a new SDK version is a change the server is told about
57
+ 'frameworkid']; // no i18n
58
+ /** The identity fields alone, for comparing one stored device_info against a fresh one. */
59
+ function deviceIdentity(info) {
60
+ var identity = {};
61
+ DEVICE_IDENTITY_KEYS.forEach(function (key) {
62
+ identity[key] = info ? info[key] : undefined;
63
+ });
64
+ return identity;
65
+ }
66
+ function checkAndUpdateDeviceInfo() {
67
+ // read the stored info before it is overwritten below: addOrUpdateDeviceID compares the two to
68
+ // decide whether the server needs telling, and cannot do that once the new info has replaced it
69
+ var previous = storage.user_defaults.item('device_info'); // no i18n
70
+ // params -> https://help.apptics.com/hc/en-us/articles/115004613766-Default-Properties-Collected-by-apptics
71
+ state._ZAP._device_info = {
72
+ osversion: env.getOS(),
73
+ browser: env.getBrowser(),
74
+ browserversion: String(env.getBrowserVersion()),
75
+ donottrackenabled: consent.hasDoNotTrackEnabled() ? 1 : 0, // no i18n
76
+ devicetype: env.getDevice(),
77
+ screenheight: screen.height,
78
+ screenwidth: screen.width,
79
+ referrer: env.getReferringURL(),
80
+ connectionType: env.getConnectionType(),
81
+ referringdomain: window.location.hostname || '', // no i18n
82
+ deviceid: storage.getKeyFromDeviceInfo('deviceid'), // no i18n
83
+ anonymousid: storage.getKeyFromDeviceInfo('anonymousid'), // no i18n
84
+ // the id of the SDK build running now, never the stored one: a stored id is the build this
85
+ // device first registered with, and every later SDK version would go on reporting it
86
+ frameworkid: state._ZAP._frameworkID,
87
+ mapid: storage.getKeyFromDeviceInfo('mapid'), // no i18n
88
+ aaid: storage.getKeyFromDeviceInfo('aaid'), // no i18n
89
+ apid: storage.getKeyFromDeviceInfo('apid'), // no i18n
90
+ appversionid: storage.getKeyFromDeviceInfo('appversionid'), // no i18n
91
+ platformid: storage.getKeyFromDeviceInfo('platformid'), // no i18n
92
+ devicetypeid: storage.getKeyFromDeviceInfo('devicetypeid'), // no i18n
93
+ osversionid: storage.getKeyFromDeviceInfo('osversionid'), // no i18n
94
+ timezoneid: storage.getKeyFromDeviceInfo('timezoneid'), // no i18n
95
+ // metaWebSDKV2 needs both of these on every call: modelid comes from device/add,
96
+ // appreleaseversionid from app/addversion
97
+ modelid: storage.getKeyFromDeviceInfo('modelid'), // no i18n
98
+ appreleaseversionid: storage.getKeyFromDeviceInfo('appreleaseversionid'), // no i18n
99
+ timezone: env.getTimeZone(),
100
+ // kept here rather than as keys of their own: the id the browser is registered under, and
101
+ // whether it has had a session (a session's firstsession says so)
102
+ uuid: state._ZAP._uuid || storage.getKeyFromDeviceInfo('uuid'), // no i18n
103
+ hadsession: storage.getKeyFromDeviceInfo('hadsession') === true ? true : undefined, // no i18n
104
+ };
105
+ // the rebuild above starts from browser facts, so the ids app/addversion returned are put
106
+ // back before anything is stored or compared
107
+ appversion.applyStoredAppVersion();
108
+ storage.user_defaults.add('device_info', state._ZAP._device_info); // no i18n
109
+ addOrUpdateDeviceID(state._ZAP._device_info, previous);
110
+ }
111
+ function addOrUpdateDeviceID(device_info, previous) {
112
+ var old_device_info = previous !== undefined ? previous : storage.user_defaults.item('device_info') || {}; // no i18n
113
+ // Compare the old and new device infos and update the server if anything changed in the new.
114
+ if (device_info.deviceid == null || device_info.frameworkid == null || device_info.anonymousid == null) {
115
+ device_info.frameworkid = state._ZAP._frameworkID;
116
+ logger.Console.log('addOrUpdateDeviceID: Initiated register the device'); // no i18n
117
+ registerDeviceWithApptics().then(function (result_device_info) {
118
+ if (utils.isSuccess(result_device_info)) {
119
+ logger.Console.log('addOrUpdateDeviceID: Success register the device ' + JSON.stringify(result_device_info.data)); // no i18n
120
+ logger.Console.dir(result_device_info.data);
121
+ }
122
+ else {
123
+ logger.Console.log('addOrUpdateDeviceID: Error registering the device: ' + result_device_info); // no i18n
124
+ }
125
+ }).catch(function (err) {
126
+ logger.Console.log('addOrUpdateDeviceID: Error registering the device: ' + err); // no i18n
127
+ });
128
+ }
129
+ else if (!utils.isEmpty(old_device_info) && !utils.compareTwoObjects(deviceIdentity(device_info), deviceIdentity(old_device_info))) {
130
+ // the device itself changed — a browser or OS upgrade, a new screen, a different timezone.
131
+ // The update carries the stored deviceid as a param and the stored uuid as a header, so the
132
+ // server amends this device rather than creating another one: same device, new details
133
+ logger.Console.log('addOrUpdateDeviceID: Device info changed, so updating new changes'); // no i18n
134
+ logger.Console.dir(deviceIdentity(device_info));
135
+ updateDeviceWithApptics(device_info).catch(function (err) {
136
+ logger.Console.log('addOrUpdateDeviceID: Error updating the device: ' + err); // no i18n
137
+ });
138
+ }
139
+ }
140
+ /**
141
+ * The ids device/add is allowed to set. Each is taken only when the answer actually carries it.
142
+ *
143
+ * device/add is not the only source of these: app/addversion runs first and is the *only* source
144
+ * of aaid, mapid, appversionid and appreleaseversionid. Its answer is already on _device_info by
145
+ * the time registration replies, and v2's device/add does not repeat all of it - it answers about
146
+ * the device, not the build. Assigning every key unconditionally therefore overwrote ids we had
147
+ * with undefined, which is how aaid and appversionid reached metaWebSDKV2 as null and mapid went
148
+ * out as an empty header.
149
+ */
150
+ const DEVICE_RESPONSE_KEYS = ['deviceid', 'anonymousid', 'aaid', 'apid', 'mapid', 'appversionid', 'osversionid', 'timezoneid', 'platformid', 'devicetypeid', 'modelid']; // no i18n
151
+ /** Copies server-assigned ids into state and persists device info; resolves once it is stored. */
152
+ function applyDeviceResponse(data) {
153
+ DEVICE_RESPONSE_KEYS.forEach(function (key) {
154
+ // an id the answer does not mention is one we already hold, or one we never had:
155
+ // either way what is on _device_info is better than undefined
156
+ if (!utils.isEmpty(data[key])) {
157
+ state._ZAP._device_info[key] = data[key];
158
+ }
159
+ });
160
+ // apid and mapid are also read straight off state for the request headers
161
+ if (!utils.isEmpty(data.apid)) {
162
+ state._ZAP._apid = data.apid;
163
+ }
164
+ if (!utils.isEmpty(data.mapid)) {
165
+ state._ZAP._mapid = data.mapid;
166
+ }
167
+ // stored before the registration counts as done, so another tab waiting to register reads it
168
+ return storage.user_defaults.save('device_info', state._ZAP._device_info); // no i18n
169
+ }
170
+ /**
171
+ * The browser's uuid: the one in storage, or a new one stored now. Every tab of the site uses the
172
+ * same one, because the device is registered under it.
173
+ */
174
+ function useStoredUuid() {
175
+ // kept in device_info: it is the id this browser is registered under
176
+ var uuid = storage.getKeyFromDeviceInfo('uuid'); // no i18n
177
+ if (utils.isEmpty(uuid)) {
178
+ uuid = utils.getNewUUID();
179
+ storage.setDeviceInfoKey('uuid', uuid); // no i18n
180
+ }
181
+ state._ZAP._uuid = uuid;
182
+ }
183
+ /**
184
+ * The ids another tab of this site registered after this one loaded its settings: those are in
185
+ * storage but not in this tab's copy. Resolves with them, applied to this tab, or null when the
186
+ * browser is still unregistered and this tab has to register it.
187
+ */
188
+ function adoptRegisteredDevice() {
189
+ return storage.reloadSettings(['device_info']).then(function () {
190
+ var stored = storage.user_defaults.item('device_info') || {}; // no i18n
191
+ if (utils.isEmpty(stored.deviceid) || utils.isEmpty(stored.anonymousid)) {
192
+ return null;
193
+ }
194
+ // the device is registered under the uuid the other tab sent
195
+ if (!utils.isEmpty(stored.uuid)) {
196
+ state._ZAP._uuid = stored.uuid;
197
+ }
198
+ logger.Console.log('registerDeviceWithApptics: another tab registered this browser, using its device ' + stored.deviceid); // no i18n
199
+ return applyDeviceResponse(stored).then(function () {
200
+ return { result: 'success', data: stored }; // no i18n
201
+ });
202
+ });
203
+ }
204
+ /**
205
+ * Sets up this browser's device at start-up: the uuid, the device info, and a registration when
206
+ * the browser has none.
207
+ *
208
+ * Settings are this tab's own copy, loaded when it started. On a browser with no device yet, two
209
+ * tabs started together would each register it and store two devices, so the first visit runs
210
+ * under a lock shared by every tab of the site: the tab that gets it re-reads storage, registers
211
+ * only when no other tab has, and keeps the lock until the new ids are stored. The next tab then
212
+ * finds them. Start-up goes on as soon as the device info is set, without waiting for the server.
213
+ */
214
+ function prepareDevice() {
215
+ var cached = storage.user_defaults.item('device_info') || {}; // no i18n
216
+ if (!utils.isEmpty(cached.deviceid) && !utils.isEmpty(cached.anonymousid)) {
217
+ useStoredUuid();
218
+ checkAndUpdateDeviceInfo();
219
+ return Promise.resolve();
220
+ }
221
+ return new Promise(function (resolve) {
222
+ locks.withLock('device', function () {
223
+ return storage.reloadSettings(['device_info']).then(function () {
224
+ useStoredUuid();
225
+ checkAndUpdateDeviceInfo();
226
+ resolve();
227
+ // held until a registration this started is stored, or has failed
228
+ return deviceReady();
229
+ });
230
+ }).catch(function (err) {
231
+ logger.Console.log('prepareDevice: ' + err); // no i18n
232
+ }).then(function () {
233
+ resolve();
234
+ });
235
+ });
236
+ }
237
+ /** True while the device has no server ids, so nothing can be sent yet. */
238
+ function needsDevice() {
239
+ // the app ids from app/addversion, and the deviceid from device/add: either missing, and the
240
+ // device is registered before anything stored is sent
241
+ return (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid'))) || !transport.hasDeviceId(); // no i18n
242
+ }
243
+ /**
244
+ * Resolves once a registration that is in flight has settled, succeeded or not, and right away
245
+ * when none is running. Callers use it to wait for the ids rather than poll for them.
246
+ */
247
+ function deviceReady() {
248
+ if (!state._ZAP._regDeviceSending) {
249
+ return Promise.resolve();
250
+ }
251
+ return new Promise(function (resolve) {
252
+ state._ZAP._regDeviceResolve.push(function () { resolve(); });
253
+ state._ZAP._regDeviceReject.push(function () { resolve(); });
254
+ });
255
+ }
256
+ /** Ends an in-flight registration: clears the flag and settles every caller that waited on it. */
257
+ function settleRegistration(ok, value) {
258
+ var waiters = ok ? state._ZAP._regDeviceResolve : state._ZAP._regDeviceReject;
259
+ state._ZAP._regDeviceSending = false;
260
+ state._ZAP._regDeviceResolve = [];
261
+ state._ZAP._regDeviceReject = [];
262
+ waiters.forEach(function (fn) {
263
+ fn(value);
264
+ });
265
+ }
266
+ // Function to register the device
267
+ function registerDeviceWithApptics() {
268
+ var label = 'registerDeviceWithApptics'; // no i18n
269
+ var blocked = transport.rejectIfDisabled(label);
270
+ if (blocked) {
271
+ return blocked;
272
+ }
273
+ if (!state._ZAP._token) {
274
+ logger.Console.warn(label + ': You must call Apptics.init() first'); // no i18n
275
+ return Promise.reject(label + ': You must call Apptics.init() first'); // no i18n
276
+ }
277
+ var offline = transport.rejectIfOffline(label);
278
+ if (offline) {
279
+ return offline;
280
+ }
281
+ if (state._ZAP._device_info.deviceid && state._ZAP._device_info.anonymousid) {
282
+ return Promise.resolve(state._ZAP._device_info);
283
+ }
284
+ return new Promise((resolve, reject) => {
285
+ if (state._ZAP._regDeviceSending) {
286
+ // a registration is already in flight; settle together with it
287
+ state._ZAP._regDeviceResolve.push(resolve);
288
+ state._ZAP._regDeviceReject.push(reject);
289
+ return;
290
+ }
291
+ state._ZAP._regDeviceSending = true;
292
+ adoptRegisteredDevice().then(function (adopted) {
293
+ if (adopted) {
294
+ return adopted;
295
+ }
296
+ var payload = deviceBody(state._ZAP._device_info);
297
+ // device/add is the one url that allows all four: zak and uuid identify the caller,
298
+ // apid and mapid the app. app/addversion has already answered with apid and mapid by
299
+ // the time registration runs, so the first call can carry them like every later one.
300
+ var headers = { zak: transport.getHeaderParams('zak'), uuid: transport.getHeaderParams('uuid'), ...transport.apiHeaders() }; // no i18n
301
+ return transport.postToApptics('regdevice', payload, headers, label).then(function (result) {
302
+ return applyDeviceResponse(result.data || {}).then(function () {
303
+ return result;
304
+ });
305
+ });
306
+ }).then(function (result) {
307
+ settleRegistration(true, result);
308
+ resolve(result);
309
+ // first visit: the ids exist now, so fetch the server settings right away
310
+ refreshServerSettings();
311
+ }).catch(function (err) {
312
+ settleRegistration(false, err);
313
+ reject(err);
314
+ });
315
+ });
316
+ }
317
+ // Function to update the device
318
+ function updateDeviceWithApptics(device_info) {
319
+ var label = 'updateDeviceWithApptics'; // no i18n
320
+ var blocked = transport.rejectIfDisabled(label) || transport.rejectIfOffline(label);
321
+ if (blocked) {
322
+ return blocked;
323
+ }
324
+ var payload = deviceBody(device_info);
325
+ var headers = { zak: transport.getHeaderParams('zak'), uuid: transport.getHeaderParams('uuid'), ...transport.apiHeaders() }; // no i18n
326
+ return transport.postToApptics('regdevice', payload, headers, label).then(function (result) {
327
+ applyDeviceResponse(result.data || {});
328
+ return result;
329
+ });
330
+ }
331
+ // Function to get updates from the server
332
+ function getUpdatesFromServer() {
333
+ var label = 'getUpdatesFromServer'; // no i18n
334
+ var blocked = transport.rejectIfOffline(label) || transport.rejectIfNoDeviceIds(label);
335
+ if (blocked) {
336
+ return blocked;
337
+ }
338
+ // getsdkconfig takes no body: the ids it needs are parameters
339
+ return transport.postToApptics('sdkconfig', '', transport.apiHeaders(), label).then(function (result) {
340
+ var data = result.data || {};
341
+ applyServerFlags(data);
342
+ // the visitor's tracking status lives in trackinfo too, and is not the server's to replace
343
+ var saved = storage.storedTrackingStatus();
344
+ storage.user_defaults.add('trackinfo', saved === undefined ? state._ZAP._trackinfo : Object.assign({}, state._ZAP._trackinfo, { tracking_status: saved })); // no i18n
345
+ logger.Console.log('getUpdatesFromServer: ' + JSON.stringify(state._ZAP._trackinfo)); // no i18n
346
+ return data;
347
+ });
348
+ }
349
+ /**
350
+ * Whether getsdkconfig says a module is on.
351
+ *
352
+ * Absent means on: a server that does not report a module has not turned it off, and an SDK that
353
+ * went quiet because of a key it had never heard of would be impossible to diagnose.
354
+ */
355
+ function moduleEnabled(status, id) {
356
+ var entry = status[id];
357
+ if (!entry || typeof entry !== 'object') {
358
+ return true;
359
+ }
360
+ return entry.enabled !== false;
361
+ }
362
+ /**
363
+ * Applies the server's tracking flags, from a getsdkconfig response or the saved trackinfo.
364
+ *
365
+ * v2 reports a flag per module, keyed by a numeric id, rather than the loose booleans getupdates
366
+ * answered with. Both shapes are read: the stored trackinfo from a previous version of this SDK
367
+ * is in the old shape, and discarding it would mean a visit tracking things the server had
368
+ * already said to stop.
369
+ */
370
+ function applyServerFlags(data) {
371
+ var status = (data.tracking_status && typeof data.tracking_status === 'object'
372
+ ? data.tracking_status : {});
373
+ var hasModules = Object.keys(status).length > 0;
374
+ if (data.hasOwnProperty('tracking_enabled')) { // no i18n
375
+ state._ZAP._shouldTrack = data.tracking_enabled !== false;
376
+ }
377
+ else {
378
+ // the old shape: archived versions and untracked customer groups
379
+ var versionArchived = !data.hasOwnProperty('versionarchivestatus') || utils.isEmpty(data.versionarchivestatus)
380
+ ? true : data.versionarchivestatus;
381
+ var customerGroupTracking = !data.hasOwnProperty('customergrouptracking') || utils.isEmpty(data.customergrouptracking)
382
+ ? true : data.customergrouptracking;
383
+ state._ZAP._shouldTrack = versionArchived || !customerGroupTracking;
384
+ }
385
+ if (hasModules) {
386
+ // sessions carry screens, events and api calls, so module 11 gates all of engagement
387
+ state._ZAP._engagementTracking = moduleEnabled(status, constants.SDK_MODULE.SESSION);
388
+ state._ZAP._errorTracking = moduleEnabled(status, constants.SDK_MODULE.CRASH);
389
+ state._ZAP._heatmapTracking = moduleEnabled(status, constants.SDK_MODULE.HEATMAP);
390
+ state._ZAP._remoteLoggerEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_LOGGER);
391
+ state._ZAP._feedbackEnabled = moduleEnabled(status, constants.SDK_MODULE.FEEDBACK);
392
+ // getsdkconfig is the authority on this one: getupdates reports it too and the two can
393
+ // disagree while both are in flight, so what is read here wins
394
+ state._ZAP._remoteConfigEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_CONFIG);
395
+ }
396
+ else {
397
+ state._ZAP._engagementTracking = data.engagementtracking !== false;
398
+ state._ZAP._errorTracking = data.errortracking !== false;
399
+ state._ZAP._heatmapTracking = data.heatmaptracking !== false;
400
+ state._ZAP._remoteLoggerEnabled = data.remoteloggerenabled !== false;
401
+ state._ZAP._feedbackEnabled = data.feedbackenabled !== false;
402
+ }
403
+ // screen recording, set up in the console: everything the form needs comes from here
404
+ var quartz = (data.quartz && typeof data.quartz === 'object' ? data.quartz : {});
405
+ var text = function (value) {
406
+ return typeof value === 'string' ? value.trim() : ''; // no i18n
407
+ };
408
+ state._ZAP._quartz = {
409
+ enabled: quartz.hasOwnProperty('quartz_enabled') ? quartz.quartz_enabled === true : data.quartzenabled === true, // no i18n
410
+ workspace: text(quartz.quartz_workspace),
411
+ department: text(quartz.quartz_department),
412
+ domain: text(quartz.quartz_domain),
413
+ cdnUrl: text(quartz.quartz_cdn_url),
414
+ };
415
+ // when to ask again, which replaces polling on a fixed interval
416
+ if (typeof data.next_check_after === 'number' && data.next_check_after > 0) {
417
+ state._ZAP._nextConfigCheck = data.next_check_after;
418
+ }
419
+ state._ZAP._trackinfo = {
420
+ engagementtracking: state._ZAP._engagementTracking,
421
+ errortracking: state._ZAP._errorTracking,
422
+ heatmaptracking: state._ZAP._heatmapTracking,
423
+ remoteloggerenabled: state._ZAP._remoteLoggerEnabled,
424
+ feedbackenabled: state._ZAP._feedbackEnabled,
425
+ // in the server's own shape, so restoring it on the next visit reads it the same way
426
+ quartz: {
427
+ quartz_enabled: state._ZAP._quartz.enabled,
428
+ quartz_workspace: state._ZAP._quartz.workspace,
429
+ quartz_department: state._ZAP._quartz.department,
430
+ quartz_domain: state._ZAP._quartz.domain,
431
+ quartz_cdn_url: state._ZAP._quartz.cdnUrl,
432
+ },
433
+ shouldtrack: state._ZAP._shouldTrack,
434
+ next_check_after: state._ZAP._nextConfigCheck
435
+ };
436
+ }
437
+ /**
438
+ * Start-up: applies the server settings saved by the last getupdates, so a "stop tracking"
439
+ * answer holds across page loads instead of resetting to "on" until the next check.
440
+ */
441
+ function restoreServerSettings() {
442
+ var saved = storage.user_defaults.item('trackinfo'); // no i18n
443
+ if (saved && typeof saved === 'object') { // no i18n
444
+ applyServerFlags(saved);
445
+ logger.Console.log('restoreServerSettings: ' + JSON.stringify(state._ZAP._trackinfo)); // no i18n
446
+ }
447
+ }
448
+ /**
449
+ * Fetches the server settings now if the device is registered. On a first visit the ids don't
450
+ * exist yet; registerDeviceWithApptics calls this as soon as device/add succeeds.
451
+ */
452
+ function refreshServerSettings() {
453
+ if (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid'))) { // no i18n
454
+ return;
455
+ }
456
+ getUpdatesFromServer().catch(function (err) {
457
+ logger.Console.log(err);
458
+ });
459
+ }
460
+ hooks.hooks.updateDevice = updateDeviceWithApptics;
461
+ hooks.hooks.refreshDeviceInfo = checkAndUpdateDeviceInfo;
462
+
463
+ exports.addOrUpdateDeviceID = addOrUpdateDeviceID;
464
+ exports.checkAndUpdateDeviceInfo = checkAndUpdateDeviceInfo;
465
+ exports.deviceReady = deviceReady;
466
+ exports.getUpdatesFromServer = getUpdatesFromServer;
467
+ exports.needsDevice = needsDevice;
468
+ exports.prepareDevice = prepareDevice;
469
+ exports.refreshServerSettings = refreshServerSettings;
470
+ exports.registerDeviceWithApptics = registerDeviceWithApptics;
471
+ exports.restoreServerSettings = restoreServerSettings;
472
+ exports.updateDeviceWithApptics = updateDeviceWithApptics;
@@ -0,0 +1,49 @@
1
+ /** The rules, as ApiTrackingBuilder.build() returns them. Every list may be empty. */
2
+ export interface ApiTrackingConfig {
3
+ /** URLs recorded, exactly; with any allow rule, only calls matching one are recorded. */
4
+ allowEquals: string[];
5
+ /** URLs never recorded, exactly. */
6
+ ignoreEquals: string[];
7
+ /** Regular expressions a recorded URL may match; with any allow rule, it must match one. */
8
+ allowRegex: RegExp[];
9
+ /** Regular expressions whose URLs are never recorded. */
10
+ ignoreRegex: RegExp[];
11
+ }
12
+ /**
13
+ * A URL as the rules compare it: `scheme://host[:port]/path`, the scheme and host lower-cased,
14
+ * the default port dropped, the query, fragment and a trailing slash removed. '' when it is not
15
+ * an absolute http(s) URL.
16
+ */
17
+ export declare function ruleUrl(raw: string): string;
18
+ /**
19
+ * Builds the rules configure takes. Each method may be called more than once, and takes any number
20
+ * of values: `new ApiTrackingBuilder().allowEquals(a, b).ignoreRegex(/x/).build()`.
21
+ */
22
+ export declare class ApiTrackingBuilder {
23
+ private rules;
24
+ /** Records only these URLs (and those any allowRegex matches): `https://api.example.com/orders`. */
25
+ allowEquals(...urls: string[]): this;
26
+ /** Never records these URLs: `https://api.example.com/health`. */
27
+ ignoreEquals(...urls: string[]): this;
28
+ /** Records only URLs these match (and those any allowEquals names): `/^https:\/\/api\.example\.com\//`. */
29
+ allowRegex(...patterns: Array<RegExp | string>): this;
30
+ /** Never records URLs these match: `/\/internal\//`. */
31
+ ignoreRegex(...patterns: Array<RegExp | string>): this;
32
+ build(): ApiTrackingConfig;
33
+ }
34
+ /**
35
+ * Applies the rules, replacing any given before. Takes what build() returned, or the builder
36
+ * itself. Call it once, before the requests it should cover; it works before init().
37
+ */
38
+ export declare function configureApiTracking(config: ApiTrackingConfig | ApiTrackingBuilder): void;
39
+ /** The rules in force, for tests and the playground. */
40
+ export declare function apiTrackingConfig(): ApiTrackingConfig;
41
+ /**
42
+ * Whether a call to `url` is recorded. Ignore rules are tried first and always win; then, when
43
+ * there are allow rules, the URL must match one. A URL that cannot be read is not recorded.
44
+ */
45
+ export declare function urlTracked(url: string): boolean;
46
+ /** The path without its query string or fragment, which is where ids and tokens travel. */
47
+ export declare function withoutQuery(path: string): string;
48
+ /** The path a call is recorded under: the query goes, and numeric ids, UUIDs and JWTs become `*`. */
49
+ export declare function normalizeApiPath(path: string): string;