@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,219 +1,206 @@
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
+ var logger = require('../core/logger.js');
5
+
4
6
  /**
5
- * What API tracking records, and under what name — Android's AppticsApiTracker.configure, built
7
+ * Which API calls are recorded, and under what name: Android's AppticsApiTracker.configure, built
6
8
  * with ApiTrackingBuilder the way user properties are built with UserBuilder:
7
9
  *
8
10
  * 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}')
11
+ * .allowRegex(/^https:\/\/api\.zylker\.(com|in)\//)
12
+ * .ignoreEquals('https://api.zylker.com/health')
13
+ * .ignoreRegex(/\/internal\//)
13
14
  * .build());
14
15
  *
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 `*`.
16
+ * Four rules, two kinds by two: an exact URL (equals) or a regular expression (regex), to allow or
17
+ * to ignore. Each is tried on the call's URL as `scheme://host[:port]/path`, with its query and
18
+ * fragment removed, so one rule can name a host, a path or a single endpoint. Ignore rules are
19
+ * tried first and always win; then, when there are allow rules, a call must match one of them.
20
+ *
21
+ * The name a call is recorded under is not configured: the query goes, and numeric ids, UUIDs and
22
+ * JWTs in its path become `*`, so `/users/42/orders` and `/users/43/orders` are one endpoint.
20
23
  *
21
24
  * 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.
25
+ * with startTrackApi(), so one set of rules decides what the API page shows.
23
26
  */
24
27
  function emptyConfig() {
25
- return {
26
- ignoreDomains: [], allowOnlyDomains: [], allowOnlyTLDs: [], ignoreTLDs: [],
27
- ignoreDomainPrefixes: [], ignoreDomainSuffixes: [], groupDomains: [], ignoreEndpoints: [],
28
- patterns: [], preserveSegments: [], autoDetection: true,
29
- };
28
+ return { allowEquals: [], ignoreEquals: [], allowRegex: [], ignoreRegex: [] };
30
29
  }
31
- /** Lower-cased, trimmed, non-empty strings: a host list is compared case-insensitively. */
32
- function cleanList(values, lower) {
30
+ /**
31
+ * A URL as the rules compare it: `scheme://host[:port]/path`, the scheme and host lower-cased,
32
+ * the default port dropped, the query, fragment and a trailing slash removed. '' when it is not
33
+ * an absolute http(s) URL.
34
+ */
35
+ function ruleUrl(raw) {
36
+ try {
37
+ var url = new URL(String(raw).trim());
38
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') { // no i18n
39
+ return ''; // no i18n
40
+ }
41
+ var path = url.pathname.length > 1 ? url.pathname.replace(/\/+$/, '') : url.pathname; // no i18n
42
+ return url.protocol + '//' + url.host.toLowerCase() + path; // no i18n
43
+ }
44
+ catch (err) {
45
+ return ''; // no i18n
46
+ }
47
+ }
48
+ /** The exact URLs given, as rules compare them; anything that is not an absolute URL is refused, and said. */
49
+ function cleanUrls(method, values) {
33
50
  var out = [];
34
51
  values.forEach(function (value) {
35
- if (typeof value !== 'string') { // no i18n
52
+ var url = typeof value === 'string' ? ruleUrl(value) : ''; // no i18n
53
+ if (url === '') { // no i18n
54
+ logger.warnAlways('ApiTrackingBuilder.' + method + ': ' + JSON.stringify(value) + ' is not a full http(s) URL, as https://api.example.com/health, and is left out'); // no i18n
36
55
  return;
37
56
  }
38
- var one = value.trim();
39
- if (one !== '') { // no i18n
40
- out.push(lower ? one.toLowerCase() : one);
57
+ if (out.indexOf(url) === -1) {
58
+ out.push(url);
41
59
  }
42
60
  });
43
61
  return out;
44
62
  }
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
63
+ /** The regular expressions given, a RegExp or its source as a string; an invalid one is refused, and said. */
64
+ function cleanRegexes(method, values) {
65
+ var out = [];
66
+ values.forEach(function (value) {
67
+ if (value instanceof RegExp) {
68
+ out.push(value);
69
+ return;
70
+ }
71
+ if (typeof value === 'string' && value !== '') { // no i18n
72
+ try {
73
+ out.push(new RegExp(value));
74
+ return;
75
+ }
76
+ catch (err) {
77
+ // falls through to the warning
78
+ }
79
+ }
80
+ logger.warnAlways('ApiTrackingBuilder.' + method + ': ' + String(value) + ' is not a regular expression, and is left out'); // no i18n
81
+ });
82
+ return out;
48
83
  }
49
- /** Builds the rules for configureApiTracking, with AppticsApiTracker.configure's names. */
84
+ /**
85
+ * Builds the rules configure takes. Each method may be called more than once, and takes any number
86
+ * of values: `new ApiTrackingBuilder().allowEquals(a, b).ignoreRegex(/x/).build()`.
87
+ */
50
88
  class ApiTrackingBuilder {
51
89
  constructor() {
52
90
  this.rules = emptyConfig();
53
91
  }
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; }
92
+ /** Records only these URLs (and those any allowRegex matches): `https://api.example.com/orders`. */
93
+ allowEquals(...urls) { this.rules.allowEquals.push(...cleanUrls('allowEquals', urls)); return this; }
94
+ /** Never records these URLs: `https://api.example.com/health`. */
95
+ ignoreEquals(...urls) { this.rules.ignoreEquals.push(...cleanUrls('ignoreEquals', urls)); return this; }
96
+ /** Records only URLs these match (and those any allowEquals names): `/^https:\/\/api\.example\.com\//`. */
97
+ allowRegex(...patterns) { this.rules.allowRegex.push(...cleanRegexes('allowRegex', patterns)); return this; }
98
+ /** Never records URLs these match: `/\/internal\//`. */
99
+ ignoreRegex(...patterns) { this.rules.ignoreRegex.push(...cleanRegexes('ignoreRegex', patterns)); return this; }
76
100
  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;
101
+ return {
102
+ allowEquals: this.rules.allowEquals.slice(),
103
+ ignoreEquals: this.rules.ignoreEquals.slice(),
104
+ allowRegex: this.rules.allowRegex.slice(),
105
+ ignoreRegex: this.rules.ignoreRegex.slice(),
106
+ };
83
107
  }
84
108
  }
109
+ /**
110
+ * 2.0.0-beta's builder methods, now gone. Called, each throws a TypeError that names what to use
111
+ * instead: a rule silently missing would record what the app meant to leave out.
112
+ */
113
+ const RETIRED_METHODS = Object.freeze({
114
+ ignoreDomains: 'ignoreRegex', // no i18n
115
+ allowOnlyDomains: 'allowRegex', // no i18n
116
+ allowOnlyTLDs: 'allowRegex', // no i18n
117
+ ignoreTLDs: 'ignoreRegex', // no i18n
118
+ ignoreDomainPrefixes: 'ignoreRegex', // no i18n
119
+ ignoreDomainSuffixes: 'ignoreRegex', // no i18n
120
+ groupDomains: '', // no i18n
121
+ ignoreEndpoint: 'ignoreEquals or ignoreRegex', // no i18n
122
+ addPattern: '', // no i18n
123
+ preserveSegments: '', // no i18n
124
+ autoDetection: '', // no i18n
125
+ });
126
+ Object.keys(RETIRED_METHODS).forEach(function (name) {
127
+ Object.defineProperty(ApiTrackingBuilder.prototype, name, {
128
+ configurable: true,
129
+ value: function () {
130
+ var instead = RETIRED_METHODS[name];
131
+ throw new TypeError('ApiTrackingBuilder.' + name + '() is no longer supported: ' // no i18n
132
+ + (instead ? 'use ' + instead + ', which match the URL without its query' : 'ids, UUIDs and tokens in a path become * on their own')); // no i18n
133
+ },
134
+ });
135
+ });
85
136
  var current = emptyConfig();
137
+ /** The values of `list` that are of the kind `keep` accepts. */
138
+ function listOf(value, keep) {
139
+ return Array.isArray(value) ? value.filter(keep) : [];
140
+ }
86
141
  /**
87
142
  * Applies the rules, replacing any given before. Takes what build() returned, or the builder
88
143
  * itself. Call it once, before the requests it should cover; it works before init().
89
144
  */
90
145
  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
- });
146
+ var built = (config instanceof ApiTrackingBuilder ? config.build() : config);
147
+ if (!built || typeof built !== 'object') { // no i18n
148
+ current = emptyConfig();
149
+ return;
103
150
  }
104
- current = next;
151
+ var isString = function (one) { return typeof one === 'string'; }; // no i18n
152
+ var isRegExp = function (one) { return one instanceof RegExp; };
153
+ current = {
154
+ // a hand-written object gets the same clean-up the builder gives
155
+ allowEquals: cleanUrls('allowEquals', listOf(built.allowEquals, isString)), // no i18n
156
+ ignoreEquals: cleanUrls('ignoreEquals', listOf(built.ignoreEquals, isString)), // no i18n
157
+ allowRegex: listOf(built.allowRegex, isRegExp).concat(cleanRegexes('allowRegex', listOf(built.allowRegex, isString))), // no i18n
158
+ ignoreRegex: listOf(built.ignoreRegex, isRegExp).concat(cleanRegexes('ignoreRegex', listOf(built.ignoreRegex, isString))), // no i18n
159
+ };
105
160
  }
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; });
161
+ /** Whether a regular expression matches, whatever its flags: a /g one keeps a position between tests. */
162
+ function regexMatches(re, url) {
163
+ re.lastIndex = 0;
164
+ return re.test(url);
109
165
  }
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) {
166
+ /**
167
+ * Whether a call to `url` is recorded. Ignore rules are tried first and always win; then, when
168
+ * there are allow rules, the URL must match one. A URL that cannot be read is not recorded.
169
+ */
170
+ function urlTracked(url) {
171
+ var target = ruleUrl(url);
172
+ if (target === '') { // no i18n
120
173
  return false;
121
174
  }
122
- if (current.allowOnlyTLDs.length > 0 && !endsWithAny(host, current.allowOnlyTLDs)) {
175
+ if (current.ignoreEquals.indexOf(target) !== -1 || current.ignoreRegex.some(function (re) { return regexMatches(re, target); })) {
123
176
  return false;
124
177
  }
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
- }
178
+ if (current.allowEquals.length === 0 && current.allowRegex.length === 0) {
179
+ return true;
141
180
  }
142
- return hostname;
181
+ return current.allowEquals.indexOf(target) !== -1 || current.allowRegex.some(function (re) { return regexMatches(re, target); });
143
182
  }
144
- /* ---------------- paths ---------------- */
183
+ /* ---------------- the name a call is recorded under ---------------- */
145
184
  /** The path without its query string or fragment, which is where ids and tokens travel. */
146
185
  function withoutQuery(path) {
147
186
  var cut = String(path || '').split(/[?#]/)[0]; // no i18n
148
187
  return cut === '' ? '/' : cut; // no i18n
149
188
  }
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
189
  const NUMERIC = /^\d+$/;
185
190
  const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
186
191
  // three base64url parts, the first starting eyJ — the encoding of '{"' every JWT header opens
187
192
  // with. Without that start, a file name such as jquery.min.js would pass for a token
188
193
  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
+ /** The path a call is recorded under: the query goes, and numeric ids, UUIDs and JWTs become `*`. */
194
195
  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
196
+ return withoutQuery(path).split('/').map(function (segment) {
197
+ return segment !== '' && (NUMERIC.test(segment) || UUID.test(segment) || JWT.test(segment)) ? '*' : segment; // no i18n
210
198
  }).join('/'); // no i18n
211
199
  }
212
200
 
213
201
  exports.ApiTrackingBuilder = ApiTrackingBuilder;
214
202
  exports.configureApiTracking = configureApiTracking;
215
- exports.endpointIgnored = endpointIgnored;
216
- exports.groupedHost = groupedHost;
217
- exports.hostAllowed = hostAllowed;
218
203
  exports.normalizeApiPath = normalizeApiPath;
204
+ exports.ruleUrl = ruleUrl;
205
+ exports.urlTracked = urlTracked;
219
206
  exports.withoutQuery = withoutQuery;
@@ -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 state = require('../core/state.js');
@@ -66,7 +66,7 @@ function shouldTrack(rawUrl) {
66
66
  }
67
67
  // configureApiTracking's rules: checked here too so a call left out is not even timed. trackApi checks them again, for the
68
68
  // calls an app reports itself
69
- return apiRules.hostAllowed(absolute.hostname) && !apiRules.endpointIgnored(absolute.pathname);
69
+ return apiRules.urlTracked(absolute.href);
70
70
  }
71
71
  /** One finished call. Never throws: a tracking failure must not surface in the app's call. */
72
72
  function record(method, url, starttime, responsecode, responsemessage) {
@@ -1,3 +1,5 @@
1
+ /** A path as screen_routes compares it: no query or fragment, no trailing slash but on the root. */
2
+ export declare function routePath(path: string): string;
1
3
  /**
2
4
  * Starts or stops automatic screen tracking. Starting records the screen the page is on, unless
3
5
  * the app has already called inScreen() for it. Safe to call repeatedly: wrapping history twice
@@ -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 logger = require('../core/logger.js');
@@ -32,9 +32,14 @@ var track = require('./track.js');
32
32
  * On by default: most apps never call inScreen() themselves, and a screen-less session says
33
33
  * little. An app that wants only its own calls sets `track_screen: false`.
34
34
  */
35
+ /** A path as screen_routes compares it: no query or fragment, no trailing slash but on the root. */
36
+ function routePath(path) {
37
+ var bare = String(path || '').split(/[?#]/)[0] || '/'; // no i18n
38
+ return bare.length > 1 ? bare.replace(/\/+$/, '') : bare; // no i18n
39
+ }
35
40
  /** The most a screen name may be, and the characters it may hold (see validateScreenName). */
36
41
  const NAME_MAX = 99;
37
- const NAME_DISALLOWED = /[^\w\s.\-&/:]/g;
42
+ const NAME_DISALLOWED = /[^\w\s.\-&/:*]/g;
38
43
  /** How long an automatic view waits for the app's own inScreen() of the same navigation. */
39
44
  const SETTLE_MS = 300;
40
45
  var installed = false;
@@ -82,13 +87,61 @@ function cleanName(raw) {
82
87
  }
83
88
  return name.replace(NAME_DISALLOWED, '_').trim().slice(0, NAME_MAX); // no i18n
84
89
  }
90
+ /** Whether a pattern segment is a wildcard: `{id}`, `:id` or `*`. */
91
+ function isWildcard(segment) {
92
+ return segment === '*' || /^\{[^/]*\}$/.test(segment) || /^:[^/]+$/.test(segment); // no i18n
93
+ }
94
+ /** Whether a route pattern matches a path: every segment, `{id}`, `:id` and `*` matching any one. */
95
+ function patternMatches(pattern, path) {
96
+ var segments = routePath(pattern).split('/'); // no i18n
97
+ var actual = path.split('/'); // no i18n
98
+ if (segments.length !== actual.length) {
99
+ return false;
100
+ }
101
+ for (var j = 0; j < segments.length; j++) {
102
+ var seg = segments[j];
103
+ if (!isWildcard(seg) && seg !== actual[j]) {
104
+ return false;
105
+ }
106
+ }
107
+ return true;
108
+ }
109
+ /**
110
+ * The name screen_routes gives a path, or the path itself when no entry matches. Tried in order:
111
+ * a route pattern records the path with its variable segments as `*` (`/users/{id}/orders`
112
+ * claims `/users/1/orders`, not `/users/1/orders/2`); an exact path or a regular expression
113
+ * records the name the entry gives. Matched on the path without its query.
114
+ */
115
+ function applyRoutes(path) {
116
+ var routes = state._ZAP._config.screen_routes;
117
+ if (!routes || routes.length === 0) {
118
+ return path;
119
+ }
120
+ var bare = routePath(path);
121
+ for (var i = 0; i < routes.length; i++) {
122
+ var route = routes[i];
123
+ if (route.kind === 'equals' && route.path === bare) { // no i18n
124
+ return route.name;
125
+ }
126
+ if (route.kind === 'regex') { // no i18n
127
+ route.regex.lastIndex = 0; // a /g regular expression keeps a position between tests
128
+ if (route.regex.test(bare)) {
129
+ return route.name;
130
+ }
131
+ }
132
+ if (route.kind === 'pattern' && patternMatches(route.pattern, bare)) { // no i18n
133
+ return routePath(route.pattern).split('/').map((s) => (isWildcard(s) ? '*' : s)).join('/'); // no i18n
134
+ }
135
+ }
136
+ return path;
137
+ }
85
138
  /**
86
139
  * The name for the page as it is now, from its url, or '' to record nothing. Never throws. An app
87
140
  * that wants its own names calls inScreen() as the route changes, which replaces this view.
88
141
  */
89
142
  function currentName() {
90
143
  try {
91
- return cleanName(nameFromUrl(new URL(window.location.href)));
144
+ return cleanName(applyRoutes(nameFromUrl(new URL(window.location.href))));
92
145
  }
93
146
  catch (err) {
94
147
  return ''; // no i18n
@@ -140,8 +193,18 @@ function cancelPending() {
140
193
  * Collects the history calls and events of one navigation into a single check. The first call of
141
194
  * a navigation starts it afresh: a inScreen() from before it belonged to the previous screen,
142
195
  * and a view still waiting is a page left too soon to count.
196
+ *
197
+ * A history call that leaves the screen where it is, is not a navigation. Next.js and other
198
+ * routers replaceState the same url right after a route change, to store their state or the
199
+ * scroll position. Treated as one, it cancelled the view still waiting to settle, and the check
200
+ * that followed found the url already seen and recorded nothing: the page went untracked and the
201
+ * view before it was never ended.
143
202
  */
144
203
  function navigated() {
204
+ var name = currentName();
205
+ if (name !== '' && (name === pendingName || (pendingName === '' && name === lastName))) { // no i18n
206
+ return;
207
+ }
145
208
  if (timer === null) {
146
209
  claimed = false;
147
210
  cancelPending();
@@ -239,3 +302,4 @@ hooks.hooks.screenTrackedByApp = screenTrackedByApp;
239
302
 
240
303
  exports.automaticScreenName = automaticScreenName;
241
304
  exports.enableScreenTracking = enableScreenTracking;
305
+ exports.routePath = routePath;
@@ -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
  /**
@@ -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');
@@ -15,8 +15,7 @@ var transport = require('../core/transport.js');
15
15
  * Sends pending engagements to the Apptics API.
16
16
  */
17
17
  function sendEngagementData(forget) {
18
- // engagementtracking off (getupdates): queued items stay until tracking is back on or they expire
19
- if (env.is_apptics_disabled() || !consent.shouldTrackOrSendData() || !state._ZAP._engagementTracking) {
18
+ if (env.is_apptics_disabled() || !consent.shouldTrackOrSendData()) {
20
19
  return Promise.resolve();
21
20
  }
22
21
  if (navigator.onLine == false) {
@@ -39,6 +38,11 @@ function sendEngagementData(forget) {
39
38
  var _payload = {};
40
39
  var sent = [];
41
40
  utils.cloneObject(engagement_items).forEach(function (item) {
41
+ // a kind the server has turned off (getsdkconfig's event, api, screen or session
42
+ // module) stays queued until it is back on or the item expires
43
+ if (state._ZAP._engagementTracking[item.type] === false) {
44
+ return;
45
+ }
42
46
  const itemWithId = { ...item };
43
47
  if (item.type == constants.AP_CONSTANTS.EVENT) {
44
48
  eventObj.push(item);
@@ -58,6 +62,11 @@ function sendEngagementData(forget) {
58
62
  sent.push(itemWithId);
59
63
  delete item.id;
60
64
  delete item.type;
65
+ // which tabs share a session, which tab shows a screen and when it was last seen: the
66
+ // SDK's own bookkeeping, not part of the payload
67
+ delete item.tabs;
68
+ delete item.tab;
69
+ delete item.lastseen;
61
70
  });
62
71
  if (eventObj.length > 0)
63
72
  _payload.events = eventObj;
@@ -15,8 +15,16 @@ export declare function updateScreenWithEndtime(): Promise<void>;
15
15
  * @param sessionData A set of properties belonging to the session.
16
16
  */
17
17
  export declare function trackSession(sessionData: Dict): Promise<void>;
18
- /** This tab's session when it has one, otherwise the newest open session in storage. */
18
+ /**
19
+ * This tab's session while it is open, otherwise the newest open session in storage: the one the
20
+ * site's other tabs are on, which this tab joins rather than starting a second session beside it.
21
+ */
19
22
  export declare function currentSession(): Promise<QueueItem | undefined>;
23
+ export declare function tabId(): string;
24
+ /** Takes this tab's lock, held until the page is gone. Once per page. */
25
+ export declare function holdTabLock(): void;
26
+ /** Ends the open screen views of tabs that are gone. Resolves right away without Web Locks. */
27
+ export declare function closeScreensOfGoneTabs(): Promise<void>;
20
28
  /**
21
29
  * Writes the waiting session, if there is one. Called by the timer and by anything that tracks an
22
30
  * item, so an event in the first seconds takes its session with it.
@@ -28,8 +36,26 @@ export declare function dropPendingSession(): boolean;
28
36
  export declare function hasPendingSession(): boolean;
29
37
  export declare function startNewSession(time: number): Promise<void>;
30
38
  /**
31
- * Closes this tab's session, so it can be sent: the tab is closing or the page is being replaced.
32
- * Nothing happens when the session is already closed.
39
+ * The tab went inactive. That moment becomes the session's lastactivetime, the time it ends at if
40
+ * the user does not come back within inactiveduration. If they do not, the session is ended at it
41
+ * then, and `onEnded` sends it, rather than staying open until they return or the tab closes.
42
+ * Going inactive twice (a window blur, then the tab hidden) keeps the first moment.
43
+ */
44
+ export declare function markTabInactive(onEnded: () => void): Promise<void>;
45
+ /**
46
+ * The tab is active again. The caller then settles the session (checkAndUpdateSession): back
47
+ * within inactiveduration it continues, later it has ended and a new one starts.
48
+ */
49
+ export declare function markTabActive(): void;
50
+ /** reset(): no timer left running, and the tab counts as active. */
51
+ export declare function resetInactivity(): void;
52
+ /**
53
+ * This tab is closing, or the page is being replaced. The session ends here when this is the last
54
+ * tab using it, so it can be sent; while another tab of the site is still on it, this tab only
55
+ * leaves it, and the last tab to close ends it. Nothing happens when it is already closed.
56
+ *
57
+ * A tab closed while inactive ends the session when the user left (its lastactivetime), not when
58
+ * the tab closed: the time it sat hidden or in the background is not session time.
33
59
  */
34
60
  export declare function endCurrentSession(): Promise<void>;
35
61
  /**