@zoho_apptics/apptics-js-sdk 2.0.0-beta → 2.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/CHANGELOG.md +191 -21
  2. package/README.md +519 -333
  3. package/dist/apptics.esm.min.js +2 -2
  4. package/dist/apptics.esm.min.js.map +1 -1
  5. package/dist/apptics.min.js +2 -2
  6. package/dist/apptics.min.js.map +1 -1
  7. package/dist/chunks/lifecycle-C6m1W7jo.min.js +3 -0
  8. package/dist/chunks/lifecycle-C6m1W7jo.min.js.map +1 -0
  9. package/dist/chunks/send-CgUO82ne.min.js +3 -0
  10. package/dist/chunks/send-CgUO82ne.min.js.map +1 -0
  11. package/dist/cjs/core/attempts.js +1 -1
  12. package/dist/cjs/core/batch.js +2 -2
  13. package/dist/cjs/core/consent.d.ts +18 -4
  14. package/dist/cjs/core/consent.js +31 -15
  15. package/dist/cjs/core/constants.d.ts +57 -27
  16. package/dist/cjs/core/constants.js +52 -33
  17. package/dist/cjs/core/db.d.ts +12 -4
  18. package/dist/cjs/core/db.js +167 -34
  19. package/dist/cjs/core/debug.js +1 -1
  20. package/dist/cjs/core/env.d.ts +0 -3
  21. package/dist/cjs/core/env.js +1 -9
  22. package/dist/cjs/core/framework-id.d.ts +1 -1
  23. package/dist/cjs/core/framework-id.js +3 -3
  24. package/dist/cjs/core/guard.d.ts +9 -0
  25. package/dist/cjs/core/guard.js +27 -13
  26. package/dist/cjs/core/hooks.d.ts +7 -4
  27. package/dist/cjs/core/hooks.js +3 -3
  28. package/dist/cjs/core/locks.d.ts +9 -0
  29. package/dist/cjs/core/locks.js +22 -0
  30. package/dist/cjs/core/logger.d.ts +3 -4
  31. package/dist/cjs/core/logger.js +4 -40
  32. package/dist/cjs/core/state.d.ts +9 -6
  33. package/dist/cjs/core/state.js +3 -5
  34. package/dist/cjs/core/storage.d.ts +20 -5
  35. package/dist/cjs/core/storage.js +115 -20
  36. package/dist/cjs/core/stylesheet.d.ts +8 -0
  37. package/dist/cjs/core/stylesheet.js +29 -0
  38. package/dist/cjs/core/transport.d.ts +2 -0
  39. package/dist/cjs/core/transport.js +30 -18
  40. package/dist/cjs/core/utils.js +1 -1
  41. package/dist/cjs/core/validate.js +2 -2
  42. package/dist/cjs/device/appversion.d.ts +10 -6
  43. package/dist/cjs/device/appversion.js +38 -3
  44. package/dist/cjs/device/index.d.ts +28 -2
  45. package/dist/cjs/device/index.js +312 -44
  46. package/dist/cjs/engagement/api-rules.d.ts +32 -67
  47. package/dist/cjs/engagement/api-rules.js +144 -157
  48. package/dist/cjs/engagement/auto-api.js +2 -2
  49. package/dist/cjs/engagement/auto-screen.d.ts +2 -0
  50. package/dist/cjs/engagement/auto-screen.js +67 -3
  51. package/dist/cjs/engagement/defined-events.js +1 -1
  52. package/dist/cjs/engagement/send.js +12 -3
  53. package/dist/cjs/engagement/session.d.ts +29 -3
  54. package/dist/cjs/engagement/session.js +253 -26
  55. package/dist/cjs/engagement/track.d.ts +4 -4
  56. package/dist/cjs/engagement/track.js +58 -30
  57. package/dist/cjs/errors/send.js +2 -2
  58. package/dist/cjs/errors/track.d.ts +2 -1
  59. package/dist/cjs/errors/track.js +49 -7
  60. package/dist/cjs/feedback/controls.d.ts +12 -0
  61. package/dist/cjs/feedback/controls.js +104 -25
  62. package/dist/cjs/feedback/index.d.ts +8 -7
  63. package/dist/cjs/feedback/index.js +32 -13
  64. package/dist/cjs/feedback/logs.d.ts +13 -11
  65. package/dist/cjs/feedback/logs.js +57 -28
  66. package/dist/cjs/feedback/quartz.d.ts +18 -0
  67. package/dist/cjs/feedback/quartz.js +105 -0
  68. package/dist/cjs/feedback/send.d.ts +5 -2
  69. package/dist/cjs/feedback/send.js +30 -28
  70. package/dist/cjs/feedback/styles.d.ts +10 -0
  71. package/dist/cjs/feedback/styles.js +8 -0
  72. package/dist/cjs/feedback/template.d.ts +1 -1
  73. package/dist/cjs/feedback/template.js +20 -12
  74. package/dist/cjs/feedback/ui.d.ts +21 -6
  75. package/dist/cjs/feedback/ui.js +283 -70
  76. package/dist/cjs/heatmap/send.d.ts +2 -0
  77. package/dist/cjs/heatmap/send.js +3 -5
  78. package/dist/cjs/heatmap/track.d.ts +1 -1
  79. package/dist/cjs/heatmap/track.js +87 -16
  80. package/dist/cjs/index.d.ts +16 -63
  81. package/dist/cjs/index.js +144 -95
  82. package/dist/cjs/lifecycle.d.ts +21 -3
  83. package/dist/cjs/lifecycle.js +353 -89
  84. package/dist/cjs/remoteconfig/country.d.ts +4 -0
  85. package/dist/cjs/remoteconfig/country.js +73 -0
  86. package/dist/cjs/remoteconfig/custom.d.ts +7 -7
  87. package/dist/cjs/remoteconfig/custom.js +31 -51
  88. package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
  89. package/dist/cjs/remoteconfig/evaluate.js +56 -43
  90. package/dist/cjs/remoteconfig/fetch.d.ts +16 -9
  91. package/dist/cjs/remoteconfig/fetch.js +120 -39
  92. package/dist/cjs/remoteconfig/index.d.ts +19 -8
  93. package/dist/cjs/remoteconfig/index.js +81 -39
  94. package/dist/cjs/remoteconfig/store.d.ts +15 -7
  95. package/dist/cjs/remoteconfig/store.js +68 -35
  96. package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
  97. package/dist/cjs/remoteconfig/tz-countries.js +13 -0
  98. package/dist/{esm/core → cjs/remotelog}/callsite.d.ts +11 -4
  99. package/dist/cjs/{core → remotelog}/callsite.js +30 -7
  100. package/dist/cjs/remotelog/gate.d.ts +2 -11
  101. package/dist/cjs/remotelog/gate.js +9 -35
  102. package/dist/cjs/remotelog/index.d.ts +4 -21
  103. package/dist/cjs/remotelog/logger.d.ts +11 -17
  104. package/dist/cjs/remotelog/logger.js +31 -59
  105. package/dist/cjs/remotelog/send.js +11 -11
  106. package/dist/cjs/remotelogger/index.d.ts +12 -0
  107. package/dist/cjs/remotelogger/index.js +18 -0
  108. package/dist/cjs/scheduler.d.ts +3 -1
  109. package/dist/cjs/scheduler.js +25 -22
  110. package/dist/cjs/types.d.ts +208 -90
  111. package/dist/cjs/user/index.js +16 -7
  112. package/dist/esm/core/attempts.js +1 -1
  113. package/dist/esm/core/batch.js +2 -2
  114. package/dist/esm/core/consent.d.ts +18 -4
  115. package/dist/esm/core/consent.js +32 -17
  116. package/dist/esm/core/constants.d.ts +57 -27
  117. package/dist/esm/core/constants.js +51 -33
  118. package/dist/esm/core/db.d.ts +12 -4
  119. package/dist/esm/core/db.js +164 -35
  120. package/dist/esm/core/debug.js +1 -1
  121. package/dist/esm/core/env.d.ts +0 -3
  122. package/dist/esm/core/env.js +2 -9
  123. package/dist/esm/core/framework-id.d.ts +1 -1
  124. package/dist/esm/core/framework-id.js +3 -3
  125. package/dist/esm/core/guard.d.ts +9 -0
  126. package/dist/esm/core/guard.js +27 -14
  127. package/dist/esm/core/hooks.d.ts +7 -4
  128. package/dist/esm/core/hooks.js +3 -3
  129. package/dist/esm/core/locks.d.ts +9 -0
  130. package/dist/esm/core/locks.js +20 -0
  131. package/dist/esm/core/logger.d.ts +3 -4
  132. package/dist/esm/core/logger.js +4 -40
  133. package/dist/esm/core/state.d.ts +9 -6
  134. package/dist/esm/core/state.js +3 -5
  135. package/dist/esm/core/storage.d.ts +20 -5
  136. package/dist/esm/core/storage.js +113 -21
  137. package/dist/esm/core/stylesheet.d.ts +8 -0
  138. package/dist/esm/core/stylesheet.js +27 -0
  139. package/dist/esm/core/transport.d.ts +2 -0
  140. package/dist/esm/core/transport.js +30 -19
  141. package/dist/esm/core/utils.js +1 -1
  142. package/dist/esm/core/validate.js +2 -2
  143. package/dist/esm/device/appversion.d.ts +10 -6
  144. package/dist/esm/device/appversion.js +37 -4
  145. package/dist/esm/device/index.d.ts +28 -2
  146. package/dist/esm/device/index.js +313 -49
  147. package/dist/esm/engagement/api-rules.d.ts +32 -67
  148. package/dist/esm/engagement/api-rules.js +143 -155
  149. package/dist/esm/engagement/auto-api.js +3 -3
  150. package/dist/esm/engagement/auto-screen.d.ts +2 -0
  151. package/dist/esm/engagement/auto-screen.js +67 -4
  152. package/dist/esm/engagement/defined-events.js +1 -1
  153. package/dist/esm/engagement/send.js +12 -3
  154. package/dist/esm/engagement/session.d.ts +29 -3
  155. package/dist/esm/engagement/session.js +251 -30
  156. package/dist/esm/engagement/track.d.ts +4 -4
  157. package/dist/esm/engagement/track.js +65 -37
  158. package/dist/esm/errors/send.js +2 -2
  159. package/dist/esm/errors/track.d.ts +2 -1
  160. package/dist/esm/errors/track.js +49 -7
  161. package/dist/esm/feedback/controls.d.ts +12 -0
  162. package/dist/esm/feedback/controls.js +103 -26
  163. package/dist/esm/feedback/index.d.ts +8 -7
  164. package/dist/esm/feedback/index.js +34 -13
  165. package/dist/esm/feedback/logs.d.ts +13 -11
  166. package/dist/esm/feedback/logs.js +59 -30
  167. package/dist/esm/feedback/quartz.d.ts +18 -0
  168. package/dist/esm/feedback/quartz.js +99 -0
  169. package/dist/esm/feedback/send.d.ts +5 -2
  170. package/dist/esm/feedback/send.js +31 -31
  171. package/dist/esm/feedback/styles.d.ts +10 -0
  172. package/dist/esm/feedback/styles.js +4 -0
  173. package/dist/esm/feedback/template.d.ts +1 -1
  174. package/dist/esm/feedback/template.js +20 -12
  175. package/dist/esm/feedback/ui.d.ts +21 -6
  176. package/dist/esm/feedback/ui.js +283 -71
  177. package/dist/esm/heatmap/send.d.ts +2 -0
  178. package/dist/esm/heatmap/send.js +3 -5
  179. package/dist/esm/heatmap/track.d.ts +1 -1
  180. package/dist/esm/heatmap/track.js +87 -16
  181. package/dist/esm/index.d.ts +16 -63
  182. package/dist/esm/index.js +148 -83
  183. package/dist/esm/lifecycle.d.ts +21 -3
  184. package/dist/esm/lifecycle.js +359 -98
  185. package/dist/esm/remoteconfig/country.d.ts +4 -0
  186. package/dist/esm/remoteconfig/country.js +70 -0
  187. package/dist/esm/remoteconfig/custom.d.ts +7 -7
  188. package/dist/esm/remoteconfig/custom.js +32 -52
  189. package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
  190. package/dist/esm/remoteconfig/evaluate.js +57 -44
  191. package/dist/esm/remoteconfig/fetch.d.ts +16 -9
  192. package/dist/esm/remoteconfig/fetch.js +120 -39
  193. package/dist/esm/remoteconfig/index.d.ts +19 -8
  194. package/dist/esm/remoteconfig/index.js +82 -40
  195. package/dist/esm/remoteconfig/store.d.ts +15 -7
  196. package/dist/esm/remoteconfig/store.js +68 -36
  197. package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
  198. package/dist/esm/remoteconfig/tz-countries.js +11 -0
  199. package/dist/{cjs/core → esm/remotelog}/callsite.d.ts +11 -4
  200. package/dist/esm/{core → remotelog}/callsite.js +30 -8
  201. package/dist/esm/remotelog/gate.d.ts +2 -11
  202. package/dist/esm/remotelog/gate.js +10 -32
  203. package/dist/esm/remotelog/index.d.ts +4 -21
  204. package/dist/esm/remotelog/logger.d.ts +11 -17
  205. package/dist/esm/remotelog/logger.js +33 -61
  206. package/dist/esm/remotelog/send.js +13 -13
  207. package/dist/esm/remotelogger/index.d.ts +12 -0
  208. package/dist/esm/remotelogger/index.js +13 -0
  209. package/dist/esm/scheduler.d.ts +3 -1
  210. package/dist/esm/scheduler.js +26 -24
  211. package/dist/esm/types.d.ts +208 -90
  212. package/dist/esm/user/index.js +18 -8
  213. package/dist/feedback.esm.min.js +2 -2
  214. package/dist/feedback.esm.min.js.map +1 -1
  215. package/dist/remoteconfig.esm.min.js +3 -0
  216. package/dist/remoteconfig.esm.min.js.map +1 -0
  217. package/dist/remotelogger.esm.min.js +3 -0
  218. package/dist/remotelogger.esm.min.js.map +1 -0
  219. package/package.json +16 -17
  220. package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
  221. package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
  222. package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
  223. package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
  224. package/dist/cjs/auto-init.d.ts +0 -8
  225. package/dist/cjs/auto-init.js +0 -32
  226. package/dist/cjs/heatmap/index.d.ts +0 -16
  227. package/dist/cjs/heatmap/index.js +0 -22
  228. package/dist/cjs/remotelog/index.js +0 -55
  229. package/dist/esm/auto-init.d.ts +0 -8
  230. package/dist/esm/auto-init.js +0 -29
  231. package/dist/esm/heatmap/index.d.ts +0 -16
  232. package/dist/esm/heatmap/index.js +0 -17
  233. package/dist/esm/remotelog/index.js +0 -51
  234. package/dist/heatmap.esm.min.js +0 -3
  235. package/dist/heatmap.esm.min.js.map +0 -1
  236. package/dist/style.css +0 -1
@@ -1,4 +1,4 @@
1
- /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.2 | (c) Zoho Corporation Private Limited | see LICENSE */
2
2
  'use strict';
3
3
 
4
4
  var constants = require('../core/constants.js');
@@ -6,6 +6,7 @@ var state = require('../core/state.js');
6
6
  var logger = require('../core/logger.js');
7
7
  var utils = require('../core/utils.js');
8
8
  var storage = require('../core/storage.js');
9
+ var locks = require('../core/locks.js');
9
10
  var env = require('../core/env.js');
10
11
  var consent = require('../core/consent.js');
11
12
  var transport = require('../core/transport.js');
@@ -95,7 +96,11 @@ function checkAndUpdateDeviceInfo() {
95
96
  // appreleaseversionid from app/addversion
96
97
  modelid: storage.getKeyFromDeviceInfo('modelid'), // no i18n
97
98
  appreleaseversionid: storage.getKeyFromDeviceInfo('appreleaseversionid'), // no i18n
98
- timezone: env.getTimeZone()
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
99
104
  };
100
105
  // the rebuild above starts from browser facts, so the ids app/addversion returned are put
101
106
  // back before anything is stored or compared
@@ -103,10 +108,18 @@ function checkAndUpdateDeviceInfo() {
103
108
  storage.user_defaults.add('device_info', state._ZAP._device_info); // no i18n
104
109
  addOrUpdateDeviceID(state._ZAP._device_info, previous);
105
110
  }
111
+ /**
112
+ * Whether device/add has registered this device: it has a deviceid. anonymousid is not required:
113
+ * not every Apptics server sends one, nothing the SDK sends carries it, and requiring it had a
114
+ * server that leaves it out register the same device again on every page load.
115
+ */
116
+ function isRegistered(info) {
117
+ return !!info && !utils.isEmpty(info.deviceid);
118
+ }
106
119
  function addOrUpdateDeviceID(device_info, previous) {
107
120
  var old_device_info = previous !== undefined ? previous : storage.user_defaults.item('device_info') || {}; // no i18n
108
121
  // Compare the old and new device infos and update the server if anything changed in the new.
109
- if (device_info.deviceid == null || device_info.frameworkid == null || device_info.anonymousid == null) {
122
+ if (!isRegistered(device_info) || device_info.frameworkid == null) {
110
123
  device_info.frameworkid = state._ZAP._frameworkID;
111
124
  logger.Console.log('addOrUpdateDeviceID: Initiated register the device'); // no i18n
112
125
  registerDeviceWithApptics().then(function (result_device_info) {
@@ -143,7 +156,7 @@ function addOrUpdateDeviceID(device_info, previous) {
143
156
  * out as an empty header.
144
157
  */
145
158
  const DEVICE_RESPONSE_KEYS = ['deviceid', 'anonymousid', 'aaid', 'apid', 'mapid', 'appversionid', 'osversionid', 'timezoneid', 'platformid', 'devicetypeid', 'modelid']; // no i18n
146
- /** Copies server-assigned ids into state and persists device info. */
159
+ /** Copies server-assigned ids into state and persists device info; resolves once it is stored. */
147
160
  function applyDeviceResponse(data) {
148
161
  DEVICE_RESPONSE_KEYS.forEach(function (key) {
149
162
  // an id the answer does not mention is one we already hold, or one we never had:
@@ -159,12 +172,81 @@ function applyDeviceResponse(data) {
159
172
  if (!utils.isEmpty(data.mapid)) {
160
173
  state._ZAP._mapid = data.mapid;
161
174
  }
162
- // Update the new device info into the localstorage
163
- storage.user_defaults.add('device_info', state._ZAP._device_info); // no i18n
175
+ // stored before the registration counts as done, so another tab waiting to register reads it
176
+ return storage.user_defaults.save('device_info', state._ZAP._device_info); // no i18n
177
+ }
178
+ /**
179
+ * The browser's uuid: the one in storage, or a new one stored now. Every tab of the site uses the
180
+ * same one, because the device is registered under it.
181
+ */
182
+ function useStoredUuid() {
183
+ // kept in device_info: it is the id this browser is registered under
184
+ var uuid = storage.getKeyFromDeviceInfo('uuid'); // no i18n
185
+ if (utils.isEmpty(uuid)) {
186
+ uuid = utils.getNewUUID();
187
+ storage.setDeviceInfoKey('uuid', uuid); // no i18n
188
+ }
189
+ state._ZAP._uuid = uuid;
190
+ }
191
+ /**
192
+ * The ids another tab of this site registered after this one loaded its settings: those are in
193
+ * storage but not in this tab's copy. Resolves with them, applied to this tab, or null when the
194
+ * browser is still unregistered and this tab has to register it.
195
+ */
196
+ function adoptRegisteredDevice() {
197
+ return storage.reloadSettings(['device_info']).then(function () {
198
+ var stored = storage.user_defaults.item('device_info') || {}; // no i18n
199
+ if (!isRegistered(stored)) {
200
+ return null;
201
+ }
202
+ // the device is registered under the uuid the other tab sent
203
+ if (!utils.isEmpty(stored.uuid)) {
204
+ state._ZAP._uuid = stored.uuid;
205
+ }
206
+ logger.Console.log('registerDeviceWithApptics: another tab registered this browser, using its device ' + stored.deviceid); // no i18n
207
+ return applyDeviceResponse(stored).then(function () {
208
+ return { result: 'success', data: stored }; // no i18n
209
+ });
210
+ });
211
+ }
212
+ /**
213
+ * Sets up this browser's device at start-up: the uuid, the device info, and a registration when
214
+ * the browser has none.
215
+ *
216
+ * Settings are this tab's own copy, loaded when it started. On a browser with no device yet, two
217
+ * tabs started together would each register it and store two devices, so the first visit runs
218
+ * under a lock shared by every tab of the site: the tab that gets it re-reads storage, registers
219
+ * only when no other tab has, and keeps the lock until the new ids are stored. The next tab then
220
+ * finds them. Start-up goes on as soon as the device info is set, without waiting for the server.
221
+ */
222
+ function prepareDevice() {
223
+ var cached = storage.user_defaults.item('device_info') || {}; // no i18n
224
+ if (isRegistered(cached)) {
225
+ useStoredUuid();
226
+ checkAndUpdateDeviceInfo();
227
+ return Promise.resolve();
228
+ }
229
+ return new Promise(function (resolve) {
230
+ locks.withLock('device', function () {
231
+ return storage.reloadSettings(['device_info']).then(function () {
232
+ useStoredUuid();
233
+ checkAndUpdateDeviceInfo();
234
+ resolve();
235
+ // held until a registration this started is stored, or has failed
236
+ return deviceReady();
237
+ });
238
+ }).catch(function (err) {
239
+ logger.Console.log('prepareDevice: ' + err); // no i18n
240
+ }).then(function () {
241
+ resolve();
242
+ });
243
+ });
164
244
  }
165
245
  /** True while the device has no server ids, so nothing can be sent yet. */
166
246
  function needsDevice() {
167
- return utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid')); // no i18n
247
+ // the app ids from app/addversion, and the deviceid from device/add: either missing, and the
248
+ // device is registered before anything stored is sent
249
+ return (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid'))) || !transport.hasDeviceId(); // no i18n
168
250
  }
169
251
  /**
170
252
  * Resolves once a registration that is in flight has settled, succeeded or not, and right away
@@ -197,14 +279,14 @@ function registerDeviceWithApptics() {
197
279
  return blocked;
198
280
  }
199
281
  if (!state._ZAP._token) {
200
- logger.Console.warn(label + ': You must call apptics.init() first'); // no i18n
201
- return Promise.reject(label + ': You must call apptics.init() first'); // no i18n
282
+ logger.Console.warn(label + ': You must call Apptics.init() first'); // no i18n
283
+ return Promise.reject(label + ': You must call Apptics.init() first'); // no i18n
202
284
  }
203
285
  var offline = transport.rejectIfOffline(label);
204
286
  if (offline) {
205
287
  return offline;
206
288
  }
207
- if (state._ZAP._device_info.deviceid && state._ZAP._device_info.anonymousid) {
289
+ if (isRegistered(state._ZAP._device_info)) {
208
290
  return Promise.resolve(state._ZAP._device_info);
209
291
  }
210
292
  return new Promise((resolve, reject) => {
@@ -215,17 +297,27 @@ function registerDeviceWithApptics() {
215
297
  return;
216
298
  }
217
299
  state._ZAP._regDeviceSending = true;
218
- var payload = deviceBody(state._ZAP._device_info);
219
- // device/add is the one url that allows all four: zak and uuid identify the caller,
220
- // apid and mapid the app. app/addversion has already answered with apid and mapid by
221
- // the time registration runs, so the first call can carry them like every later one.
222
- var headers = { zak: transport.getHeaderParams('zak'), uuid: transport.getHeaderParams('uuid'), ...transport.apiHeaders() }; // no i18n
223
- transport.postToApptics('regdevice', payload, headers, label).then(function (result) {
224
- applyDeviceResponse(result.data || {});
300
+ adoptRegisteredDevice().then(function (adopted) {
301
+ if (adopted) {
302
+ return adopted;
303
+ }
304
+ var payload = deviceBody(state._ZAP._device_info);
305
+ // device/add is the one url that allows all four: zak and uuid identify the caller,
306
+ // apid and mapid the app. app/addversion has already answered with apid and mapid by
307
+ // the time registration runs, so the first call can carry them like every later one.
308
+ var headers = { zak: transport.getHeaderParams('zak'), uuid: transport.getHeaderParams('uuid'), ...transport.apiHeaders() }; // no i18n
309
+ return transport.postToApptics('regdevice', payload, headers, label).then(function (result) {
310
+ return applyDeviceResponse(result.data || {}).then(function () {
311
+ return result;
312
+ });
313
+ });
314
+ }).then(function (result) {
225
315
  settleRegistration(true, result);
226
316
  resolve(result);
227
- // first visit: the ids exist now, so fetch the server settings right away
228
- refreshServerSettings();
317
+ // first visit: the ids exist now, so fetch the server settings right away. A device
318
+ // registered again later (its details or the framework id changed) asks nothing more:
319
+ // start-up has already asked on this page load
320
+ startServerSettingChecks();
229
321
  }).catch(function (err) {
230
322
  settleRegistration(false, err);
231
323
  reject(err);
@@ -257,7 +349,15 @@ function getUpdatesFromServer() {
257
349
  return transport.postToApptics('sdkconfig', '', transport.apiHeaders(), label).then(function (result) {
258
350
  var data = result.data || {};
259
351
  applyServerFlags(data);
260
- storage.user_defaults.add('trackinfo', state._ZAP._trackinfo); // no i18n
352
+ if (state._ZAP._nextConfigCheck === 0) {
353
+ // no next_check_after: getupdates_interval from now, stored so a later page load
354
+ // can tell whether the answer is still current
355
+ state._ZAP._nextConfigCheck = utils.nowMs() + state.getConfig('getupdates_interval'); // no i18n
356
+ state._ZAP._trackinfo.next_check_after = state._ZAP._nextConfigCheck;
357
+ }
358
+ // the visitor's tracking status lives in trackinfo too, and is not the server's to replace
359
+ var saved = storage.storedTrackingStatus();
360
+ storage.user_defaults.add('trackinfo', saved === undefined ? state._ZAP._trackinfo : Object.assign({}, state._ZAP._trackinfo, { tracking_status: saved })); // no i18n
261
361
  logger.Console.log('getUpdatesFromServer: ' + JSON.stringify(state._ZAP._trackinfo)); // no i18n
262
362
  return data;
263
363
  });
@@ -290,6 +390,10 @@ function applyServerFlags(data) {
290
390
  if (data.hasOwnProperty('tracking_enabled')) { // no i18n
291
391
  state._ZAP._shouldTrack = data.tracking_enabled !== false;
292
392
  }
393
+ else if (data.hasOwnProperty('shouldtrack')) { // no i18n
394
+ // what this SDK saves, read back on the next visit
395
+ state._ZAP._shouldTrack = data.shouldtrack !== false;
396
+ }
293
397
  else {
294
398
  // the old shape: archived versions and untracked customer groups
295
399
  var versionArchived = !data.hasOwnProperty('versionarchivestatus') || utils.isEmpty(data.versionarchivestatus)
@@ -299,9 +403,14 @@ function applyServerFlags(data) {
299
403
  state._ZAP._shouldTrack = versionArchived || !customerGroupTracking;
300
404
  }
301
405
  if (hasModules) {
302
- // sessions carry screens, events and api calls, so module 11 gates all of engagement
303
- state._ZAP._engagementTracking = moduleEnabled(status, constants.SDK_MODULE.SESSION);
304
- state._ZAP._errorTracking = moduleEnabled(status, constants.SDK_MODULE.CRASH);
406
+ // each kind of engagement item has its own module, so the server can turn one off alone
407
+ state._ZAP._engagementTracking = {
408
+ event: moduleEnabled(status, constants.SDK_MODULE.EVENT),
409
+ api: moduleEnabled(status, constants.SDK_MODULE.API),
410
+ screen: moduleEnabled(status, constants.SDK_MODULE.SCREEN),
411
+ session: moduleEnabled(status, constants.SDK_MODULE.SESSION),
412
+ };
413
+ state._ZAP._errorTracking = moduleEnabled(status, constants.SDK_MODULE.JS_ERROR);
305
414
  state._ZAP._heatmapTracking = moduleEnabled(status, constants.SDK_MODULE.HEATMAP);
306
415
  state._ZAP._remoteLoggerEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_LOGGER);
307
416
  state._ZAP._feedbackEnabled = moduleEnabled(status, constants.SDK_MODULE.FEEDBACK);
@@ -310,27 +419,52 @@ function applyServerFlags(data) {
310
419
  state._ZAP._remoteConfigEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_CONFIG);
311
420
  }
312
421
  else {
313
- state._ZAP._engagementTracking = data.engagementtracking !== false;
314
- state._ZAP._errorTracking = data.errortracking !== false;
315
- state._ZAP._heatmapTracking = data.heatmaptracking !== false;
316
- state._ZAP._remoteLoggerEnabled = data.remoteloggerenabled !== false;
317
- state._ZAP._feedbackEnabled = data.feedbackenabled !== false;
422
+ // this SDK saves one flag per kind under engagement; an earlier build saved one
423
+ // engagementtracking flag for all four
424
+ var saved = (data.engagement && typeof data.engagement === 'object' // no i18n
425
+ ? data.engagement : null);
426
+ var kindOn = function (kind) {
427
+ return saved ? saved[kind] !== false : data.engagementtracking !== false;
428
+ };
429
+ state._ZAP._engagementTracking = { event: kindOn('event'), api: kindOn('api'), screen: kindOn('screen'), session: kindOn('session') }; // no i18n
430
+ // the same for the others: read the short key this SDK saves, else the earlier build's
431
+ var flagOn = function (key, oldKey) {
432
+ return (data.hasOwnProperty(key) ? data[key] : data[oldKey]) !== false;
433
+ };
434
+ state._ZAP._errorTracking = flagOn('error', 'errortracking'); // no i18n
435
+ state._ZAP._heatmapTracking = flagOn('heatmap', 'heatmaptracking'); // no i18n
436
+ state._ZAP._remoteLoggerEnabled = flagOn('remotelogger', 'remoteloggerenabled'); // no i18n
437
+ state._ZAP._feedbackEnabled = flagOn('feedback', 'feedbackenabled'); // no i18n
318
438
  }
439
+ // screen recording, set up in the console: everything the form needs comes from here
319
440
  var quartz = (data.quartz && typeof data.quartz === 'object' ? data.quartz : {});
320
- state._ZAP._quartzEnabled = quartz.hasOwnProperty('quartz_enabled') // no i18n
321
- ? quartz.quartz_enabled !== false
322
- : data.quartzenabled !== false;
323
- // when to ask again, which replaces polling on a fixed interval
324
- if (typeof data.next_check_after === 'number' && data.next_check_after > 0) {
325
- state._ZAP._nextConfigCheck = data.next_check_after;
326
- }
441
+ var text = function (value) {
442
+ return typeof value === 'string' ? value.trim() : ''; // no i18n
443
+ };
444
+ state._ZAP._quartz = {
445
+ enabled: quartz.hasOwnProperty('quartz_enabled') ? quartz.quartz_enabled === true : data.quartzenabled === true, // no i18n
446
+ workspace: text(quartz.quartz_workspace),
447
+ department: text(quartz.quartz_department),
448
+ domain: text(quartz.quartz_domain),
449
+ cdnUrl: text(quartz.quartz_cdn_url),
450
+ };
451
+ // when to ask again, which replaces polling on a fixed interval. Kept as a timestamp, so the
452
+ // stored copy reads back the same. An answer without one falls back to getupdates_interval
453
+ state._ZAP._nextConfigCheck = nextCheckTime(data.next_check_after, utils.nowMs());
327
454
  state._ZAP._trackinfo = {
328
- engagementtracking: state._ZAP._engagementTracking,
329
- errortracking: state._ZAP._errorTracking,
330
- heatmaptracking: state._ZAP._heatmapTracking,
331
- remoteloggerenabled: state._ZAP._remoteLoggerEnabled,
332
- feedbackenabled: state._ZAP._feedbackEnabled,
333
- quartzenabled: state._ZAP._quartzEnabled,
455
+ engagement: state._ZAP._engagementTracking,
456
+ error: state._ZAP._errorTracking,
457
+ heatmap: state._ZAP._heatmapTracking,
458
+ remotelogger: state._ZAP._remoteLoggerEnabled,
459
+ feedback: state._ZAP._feedbackEnabled,
460
+ // in the server's own shape, so restoring it on the next visit reads it the same way
461
+ quartz: {
462
+ quartz_enabled: state._ZAP._quartz.enabled,
463
+ quartz_workspace: state._ZAP._quartz.workspace,
464
+ quartz_department: state._ZAP._quartz.department,
465
+ quartz_domain: state._ZAP._quartz.domain,
466
+ quartz_cdn_url: state._ZAP._quartz.cdnUrl,
467
+ },
334
468
  shouldtrack: state._ZAP._shouldTrack,
335
469
  next_check_after: state._ZAP._nextConfigCheck
336
470
  };
@@ -343,30 +477,164 @@ function restoreServerSettings() {
343
477
  var saved = storage.user_defaults.item('trackinfo'); // no i18n
344
478
  if (saved && typeof saved === 'object') { // no i18n
345
479
  applyServerFlags(saved);
480
+ // saved by an earlier build under the old names: store it again in this SDK's shape now,
481
+ // rather than at the next getsdkconfig, which may be a while off. The visitor's tracking
482
+ // status is kept as it is
483
+ if (OLD_TRACKINFO_KEYS.some(function (key) { return saved.hasOwnProperty(key); })) {
484
+ storage.user_defaults.add('trackinfo', saved.tracking_status === undefined ? state._ZAP._trackinfo // no i18n
485
+ : Object.assign({}, state._ZAP._trackinfo, { tracking_status: saved.tracking_status }));
486
+ }
346
487
  logger.Console.log('restoreServerSettings: ' + JSON.stringify(state._ZAP._trackinfo)); // no i18n
347
488
  }
348
489
  }
490
+ /** trackinfo keys an earlier build saved, which this SDK saves under shorter names. */
491
+ var OLD_TRACKINFO_KEYS = ['engagementtracking', 'errortracking', 'heatmaptracking', 'remoteloggerenabled', 'feedbackenabled']; // no i18n
349
492
  /**
350
- * Fetches the server settings now if the device is registered. On a first visit the ids don't
351
- * exist yet; registerDeviceWithApptics calls this as soon as device/add succeeds.
493
+ * next_check_after as a time: the server's value is milliseconds, either a timestamp or a wait
494
+ * from now. Anything below 1e12 (2001 as a timestamp) can only be a wait.
495
+ */
496
+ function nextCheckTime(value, now) {
497
+ if (typeof value !== 'number' || !(value > 0)) { // no i18n
498
+ return 0;
499
+ }
500
+ return value >= 1e12 ? value : now + value;
501
+ }
502
+ /** setTimeout's longest wait; a longer one fires straight away. */
503
+ var MAX_TIMEOUT = 2147483647;
504
+ var configTimer = null;
505
+ var configInFlight = false;
506
+ /** Bumped by stopServerSettingChecks(), so an answer to an ask from before a reset schedules nothing. */
507
+ var configRound = 0;
508
+ /** Whether this page load has asked, or set the timer from the saved answer instead. */
509
+ var configAsked = false;
510
+ /** A user registered, or getupdates reported a change, while a getsdkconfig was in flight: asked again when it answers. */
511
+ var configAskAgain = false;
512
+ /** When the next getsdkconfig is due, as a timestamp. 0 until the first answer. */
513
+ var configDueAt = 0;
514
+ /**
515
+ * Asks for the server settings now if the device is registered, then schedules the next ask
516
+ * for the time the answer gave in next_check_after. On a first visit the ids don't exist yet;
517
+ * registerDeviceWithApptics calls this as soon as device/add succeeds.
352
518
  */
353
519
  function refreshServerSettings() {
354
- if (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid'))) { // no i18n
520
+ if (configInFlight || !transport.hasDeviceId() || (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid')))) { // no i18n
355
521
  return;
356
522
  }
523
+ if (configTimer !== null) {
524
+ clearTimeout(configTimer);
525
+ configTimer = null;
526
+ }
527
+ configInFlight = true;
528
+ configAsked = true;
529
+ markTabAsked();
530
+ var round = configRound;
531
+ var askedAt = utils.nowMs();
357
532
  getUpdatesFromServer().catch(function (err) {
358
533
  logger.Console.log(err);
534
+ }).then(function () {
535
+ if (round !== configRound) {
536
+ // stopServerSettingChecks() ran while this was in flight
537
+ return;
538
+ }
539
+ configInFlight = false;
540
+ if (configAskAgain) {
541
+ configAskAgain = false;
542
+ refreshServerSettings();
543
+ return;
544
+ }
545
+ // an answer without a usable next_check_after, or a failed call, waits getupdates_interval
546
+ configDueAt = state._ZAP._nextConfigCheck > utils.nowMs() ? state._ZAP._nextConfigCheck : askedAt + state.getConfig('getupdates_interval'); // no i18n
547
+ scheduleServerSettingCheck();
359
548
  });
360
549
  }
550
+ /**
551
+ * getsdkconfig now, not at the next check: setUser registered a user, or getupdates reported a
552
+ * module changed.
553
+ */
554
+ function askServerSettings() {
555
+ if (configInFlight) {
556
+ // the one in flight went out before the change: ask again once it answers
557
+ configAskAgain = true;
558
+ return;
559
+ }
560
+ refreshServerSettings();
561
+ }
562
+ /**
563
+ * Start-up's getsdkconfig. Asked on a first visit and on the first page load in each tab; any
564
+ * other load (a reload, a link to another page of the site, back/forward) uses the saved answer
565
+ * and waits for its next_check_after, unless that has already passed. Nothing happens if one has
566
+ * gone out already on this load (after device/add).
567
+ */
568
+ function startServerSettingChecks() {
569
+ if (configAsked) {
570
+ return;
571
+ }
572
+ if (tabHasAsked() && state._ZAP._nextConfigCheck > utils.nowMs()) {
573
+ logger.Console.log('getsdkconfig: the saved answer is still current'); // no i18n
574
+ configAsked = true;
575
+ configDueAt = state._ZAP._nextConfigCheck;
576
+ scheduleServerSettingCheck();
577
+ return;
578
+ }
579
+ refreshServerSettings();
580
+ }
581
+ /** sessionStorage: per tab, and kept across a reload and the site's own links. */
582
+ var TAB_ASKED_KEY = '_apptics_sdkconfig_asked'; // no i18n
583
+ /** Whether getsdkconfig has gone out in this tab before, on an earlier page load. */
584
+ function tabHasAsked() {
585
+ try {
586
+ return sessionStorage.getItem(TAB_ASKED_KEY) === '1'; // no i18n
587
+ }
588
+ catch (e) {
589
+ return false; // no sessionStorage (private mode, sandboxed frame): every load asks
590
+ }
591
+ }
592
+ function markTabAsked() {
593
+ try {
594
+ sessionStorage.setItem(TAB_ASKED_KEY, '1'); // no i18n
595
+ }
596
+ catch (e) {
597
+ // as above: without it, every load asks
598
+ }
599
+ }
600
+ /** Sets the timer for the next getsdkconfig, at configDueAt. */
601
+ function scheduleServerSettingCheck() {
602
+ if (configTimer !== null) {
603
+ clearTimeout(configTimer);
604
+ }
605
+ var wait = Math.min(Math.max(configDueAt - utils.nowMs(), 0), MAX_TIMEOUT);
606
+ logger.Console.log('getsdkconfig: next check in ' + wait + ' ms'); // no i18n
607
+ configTimer = setTimeout(function () {
608
+ configTimer = null;
609
+ refreshServerSettings();
610
+ }, wait);
611
+ }
612
+ /** reset(): no more checks, and the next init() asks again straight away. */
613
+ function stopServerSettingChecks() {
614
+ if (configTimer !== null) {
615
+ clearTimeout(configTimer);
616
+ }
617
+ configTimer = null;
618
+ configRound++;
619
+ configInFlight = false;
620
+ configAsked = false;
621
+ configAskAgain = false;
622
+ configDueAt = 0;
623
+ }
361
624
  hooks.hooks.updateDevice = updateDeviceWithApptics;
362
625
  hooks.hooks.refreshDeviceInfo = checkAndUpdateDeviceInfo;
626
+ hooks.hooks.askServerSettings = askServerSettings;
363
627
 
364
628
  exports.addOrUpdateDeviceID = addOrUpdateDeviceID;
365
629
  exports.checkAndUpdateDeviceInfo = checkAndUpdateDeviceInfo;
366
630
  exports.deviceReady = deviceReady;
367
631
  exports.getUpdatesFromServer = getUpdatesFromServer;
368
632
  exports.needsDevice = needsDevice;
633
+ exports.nextCheckTime = nextCheckTime;
634
+ exports.prepareDevice = prepareDevice;
369
635
  exports.refreshServerSettings = refreshServerSettings;
370
636
  exports.registerDeviceWithApptics = registerDeviceWithApptics;
371
637
  exports.restoreServerSettings = restoreServerSettings;
638
+ exports.startServerSettingChecks = startServerSettingChecks;
639
+ exports.stopServerSettingChecks = stopServerSettingChecks;
372
640
  exports.updateDeviceWithApptics = updateDeviceWithApptics;
@@ -1,62 +1,34 @@
1
- /**
2
- * What API tracking records, and under what name — Android's AppticsApiTracker.configure, built
3
- * with ApiTrackingBuilder the way user properties are built with UserBuilder:
4
- *
5
- * configureApiTracking(new ApiTrackingBuilder()
6
- * .allowOnlyDomains('api.zylker.com', 'api.zylker.in')
7
- * .groupDomains('api.zylker.*')
8
- * .ignoreEndpoint('/health', '/internal/**')
9
- * .addPattern('/v2/catalog/{categoryId}/items/{itemId}')
10
- * .build());
11
- *
12
- * Two jobs. Filtering decides whether a call is recorded at all: ignore rules first, and they
13
- * always win, then the allow lists. Normalization decides the path it is recorded under, so that
14
- * `/users/42/orders` and `/users/43/orders` are one endpoint on the console rather than two: the
15
- * query string goes, an explicit pattern is used when one matches, and otherwise numeric ids,
16
- * UUIDs and JWTs become `*`.
17
- *
18
- * Both apply to every call, the ones the fetch/XHR wrappers catch and the ones the app reports
19
- * with trackApi() or startTrackApi(), so one set of rules decides what the API page shows.
20
- */
21
1
  /** The rules, as ApiTrackingBuilder.build() returns them. Every list may be empty. */
22
2
  export interface ApiTrackingConfig {
23
- ignoreDomains: string[];
24
- allowOnlyDomains: string[];
25
- allowOnlyTLDs: string[];
26
- ignoreTLDs: string[];
27
- ignoreDomainPrefixes: string[];
28
- ignoreDomainSuffixes: string[];
29
- groupDomains: string[];
30
- ignoreEndpoints: string[];
31
- patterns: string[];
32
- preserveSegments: string[];
33
- autoDetection: boolean;
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[];
34
11
  }
35
- /** Builds the rules for configureApiTracking, with AppticsApiTracker.configure's names. */
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
+ */
36
22
  export declare class ApiTrackingBuilder {
37
23
  private rules;
38
- /** Never records these hosts. */
39
- ignoreDomains(...domains: string[]): this;
40
- /** Records only these hosts; every other is skipped. */
41
- allowOnlyDomains(...domains: string[]): this;
42
- /** Records only hosts ending in these TLDs, e.g. `.com`. */
43
- allowOnlyTLDs(...tlds: string[]): this;
44
- /** Never records hosts ending in these TLDs, e.g. `.local`. */
45
- ignoreTLDs(...tlds: string[]): this;
46
- /** Never records hosts starting with these, e.g. `dev.`. */
47
- ignoreDomainPrefixes(...prefixes: string[]): this;
48
- /** Never records hosts ending with these, e.g. `.cdn.net`. */
49
- ignoreDomainSuffixes(...suffixes: string[]): this;
50
- /** Records the hosts a pattern such as `api.myapp.*` matches under that one name. */
51
- groupDomains(...patterns: string[]): this;
52
- /** Never records these paths: exact, `*` or `{name}` for one segment, `**` at the end for a prefix. */
53
- ignoreEndpoint(...paths: string[]): this;
54
- /** Records a path this pattern matches as the pattern, with each `{name}` as `*`. Tried before auto-detection. */
55
- addPattern(...patterns: string[]): this;
56
- /** Segments never turned into `*`, e.g. `v1` or `404`. */
57
- preserveSegments(...segments: string[]): this;
58
- /** `false`: only addPattern paths are normalized; every other path is recorded as it is. */
59
- autoDetection(enabled: boolean): this;
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;
60
32
  build(): ApiTrackingConfig;
61
33
  }
62
34
  /**
@@ -66,19 +38,12 @@ export declare class ApiTrackingBuilder {
66
38
  export declare function configureApiTracking(config: ApiTrackingConfig | ApiTrackingBuilder): void;
67
39
  /** The rules in force, for tests and the playground. */
68
40
  export declare function apiTrackingConfig(): ApiTrackingConfig;
69
- /** Whether calls to this host are recorded. Ignore rules are checked first and always win. */
70
- export declare function hostAllowed(hostname: string): boolean;
71
- /** The name a host is recorded under: the first groupDomains pattern it matches, or itself. */
72
- export declare function groupedHost(hostname: string): string;
73
- /** Whether a host is one of the groupDomains names, which carry a `*` no real host has. */
74
- export declare function isGroupedHost(hostname: string): boolean;
75
- /** The path without its query string or fragment, which is where ids and tokens travel. */
76
- export declare function withoutQuery(path: string): string;
77
- /** Whether ignoreEndpoint leaves this path out. Matched on the path as called, query removed. */
78
- export declare function endpointIgnored(path: string): boolean;
79
41
  /**
80
- * The path a call is recorded under. The query goes; then the first addPattern that matches is
81
- * used, with each `{name}` as `*` (the server's path rule has no room for braces); otherwise, with
82
- * autoDetection on, numeric ids, UUIDs and JWTs become `*`, except preserveSegments.
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.
83
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 `*`. */
84
49
  export declare function normalizeApiPath(path: string): string;