@zoho_apptics/apptics-js-sdk 1.0.1 → 2.0.0-beta

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 (226) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +13 -0
  3. package/README.md +747 -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/batch-BU_mTpm1.min.js +3 -0
  9. package/dist/chunks/batch-BU_mTpm1.min.js.map +1 -0
  10. package/dist/chunks/logs-Bea5zUW_.min.js +3 -0
  11. package/dist/chunks/logs-Bea5zUW_.min.js.map +1 -0
  12. package/dist/cjs/auto-init.d.ts +8 -0
  13. package/dist/cjs/auto-init.js +32 -0
  14. package/dist/cjs/core/attempts.d.ts +28 -0
  15. package/dist/cjs/core/attempts.js +113 -0
  16. package/dist/cjs/core/batch.d.ts +32 -0
  17. package/dist/cjs/core/batch.js +115 -0
  18. package/dist/cjs/core/callsite.d.ts +28 -0
  19. package/dist/cjs/core/callsite.js +93 -0
  20. package/dist/cjs/core/consent.d.ts +23 -0
  21. package/dist/cjs/core/consent.js +130 -0
  22. package/dist/cjs/core/constants.d.ts +212 -0
  23. package/dist/cjs/core/constants.js +299 -0
  24. package/dist/cjs/core/db.d.ts +40 -0
  25. package/dist/cjs/core/db.js +334 -0
  26. package/dist/cjs/core/debug.d.ts +22 -0
  27. package/dist/cjs/core/debug.js +88 -0
  28. package/dist/cjs/core/env.d.ts +28 -0
  29. package/dist/cjs/core/env.js +267 -0
  30. package/dist/cjs/core/framework-id.d.ts +1 -0
  31. package/dist/cjs/core/framework-id.js +9 -0
  32. package/dist/cjs/core/guard.d.ts +2 -0
  33. package/dist/cjs/core/guard.js +72 -0
  34. package/dist/cjs/core/hooks.d.ts +41 -0
  35. package/dist/cjs/core/hooks.js +23 -0
  36. package/dist/cjs/core/logger.d.ts +16 -0
  37. package/dist/cjs/core/logger.js +86 -0
  38. package/dist/cjs/core/state.d.ts +69 -0
  39. package/dist/cjs/core/state.js +78 -0
  40. package/dist/cjs/core/storage.d.ts +46 -0
  41. package/dist/cjs/core/storage.js +229 -0
  42. package/dist/cjs/core/transport.d.ts +83 -0
  43. package/dist/cjs/core/transport.js +441 -0
  44. package/dist/cjs/core/utils.d.ts +26 -0
  45. package/dist/cjs/core/utils.js +175 -0
  46. package/dist/cjs/core/validate.d.ts +18 -0
  47. package/dist/cjs/core/validate.js +233 -0
  48. package/dist/cjs/device/appversion.d.ts +20 -0
  49. package/dist/cjs/device/appversion.js +158 -0
  50. package/dist/cjs/device/index.d.ts +23 -0
  51. package/dist/cjs/device/index.js +372 -0
  52. package/dist/cjs/engagement/api-rules.d.ts +84 -0
  53. package/dist/cjs/engagement/api-rules.js +219 -0
  54. package/dist/cjs/engagement/auto-api.d.ts +7 -0
  55. package/dist/cjs/engagement/auto-api.js +200 -0
  56. package/dist/cjs/engagement/auto-screen.d.ts +16 -0
  57. package/dist/cjs/engagement/auto-screen.js +241 -0
  58. package/dist/cjs/engagement/defined-events.d.ts +23 -0
  59. package/dist/cjs/engagement/defined-events.js +58 -0
  60. package/dist/cjs/engagement/send.d.ts +6 -0
  61. package/dist/cjs/engagement/send.js +99 -0
  62. package/dist/cjs/engagement/session.d.ts +42 -0
  63. package/dist/cjs/engagement/session.js +327 -0
  64. package/dist/cjs/engagement/track.d.ts +56 -0
  65. package/dist/cjs/engagement/track.js +350 -0
  66. package/dist/cjs/errors/send.d.ts +6 -0
  67. package/dist/cjs/errors/send.js +73 -0
  68. package/dist/cjs/errors/track.d.ts +4 -0
  69. package/dist/cjs/errors/track.js +127 -0
  70. package/dist/cjs/feedback/controls.d.ts +31 -0
  71. package/dist/cjs/feedback/controls.js +286 -0
  72. package/dist/cjs/feedback/index.d.ts +8 -0
  73. package/dist/cjs/feedback/index.js +37 -0
  74. package/dist/cjs/feedback/logs.d.ts +40 -0
  75. package/dist/cjs/feedback/logs.js +202 -0
  76. package/dist/cjs/feedback/send.d.ts +25 -0
  77. package/dist/cjs/feedback/send.js +178 -0
  78. package/dist/cjs/feedback/template.d.ts +2 -0
  79. package/dist/cjs/feedback/template.js +162 -0
  80. package/dist/cjs/feedback/ui.d.ts +55 -0
  81. package/dist/cjs/feedback/ui.js +631 -0
  82. package/dist/cjs/heatmap/index.d.ts +16 -0
  83. package/dist/cjs/heatmap/index.js +22 -0
  84. package/dist/cjs/heatmap/send.d.ts +13 -0
  85. package/dist/cjs/heatmap/send.js +82 -0
  86. package/dist/cjs/heatmap/track.d.ts +4 -0
  87. package/dist/cjs/heatmap/track.js +377 -0
  88. package/dist/cjs/index.d.ts +68 -0
  89. package/dist/cjs/index.js +150 -0
  90. package/dist/cjs/lifecycle.d.ts +14 -0
  91. package/dist/cjs/lifecycle.js +538 -0
  92. package/dist/cjs/package.json +3 -0
  93. package/dist/cjs/remoteconfig/custom.d.ts +10 -0
  94. package/dist/cjs/remoteconfig/custom.js +78 -0
  95. package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
  96. package/dist/cjs/remoteconfig/evaluate.js +171 -0
  97. package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
  98. package/dist/cjs/remoteconfig/fetch.js +214 -0
  99. package/dist/cjs/remoteconfig/index.d.ts +13 -0
  100. package/dist/cjs/remoteconfig/index.js +90 -0
  101. package/dist/cjs/remoteconfig/store.d.ts +18 -0
  102. package/dist/cjs/remoteconfig/store.js +98 -0
  103. package/dist/cjs/remotelog/gate.d.ts +11 -0
  104. package/dist/cjs/remotelog/gate.js +54 -0
  105. package/dist/cjs/remotelog/index.d.ts +25 -0
  106. package/dist/cjs/remotelog/index.js +55 -0
  107. package/dist/cjs/remotelog/logger.d.ts +28 -0
  108. package/dist/cjs/remotelog/logger.js +139 -0
  109. package/dist/cjs/remotelog/send.d.ts +13 -0
  110. package/dist/cjs/remotelog/send.js +153 -0
  111. package/dist/cjs/scheduler.d.ts +8 -0
  112. package/dist/cjs/scheduler.js +78 -0
  113. package/dist/cjs/types.d.ts +312 -0
  114. package/dist/cjs/user/index.d.ts +33 -0
  115. package/dist/cjs/user/index.js +178 -0
  116. package/dist/esm/auto-init.d.ts +8 -0
  117. package/dist/esm/auto-init.js +29 -0
  118. package/dist/esm/core/attempts.d.ts +28 -0
  119. package/dist/esm/core/attempts.js +106 -0
  120. package/dist/esm/core/batch.d.ts +32 -0
  121. package/dist/esm/core/batch.js +108 -0
  122. package/dist/esm/core/callsite.d.ts +28 -0
  123. package/dist/esm/core/callsite.js +89 -0
  124. package/dist/esm/core/consent.d.ts +23 -0
  125. package/dist/esm/core/consent.js +120 -0
  126. package/dist/esm/core/constants.d.ts +212 -0
  127. package/dist/esm/core/constants.js +277 -0
  128. package/dist/esm/core/db.d.ts +40 -0
  129. package/dist/esm/core/db.js +324 -0
  130. package/dist/esm/core/debug.d.ts +22 -0
  131. package/dist/esm/core/debug.js +81 -0
  132. package/dist/esm/core/env.d.ts +28 -0
  133. package/dist/esm/core/env.js +251 -0
  134. package/dist/esm/core/framework-id.d.ts +1 -0
  135. package/dist/esm/core/framework-id.js +7 -0
  136. package/dist/esm/core/guard.d.ts +2 -0
  137. package/dist/esm/core/guard.js +70 -0
  138. package/dist/esm/core/hooks.d.ts +41 -0
  139. package/dist/esm/core/hooks.js +21 -0
  140. package/dist/esm/core/logger.d.ts +16 -0
  141. package/dist/esm/core/logger.js +82 -0
  142. package/dist/esm/core/state.d.ts +69 -0
  143. package/dist/esm/core/state.js +73 -0
  144. package/dist/esm/core/storage.d.ts +46 -0
  145. package/dist/esm/core/storage.js +219 -0
  146. package/dist/esm/core/transport.d.ts +83 -0
  147. package/dist/esm/core/transport.js +425 -0
  148. package/dist/esm/core/utils.d.ts +26 -0
  149. package/dist/esm/core/utils.js +161 -0
  150. package/dist/esm/core/validate.d.ts +18 -0
  151. package/dist/esm/core/validate.js +226 -0
  152. package/dist/esm/device/appversion.d.ts +20 -0
  153. package/dist/esm/device/appversion.js +153 -0
  154. package/dist/esm/device/index.d.ts +23 -0
  155. package/dist/esm/device/index.js +362 -0
  156. package/dist/esm/engagement/api-rules.d.ts +84 -0
  157. package/dist/esm/engagement/api-rules.js +211 -0
  158. package/dist/esm/engagement/auto-api.d.ts +7 -0
  159. package/dist/esm/engagement/auto-api.js +198 -0
  160. package/dist/esm/engagement/auto-screen.d.ts +16 -0
  161. package/dist/esm/engagement/auto-screen.js +238 -0
  162. package/dist/esm/engagement/defined-events.d.ts +23 -0
  163. package/dist/esm/engagement/defined-events.js +55 -0
  164. package/dist/esm/engagement/send.d.ts +6 -0
  165. package/dist/esm/engagement/send.js +96 -0
  166. package/dist/esm/engagement/session.d.ts +42 -0
  167. package/dist/esm/engagement/session.js +315 -0
  168. package/dist/esm/engagement/track.d.ts +56 -0
  169. package/dist/esm/engagement/track.js +340 -0
  170. package/dist/esm/errors/send.d.ts +6 -0
  171. package/dist/esm/errors/send.js +70 -0
  172. package/dist/esm/errors/track.d.ts +4 -0
  173. package/dist/esm/errors/track.js +123 -0
  174. package/dist/esm/feedback/controls.d.ts +31 -0
  175. package/dist/esm/feedback/controls.js +280 -0
  176. package/dist/esm/feedback/index.d.ts +8 -0
  177. package/dist/esm/feedback/index.js +30 -0
  178. package/dist/esm/feedback/logs.d.ts +40 -0
  179. package/dist/esm/feedback/logs.js +188 -0
  180. package/dist/esm/feedback/send.d.ts +25 -0
  181. package/dist/esm/feedback/send.js +175 -0
  182. package/dist/esm/feedback/template.d.ts +2 -0
  183. package/dist/esm/feedback/template.js +159 -0
  184. package/dist/esm/feedback/ui.d.ts +55 -0
  185. package/dist/esm/feedback/ui.js +626 -0
  186. package/dist/esm/heatmap/index.d.ts +16 -0
  187. package/dist/esm/heatmap/index.js +17 -0
  188. package/dist/esm/heatmap/send.d.ts +13 -0
  189. package/dist/esm/heatmap/send.js +79 -0
  190. package/dist/esm/heatmap/track.d.ts +4 -0
  191. package/dist/esm/heatmap/track.js +374 -0
  192. package/dist/esm/index.d.ts +68 -0
  193. package/dist/esm/index.js +122 -0
  194. package/dist/esm/lifecycle.d.ts +14 -0
  195. package/dist/esm/lifecycle.js +534 -0
  196. package/dist/esm/package.json +3 -0
  197. package/dist/esm/remoteconfig/custom.d.ts +10 -0
  198. package/dist/esm/remoteconfig/custom.js +74 -0
  199. package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
  200. package/dist/esm/remoteconfig/evaluate.js +166 -0
  201. package/dist/esm/remoteconfig/fetch.d.ts +24 -0
  202. package/dist/esm/remoteconfig/fetch.js +210 -0
  203. package/dist/esm/remoteconfig/index.d.ts +13 -0
  204. package/dist/esm/remoteconfig/index.js +85 -0
  205. package/dist/esm/remoteconfig/store.d.ts +18 -0
  206. package/dist/esm/remoteconfig/store.js +91 -0
  207. package/dist/esm/remotelog/gate.d.ts +11 -0
  208. package/dist/esm/remotelog/gate.js +48 -0
  209. package/dist/esm/remotelog/index.d.ts +25 -0
  210. package/dist/esm/remotelog/index.js +51 -0
  211. package/dist/esm/remotelog/logger.d.ts +28 -0
  212. package/dist/esm/remotelog/logger.js +136 -0
  213. package/dist/esm/remotelog/send.d.ts +13 -0
  214. package/dist/esm/remotelog/send.js +149 -0
  215. package/dist/esm/scheduler.d.ts +8 -0
  216. package/dist/esm/scheduler.js +74 -0
  217. package/dist/esm/types.d.ts +312 -0
  218. package/dist/esm/user/index.d.ts +33 -0
  219. package/dist/esm/user/index.js +170 -0
  220. package/dist/feedback.esm.min.js +3 -0
  221. package/dist/feedback.esm.min.js.map +1 -0
  222. package/dist/heatmap.esm.min.js +3 -0
  223. package/dist/heatmap.esm.min.js.map +1 -0
  224. package/dist/style.css +1 -0
  225. package/package.json +124 -7
  226. package/src/appticssdk.js +0 -3817
@@ -0,0 +1,372 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (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 env = require('../core/env.js');
10
+ var consent = require('../core/consent.js');
11
+ var transport = require('../core/transport.js');
12
+ var hooks = require('../core/hooks.js');
13
+ var appversion = require('./appversion.js');
14
+
15
+ /** Server-assigned ids, never sent back in device payloads. */
16
+ /**
17
+ * Exactly the keys deviceInfoWebSDKV2 declares, which is exactly what device/add accepts.
18
+ *
19
+ * An allow-list rather than an exclusion list: the template is a closed set of eleven mandatory
20
+ * keys, so anything else on device_info is refused as EXTRA_PARAM_FOUND and takes the whole
21
+ * registration with it. v1 carried `frameworkid` in the body; v2 moved it to a query param, and
22
+ * `platformid` — which v1 left out — became mandatory here. It comes from app/addversion, which
23
+ * is why that call has to land first.
24
+ */
25
+ const DEVICE_BODY_KEYS = ['devicetype', 'osversion', 'timezone', 'screenheight', 'screenwidth', 'referrer', 'referringdomain', 'browser', 'browserversion', 'donottrackenabled', 'platformid']; // no i18n
26
+ /** The ones the template types as int, so an empty value has to become 0 rather than ''. */
27
+ const DEVICE_BODY_NUMERIC = ['screenheight', 'screenwidth', 'donottrackenabled', 'platformid']; // no i18n
28
+ /**
29
+ * The device/add body: all eleven declared keys, always.
30
+ *
31
+ * Every one is min-occurrences="1", so a key is sent even when its value is empty — `referrer` is
32
+ * empty on any direct visit, and leaving it out would refuse the registration over a blank the
33
+ * server is happy to receive.
34
+ */
35
+ function deviceBody(info) {
36
+ var out = {};
37
+ DEVICE_BODY_KEYS.forEach(function (key) {
38
+ var value = info[key];
39
+ var numeric = DEVICE_BODY_NUMERIC.indexOf(key) !== -1;
40
+ out[key] = utils.isEmpty(value) ? (numeric ? 0 : '') : value; // no i18n
41
+ });
42
+ return JSON.stringify(out);
43
+ }
44
+ /**
45
+ * The fields that describe the device itself, and so the only ones worth a device/add when they
46
+ * change: a browser or OS upgrade, a new screen, a move between timezones, a do-not-track switch.
47
+ *
48
+ * `referrer` and `connectionType` are deliberately absent. Both change constantly — the referrer on
49
+ * most navigations, the connection on every network switch — so including them would send a
50
+ * device/add on nearly every page load. The server-assigned ids are absent for the opposite reason:
51
+ * they arrive from the server, so comparing them would report a change the moment registration
52
+ * fills them in.
53
+ */
54
+ const DEVICE_IDENTITY_KEYS = ['osversion', 'browser', 'browserversion', 'devicetype', 'screenheight', 'screenwidth', 'timezone', 'donottrackenabled',
55
+ // the SDK build this device runs: an upgrade to a new SDK version is a change the server is told about
56
+ 'frameworkid']; // no i18n
57
+ /** The identity fields alone, for comparing one stored device_info against a fresh one. */
58
+ function deviceIdentity(info) {
59
+ var identity = {};
60
+ DEVICE_IDENTITY_KEYS.forEach(function (key) {
61
+ identity[key] = info ? info[key] : undefined;
62
+ });
63
+ return identity;
64
+ }
65
+ function checkAndUpdateDeviceInfo() {
66
+ // read the stored info before it is overwritten below: addOrUpdateDeviceID compares the two to
67
+ // decide whether the server needs telling, and cannot do that once the new info has replaced it
68
+ var previous = storage.user_defaults.item('device_info'); // no i18n
69
+ // params -> https://help.apptics.com/hc/en-us/articles/115004613766-Default-Properties-Collected-by-apptics
70
+ state._ZAP._device_info = {
71
+ osversion: env.getOS(),
72
+ browser: env.getBrowser(),
73
+ browserversion: String(env.getBrowserVersion()),
74
+ donottrackenabled: consent.hasDoNotTrackEnabled() ? 1 : 0, // no i18n
75
+ devicetype: env.getDevice(),
76
+ screenheight: screen.height,
77
+ screenwidth: screen.width,
78
+ referrer: env.getReferringURL(),
79
+ connectionType: env.getConnectionType(),
80
+ referringdomain: window.location.hostname || '', // no i18n
81
+ deviceid: storage.getKeyFromDeviceInfo('deviceid'), // no i18n
82
+ anonymousid: storage.getKeyFromDeviceInfo('anonymousid'), // no i18n
83
+ // the id of the SDK build running now, never the stored one: a stored id is the build this
84
+ // device first registered with, and every later SDK version would go on reporting it
85
+ frameworkid: state._ZAP._frameworkID,
86
+ mapid: storage.getKeyFromDeviceInfo('mapid'), // no i18n
87
+ aaid: storage.getKeyFromDeviceInfo('aaid'), // no i18n
88
+ apid: storage.getKeyFromDeviceInfo('apid'), // no i18n
89
+ appversionid: storage.getKeyFromDeviceInfo('appversionid'), // no i18n
90
+ platformid: storage.getKeyFromDeviceInfo('platformid'), // no i18n
91
+ devicetypeid: storage.getKeyFromDeviceInfo('devicetypeid'), // no i18n
92
+ osversionid: storage.getKeyFromDeviceInfo('osversionid'), // no i18n
93
+ timezoneid: storage.getKeyFromDeviceInfo('timezoneid'), // no i18n
94
+ // metaWebSDKV2 needs both of these on every call: modelid comes from device/add,
95
+ // appreleaseversionid from app/addversion
96
+ modelid: storage.getKeyFromDeviceInfo('modelid'), // no i18n
97
+ appreleaseversionid: storage.getKeyFromDeviceInfo('appreleaseversionid'), // no i18n
98
+ timezone: env.getTimeZone()
99
+ };
100
+ // the rebuild above starts from browser facts, so the ids app/addversion returned are put
101
+ // back before anything is stored or compared
102
+ appversion.applyStoredAppVersion();
103
+ storage.user_defaults.add('device_info', state._ZAP._device_info); // no i18n
104
+ addOrUpdateDeviceID(state._ZAP._device_info, previous);
105
+ }
106
+ function addOrUpdateDeviceID(device_info, previous) {
107
+ var old_device_info = previous !== undefined ? previous : storage.user_defaults.item('device_info') || {}; // no i18n
108
+ // 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) {
110
+ device_info.frameworkid = state._ZAP._frameworkID;
111
+ logger.Console.log('addOrUpdateDeviceID: Initiated register the device'); // no i18n
112
+ registerDeviceWithApptics().then(function (result_device_info) {
113
+ if (utils.isSuccess(result_device_info)) {
114
+ logger.Console.log('addOrUpdateDeviceID: Success register the device ' + JSON.stringify(result_device_info.data)); // no i18n
115
+ logger.Console.dir(result_device_info.data);
116
+ }
117
+ else {
118
+ logger.Console.log('addOrUpdateDeviceID: Error registering the device: ' + result_device_info); // no i18n
119
+ }
120
+ }).catch(function (err) {
121
+ logger.Console.log('addOrUpdateDeviceID: Error registering the device: ' + err); // no i18n
122
+ });
123
+ }
124
+ else if (!utils.isEmpty(old_device_info) && !utils.compareTwoObjects(deviceIdentity(device_info), deviceIdentity(old_device_info))) {
125
+ // the device itself changed — a browser or OS upgrade, a new screen, a different timezone.
126
+ // The update carries the stored deviceid as a param and the stored uuid as a header, so the
127
+ // server amends this device rather than creating another one: same device, new details
128
+ logger.Console.log('addOrUpdateDeviceID: Device info changed, so updating new changes'); // no i18n
129
+ logger.Console.dir(deviceIdentity(device_info));
130
+ updateDeviceWithApptics(device_info).catch(function (err) {
131
+ logger.Console.log('addOrUpdateDeviceID: Error updating the device: ' + err); // no i18n
132
+ });
133
+ }
134
+ }
135
+ /**
136
+ * The ids device/add is allowed to set. Each is taken only when the answer actually carries it.
137
+ *
138
+ * device/add is not the only source of these: app/addversion runs first and is the *only* source
139
+ * of aaid, mapid, appversionid and appreleaseversionid. Its answer is already on _device_info by
140
+ * the time registration replies, and v2's device/add does not repeat all of it - it answers about
141
+ * the device, not the build. Assigning every key unconditionally therefore overwrote ids we had
142
+ * with undefined, which is how aaid and appversionid reached metaWebSDKV2 as null and mapid went
143
+ * out as an empty header.
144
+ */
145
+ 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. */
147
+ function applyDeviceResponse(data) {
148
+ DEVICE_RESPONSE_KEYS.forEach(function (key) {
149
+ // an id the answer does not mention is one we already hold, or one we never had:
150
+ // either way what is on _device_info is better than undefined
151
+ if (!utils.isEmpty(data[key])) {
152
+ state._ZAP._device_info[key] = data[key];
153
+ }
154
+ });
155
+ // apid and mapid are also read straight off state for the request headers
156
+ if (!utils.isEmpty(data.apid)) {
157
+ state._ZAP._apid = data.apid;
158
+ }
159
+ if (!utils.isEmpty(data.mapid)) {
160
+ state._ZAP._mapid = data.mapid;
161
+ }
162
+ // Update the new device info into the localstorage
163
+ storage.user_defaults.add('device_info', state._ZAP._device_info); // no i18n
164
+ }
165
+ /** True while the device has no server ids, so nothing can be sent yet. */
166
+ function needsDevice() {
167
+ return utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid')); // no i18n
168
+ }
169
+ /**
170
+ * Resolves once a registration that is in flight has settled, succeeded or not, and right away
171
+ * when none is running. Callers use it to wait for the ids rather than poll for them.
172
+ */
173
+ function deviceReady() {
174
+ if (!state._ZAP._regDeviceSending) {
175
+ return Promise.resolve();
176
+ }
177
+ return new Promise(function (resolve) {
178
+ state._ZAP._regDeviceResolve.push(function () { resolve(); });
179
+ state._ZAP._regDeviceReject.push(function () { resolve(); });
180
+ });
181
+ }
182
+ /** Ends an in-flight registration: clears the flag and settles every caller that waited on it. */
183
+ function settleRegistration(ok, value) {
184
+ var waiters = ok ? state._ZAP._regDeviceResolve : state._ZAP._regDeviceReject;
185
+ state._ZAP._regDeviceSending = false;
186
+ state._ZAP._regDeviceResolve = [];
187
+ state._ZAP._regDeviceReject = [];
188
+ waiters.forEach(function (fn) {
189
+ fn(value);
190
+ });
191
+ }
192
+ // Function to register the device
193
+ function registerDeviceWithApptics() {
194
+ var label = 'registerDeviceWithApptics'; // no i18n
195
+ var blocked = transport.rejectIfDisabled(label);
196
+ if (blocked) {
197
+ return blocked;
198
+ }
199
+ 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
202
+ }
203
+ var offline = transport.rejectIfOffline(label);
204
+ if (offline) {
205
+ return offline;
206
+ }
207
+ if (state._ZAP._device_info.deviceid && state._ZAP._device_info.anonymousid) {
208
+ return Promise.resolve(state._ZAP._device_info);
209
+ }
210
+ return new Promise((resolve, reject) => {
211
+ if (state._ZAP._regDeviceSending) {
212
+ // a registration is already in flight; settle together with it
213
+ state._ZAP._regDeviceResolve.push(resolve);
214
+ state._ZAP._regDeviceReject.push(reject);
215
+ return;
216
+ }
217
+ 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 || {});
225
+ settleRegistration(true, result);
226
+ resolve(result);
227
+ // first visit: the ids exist now, so fetch the server settings right away
228
+ refreshServerSettings();
229
+ }).catch(function (err) {
230
+ settleRegistration(false, err);
231
+ reject(err);
232
+ });
233
+ });
234
+ }
235
+ // Function to update the device
236
+ function updateDeviceWithApptics(device_info) {
237
+ var label = 'updateDeviceWithApptics'; // no i18n
238
+ var blocked = transport.rejectIfDisabled(label) || transport.rejectIfOffline(label);
239
+ if (blocked) {
240
+ return blocked;
241
+ }
242
+ var payload = deviceBody(device_info);
243
+ var headers = { zak: transport.getHeaderParams('zak'), uuid: transport.getHeaderParams('uuid'), ...transport.apiHeaders() }; // no i18n
244
+ return transport.postToApptics('regdevice', payload, headers, label).then(function (result) {
245
+ applyDeviceResponse(result.data || {});
246
+ return result;
247
+ });
248
+ }
249
+ // Function to get updates from the server
250
+ function getUpdatesFromServer() {
251
+ var label = 'getUpdatesFromServer'; // no i18n
252
+ var blocked = transport.rejectIfOffline(label) || transport.rejectIfNoDeviceIds(label);
253
+ if (blocked) {
254
+ return blocked;
255
+ }
256
+ // getsdkconfig takes no body: the ids it needs are parameters
257
+ return transport.postToApptics('sdkconfig', '', transport.apiHeaders(), label).then(function (result) {
258
+ var data = result.data || {};
259
+ applyServerFlags(data);
260
+ storage.user_defaults.add('trackinfo', state._ZAP._trackinfo); // no i18n
261
+ logger.Console.log('getUpdatesFromServer: ' + JSON.stringify(state._ZAP._trackinfo)); // no i18n
262
+ return data;
263
+ });
264
+ }
265
+ /**
266
+ * Whether getsdkconfig says a module is on.
267
+ *
268
+ * Absent means on: a server that does not report a module has not turned it off, and an SDK that
269
+ * went quiet because of a key it had never heard of would be impossible to diagnose.
270
+ */
271
+ function moduleEnabled(status, id) {
272
+ var entry = status[id];
273
+ if (!entry || typeof entry !== 'object') {
274
+ return true;
275
+ }
276
+ return entry.enabled !== false;
277
+ }
278
+ /**
279
+ * Applies the server's tracking flags, from a getsdkconfig response or the saved trackinfo.
280
+ *
281
+ * v2 reports a flag per module, keyed by a numeric id, rather than the loose booleans getupdates
282
+ * answered with. Both shapes are read: the stored trackinfo from a previous version of this SDK
283
+ * is in the old shape, and discarding it would mean a visit tracking things the server had
284
+ * already said to stop.
285
+ */
286
+ function applyServerFlags(data) {
287
+ var status = (data.tracking_status && typeof data.tracking_status === 'object'
288
+ ? data.tracking_status : {});
289
+ var hasModules = Object.keys(status).length > 0;
290
+ if (data.hasOwnProperty('tracking_enabled')) { // no i18n
291
+ state._ZAP._shouldTrack = data.tracking_enabled !== false;
292
+ }
293
+ else {
294
+ // the old shape: archived versions and untracked customer groups
295
+ var versionArchived = !data.hasOwnProperty('versionarchivestatus') || utils.isEmpty(data.versionarchivestatus)
296
+ ? true : data.versionarchivestatus;
297
+ var customerGroupTracking = !data.hasOwnProperty('customergrouptracking') || utils.isEmpty(data.customergrouptracking)
298
+ ? true : data.customergrouptracking;
299
+ state._ZAP._shouldTrack = versionArchived || !customerGroupTracking;
300
+ }
301
+ 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);
305
+ state._ZAP._heatmapTracking = moduleEnabled(status, constants.SDK_MODULE.HEATMAP);
306
+ state._ZAP._remoteLoggerEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_LOGGER);
307
+ state._ZAP._feedbackEnabled = moduleEnabled(status, constants.SDK_MODULE.FEEDBACK);
308
+ // getsdkconfig is the authority on this one: getupdates reports it too and the two can
309
+ // disagree while both are in flight, so what is read here wins
310
+ state._ZAP._remoteConfigEnabled = moduleEnabled(status, constants.SDK_MODULE.REMOTE_CONFIG);
311
+ }
312
+ 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;
318
+ }
319
+ 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
+ }
327
+ 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,
334
+ shouldtrack: state._ZAP._shouldTrack,
335
+ next_check_after: state._ZAP._nextConfigCheck
336
+ };
337
+ }
338
+ /**
339
+ * Start-up: applies the server settings saved by the last getupdates, so a "stop tracking"
340
+ * answer holds across page loads instead of resetting to "on" until the next check.
341
+ */
342
+ function restoreServerSettings() {
343
+ var saved = storage.user_defaults.item('trackinfo'); // no i18n
344
+ if (saved && typeof saved === 'object') { // no i18n
345
+ applyServerFlags(saved);
346
+ logger.Console.log('restoreServerSettings: ' + JSON.stringify(state._ZAP._trackinfo)); // no i18n
347
+ }
348
+ }
349
+ /**
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.
352
+ */
353
+ function refreshServerSettings() {
354
+ if (utils.isEmpty(transport.getHeaderParams('apid')) && utils.isEmpty(transport.getHeaderParams('mapid'))) { // no i18n
355
+ return;
356
+ }
357
+ getUpdatesFromServer().catch(function (err) {
358
+ logger.Console.log(err);
359
+ });
360
+ }
361
+ hooks.hooks.updateDevice = updateDeviceWithApptics;
362
+ hooks.hooks.refreshDeviceInfo = checkAndUpdateDeviceInfo;
363
+
364
+ exports.addOrUpdateDeviceID = addOrUpdateDeviceID;
365
+ exports.checkAndUpdateDeviceInfo = checkAndUpdateDeviceInfo;
366
+ exports.deviceReady = deviceReady;
367
+ exports.getUpdatesFromServer = getUpdatesFromServer;
368
+ exports.needsDevice = needsDevice;
369
+ exports.refreshServerSettings = refreshServerSettings;
370
+ exports.registerDeviceWithApptics = registerDeviceWithApptics;
371
+ exports.restoreServerSettings = restoreServerSettings;
372
+ exports.updateDeviceWithApptics = updateDeviceWithApptics;
@@ -0,0 +1,84 @@
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
+ /** The rules, as ApiTrackingBuilder.build() returns them. Every list may be empty. */
22
+ 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;
34
+ }
35
+ /** Builds the rules for configureApiTracking, with AppticsApiTracker.configure's names. */
36
+ export declare class ApiTrackingBuilder {
37
+ 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;
60
+ build(): ApiTrackingConfig;
61
+ }
62
+ /**
63
+ * Applies the rules, replacing any given before. Takes what build() returned, or the builder
64
+ * itself. Call it once, before the requests it should cover; it works before init().
65
+ */
66
+ export declare function configureApiTracking(config: ApiTrackingConfig | ApiTrackingBuilder): void;
67
+ /** The rules in force, for tests and the playground. */
68
+ 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
+ /**
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.
83
+ */
84
+ export declare function normalizeApiPath(path: string): string;
@@ -0,0 +1,219 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ /**
5
+ * What API tracking records, and under what name — Android's AppticsApiTracker.configure, built
6
+ * with ApiTrackingBuilder the way user properties are built with UserBuilder:
7
+ *
8
+ * configureApiTracking(new ApiTrackingBuilder()
9
+ * .allowOnlyDomains('api.zylker.com', 'api.zylker.in')
10
+ * .groupDomains('api.zylker.*')
11
+ * .ignoreEndpoint('/health', '/internal/**')
12
+ * .addPattern('/v2/catalog/{categoryId}/items/{itemId}')
13
+ * .build());
14
+ *
15
+ * Two jobs. Filtering decides whether a call is recorded at all: ignore rules first, and they
16
+ * always win, then the allow lists. Normalization decides the path it is recorded under, so that
17
+ * `/users/42/orders` and `/users/43/orders` are one endpoint on the console rather than two: the
18
+ * query string goes, an explicit pattern is used when one matches, and otherwise numeric ids,
19
+ * UUIDs and JWTs become `*`.
20
+ *
21
+ * Both apply to every call, the ones the fetch/XHR wrappers catch and the ones the app reports
22
+ * with trackApi() or startTrackApi(), so one set of rules decides what the API page shows.
23
+ */
24
+ function emptyConfig() {
25
+ return {
26
+ ignoreDomains: [], allowOnlyDomains: [], allowOnlyTLDs: [], ignoreTLDs: [],
27
+ ignoreDomainPrefixes: [], ignoreDomainSuffixes: [], groupDomains: [], ignoreEndpoints: [],
28
+ patterns: [], preserveSegments: [], autoDetection: true,
29
+ };
30
+ }
31
+ /** Lower-cased, trimmed, non-empty strings: a host list is compared case-insensitively. */
32
+ function cleanList(values, lower) {
33
+ var out = [];
34
+ values.forEach(function (value) {
35
+ if (typeof value !== 'string') { // no i18n
36
+ return;
37
+ }
38
+ var one = value.trim();
39
+ if (one !== '') { // no i18n
40
+ out.push(lower ? one.toLowerCase() : one);
41
+ }
42
+ });
43
+ return out;
44
+ }
45
+ /** A TLD as `.com`, whether it was given as `.com` or `com`. */
46
+ function asTld(value) {
47
+ return value.charAt(0) === '.' ? value : '.' + value; // no i18n
48
+ }
49
+ /** Builds the rules for configureApiTracking, with AppticsApiTracker.configure's names. */
50
+ class ApiTrackingBuilder {
51
+ constructor() {
52
+ this.rules = emptyConfig();
53
+ }
54
+ /** Never records these hosts. */
55
+ ignoreDomains(...domains) { this.rules.ignoreDomains.push(...cleanList(domains, true)); return this; }
56
+ /** Records only these hosts; every other is skipped. */
57
+ allowOnlyDomains(...domains) { this.rules.allowOnlyDomains.push(...cleanList(domains, true)); return this; }
58
+ /** Records only hosts ending in these TLDs, e.g. `.com`. */
59
+ allowOnlyTLDs(...tlds) { this.rules.allowOnlyTLDs.push(...cleanList(tlds, true).map(asTld)); return this; }
60
+ /** Never records hosts ending in these TLDs, e.g. `.local`. */
61
+ ignoreTLDs(...tlds) { this.rules.ignoreTLDs.push(...cleanList(tlds, true).map(asTld)); return this; }
62
+ /** Never records hosts starting with these, e.g. `dev.`. */
63
+ ignoreDomainPrefixes(...prefixes) { this.rules.ignoreDomainPrefixes.push(...cleanList(prefixes, true)); return this; }
64
+ /** Never records hosts ending with these, e.g. `.cdn.net`. */
65
+ ignoreDomainSuffixes(...suffixes) { this.rules.ignoreDomainSuffixes.push(...cleanList(suffixes, true)); return this; }
66
+ /** Records the hosts a pattern such as `api.myapp.*` matches under that one name. */
67
+ groupDomains(...patterns) { this.rules.groupDomains.push(...cleanList(patterns, true)); return this; }
68
+ /** Never records these paths: exact, `*` or `{name}` for one segment, `**` at the end for a prefix. */
69
+ ignoreEndpoint(...paths) { this.rules.ignoreEndpoints.push(...cleanList(paths, false)); return this; }
70
+ /** Records a path this pattern matches as the pattern, with each `{name}` as `*`. Tried before auto-detection. */
71
+ addPattern(...patterns) { this.rules.patterns.push(...cleanList(patterns, false)); return this; }
72
+ /** Segments never turned into `*`, e.g. `v1` or `404`. */
73
+ preserveSegments(...segments) { this.rules.preserveSegments.push(...cleanList(segments, false)); return this; }
74
+ /** `false`: only addPattern paths are normalized; every other path is recorded as it is. */
75
+ autoDetection(enabled) { this.rules.autoDetection = enabled !== false; return this; }
76
+ build() {
77
+ var built = emptyConfig();
78
+ Object.keys(built).forEach((key) => {
79
+ var value = this.rules[key];
80
+ built[key] = Array.isArray(value) ? value.slice() : value;
81
+ });
82
+ return built;
83
+ }
84
+ }
85
+ var current = emptyConfig();
86
+ /**
87
+ * Applies the rules, replacing any given before. Takes what build() returned, or the builder
88
+ * itself. Call it once, before the requests it should cover; it works before init().
89
+ */
90
+ function configureApiTracking(config) {
91
+ var built = config instanceof ApiTrackingBuilder ? config.build() : config;
92
+ var next = emptyConfig();
93
+ if (built && typeof built === 'object') { // no i18n
94
+ Object.keys(next).forEach(function (key) {
95
+ var value = built[key];
96
+ if (key === 'autoDetection') { // no i18n
97
+ next.autoDetection = value !== false;
98
+ }
99
+ else if (Array.isArray(value)) {
100
+ next[key] = value.slice();
101
+ }
102
+ });
103
+ }
104
+ current = next;
105
+ }
106
+ /* ---------------- hosts ---------------- */
107
+ function endsWithAny(host, list) {
108
+ return list.some(function (one) { return host.length >= one.length && host.slice(host.length - one.length) === one; });
109
+ }
110
+ /** Whether calls to this host are recorded. Ignore rules are checked first and always win. */
111
+ function hostAllowed(hostname) {
112
+ var host = String(hostname || '').toLowerCase(); // no i18n
113
+ if (current.ignoreDomains.indexOf(host) !== -1
114
+ || endsWithAny(host, current.ignoreTLDs)
115
+ || endsWithAny(host, current.ignoreDomainSuffixes)
116
+ || current.ignoreDomainPrefixes.some(function (prefix) { return host.indexOf(prefix) === 0; })) {
117
+ return false;
118
+ }
119
+ if (current.allowOnlyDomains.length > 0 && current.allowOnlyDomains.indexOf(host) === -1) {
120
+ return false;
121
+ }
122
+ if (current.allowOnlyTLDs.length > 0 && !endsWithAny(host, current.allowOnlyTLDs)) {
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+ function wildcardHost(pattern) {
128
+ var source = pattern.split('*').map(function (part) {
129
+ return part.replace(/[.+?^${}()|[\]\\]/g, '\\$&'); // no i18n
130
+ }).join('[a-z0-9.-]+'); // no i18n
131
+ return new RegExp('^' + source + '$'); // no i18n
132
+ }
133
+ /** The name a host is recorded under: the first groupDomains pattern it matches, or itself. */
134
+ function groupedHost(hostname) {
135
+ var host = String(hostname || '').toLowerCase(); // no i18n
136
+ for (var i = 0; i < current.groupDomains.length; i++) {
137
+ var pattern = current.groupDomains[i];
138
+ if (pattern.indexOf('*') !== -1 && wildcardHost(pattern).test(host)) { // no i18n
139
+ return pattern;
140
+ }
141
+ }
142
+ return hostname;
143
+ }
144
+ /* ---------------- paths ---------------- */
145
+ /** The path without its query string or fragment, which is where ids and tokens travel. */
146
+ function withoutQuery(path) {
147
+ var cut = String(path || '').split(/[?#]/)[0]; // no i18n
148
+ return cut === '' ? '/' : cut; // no i18n
149
+ }
150
+ function segmentsOf(path) {
151
+ var trimmed = path.length > 1 ? path.replace(/\/+$/, '') : path; // no i18n
152
+ return trimmed.split('/').slice(1); // no i18n
153
+ }
154
+ const ONE_SEGMENT = /^(\*|\{[^/{}]*\})$/;
155
+ /** Whether a path matches a pattern: `*` and `{name}` are one segment, a final `**` is the rest. */
156
+ function matches(pattern, path) {
157
+ var want = segmentsOf(pattern);
158
+ var have = segmentsOf(path);
159
+ for (var i = 0; i < want.length; i++) {
160
+ var part = want[i];
161
+ if (part === '**' && i === want.length - 1) { // no i18n
162
+ return true;
163
+ }
164
+ if (i >= have.length) {
165
+ return false;
166
+ }
167
+ if (ONE_SEGMENT.test(part)) {
168
+ if (have[i] === '') { // no i18n
169
+ return false;
170
+ }
171
+ continue;
172
+ }
173
+ if (part !== have[i]) {
174
+ return false;
175
+ }
176
+ }
177
+ return want.length === have.length;
178
+ }
179
+ /** Whether ignoreEndpoint leaves this path out. Matched on the path as called, query removed. */
180
+ function endpointIgnored(path) {
181
+ var bare = withoutQuery(path);
182
+ return current.ignoreEndpoints.some(function (pattern) { return matches(pattern, bare); });
183
+ }
184
+ const NUMERIC = /^\d+$/;
185
+ const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
186
+ // three base64url parts, the first starting eyJ — the encoding of '{"' every JWT header opens
187
+ // with. Without that start, a file name such as jquery.min.js would pass for a token
188
+ const JWT = /^eyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
189
+ /**
190
+ * The path a call is recorded under. The query goes; then the first addPattern that matches is
191
+ * used, with each `{name}` as `*` (the server's path rule has no room for braces); otherwise, with
192
+ * autoDetection on, numeric ids, UUIDs and JWTs become `*`, except preserveSegments.
193
+ */
194
+ function normalizeApiPath(path) {
195
+ var bare = withoutQuery(path);
196
+ for (var i = 0; i < current.patterns.length; i++) {
197
+ var pattern = current.patterns[i];
198
+ if (matches(pattern, bare)) {
199
+ return pattern.replace(/\{[^/{}]*\}/g, '*'); // no i18n
200
+ }
201
+ }
202
+ if (!current.autoDetection) {
203
+ return bare;
204
+ }
205
+ return bare.split('/').map(function (segment) {
206
+ if (segment === '' || current.preserveSegments.indexOf(segment) !== -1) { // no i18n
207
+ return segment;
208
+ }
209
+ return NUMERIC.test(segment) || UUID.test(segment) || JWT.test(segment) ? '*' : segment; // no i18n
210
+ }).join('/'); // no i18n
211
+ }
212
+
213
+ exports.ApiTrackingBuilder = ApiTrackingBuilder;
214
+ exports.configureApiTracking = configureApiTracking;
215
+ exports.endpointIgnored = endpointIgnored;
216
+ exports.groupedHost = groupedHost;
217
+ exports.hostAllowed = hostAllowed;
218
+ exports.normalizeApiPath = normalizeApiPath;
219
+ exports.withoutQuery = withoutQuery;