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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/CHANGELOG.md +111 -5
  2. package/README.md +399 -300
  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-VYr43KcB.min.js +3 -0
  8. package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
  9. package/dist/cjs/auto-init.d.ts +3 -3
  10. package/dist/cjs/auto-init.js +1 -1
  11. package/dist/cjs/core/attempts.js +1 -1
  12. package/dist/cjs/core/batch.js +2 -1
  13. package/dist/cjs/core/callsite.d.ts +6 -0
  14. package/dist/cjs/core/callsite.js +25 -3
  15. package/dist/cjs/core/consent.d.ts +5 -1
  16. package/dist/cjs/core/consent.js +8 -6
  17. package/dist/cjs/core/constants.d.ts +35 -10
  18. package/dist/cjs/core/constants.js +31 -15
  19. package/dist/cjs/core/db.d.ts +12 -4
  20. package/dist/cjs/core/db.js +167 -34
  21. package/dist/cjs/core/debug.js +1 -1
  22. package/dist/cjs/core/env.js +1 -1
  23. package/dist/cjs/core/framework-id.d.ts +1 -1
  24. package/dist/cjs/core/framework-id.js +3 -3
  25. package/dist/cjs/core/guard.d.ts +9 -0
  26. package/dist/cjs/core/guard.js +27 -13
  27. package/dist/cjs/core/hooks.d.ts +2 -4
  28. package/dist/cjs/core/hooks.js +2 -3
  29. package/dist/cjs/core/locks.d.ts +9 -0
  30. package/dist/cjs/core/locks.js +22 -0
  31. package/dist/cjs/core/logger.d.ts +3 -4
  32. package/dist/cjs/core/logger.js +4 -40
  33. package/dist/cjs/core/state.d.ts +3 -2
  34. package/dist/cjs/core/state.js +2 -2
  35. package/dist/cjs/core/storage.d.ts +20 -5
  36. package/dist/cjs/core/storage.js +115 -17
  37. package/dist/cjs/core/stylesheet.d.ts +8 -0
  38. package/dist/cjs/core/stylesheet.js +29 -0
  39. package/dist/cjs/core/transport.d.ts +2 -0
  40. package/dist/cjs/core/transport.js +19 -4
  41. package/dist/cjs/core/utils.js +1 -1
  42. package/dist/cjs/core/validate.js +2 -2
  43. package/dist/cjs/device/appversion.d.ts +1 -6
  44. package/dist/cjs/device/appversion.js +13 -1
  45. package/dist/cjs/device/index.d.ts +11 -0
  46. package/dist/cjs/device/index.js +120 -20
  47. package/dist/cjs/engagement/api-rules.d.ts +32 -67
  48. package/dist/cjs/engagement/api-rules.js +144 -157
  49. package/dist/cjs/engagement/auto-api.js +2 -2
  50. package/dist/cjs/engagement/auto-screen.d.ts +2 -0
  51. package/dist/cjs/engagement/auto-screen.js +67 -3
  52. package/dist/cjs/engagement/defined-events.js +1 -1
  53. package/dist/cjs/engagement/send.js +6 -1
  54. package/dist/cjs/engagement/session.d.ts +12 -3
  55. package/dist/cjs/engagement/session.js +161 -25
  56. package/dist/cjs/engagement/track.js +45 -20
  57. package/dist/cjs/errors/send.js +1 -1
  58. package/dist/cjs/errors/track.d.ts +1 -1
  59. package/dist/cjs/errors/track.js +46 -6
  60. package/dist/cjs/feedback/controls.d.ts +6 -0
  61. package/dist/cjs/feedback/controls.js +31 -1
  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 +10 -5
  65. package/dist/cjs/feedback/logs.js +52 -13
  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 +3 -0
  69. package/dist/cjs/feedback/send.js +28 -3
  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 +12 -12
  74. package/dist/cjs/feedback/ui.d.ts +14 -6
  75. package/dist/cjs/feedback/ui.js +126 -48
  76. package/dist/cjs/heatmap/send.js +1 -1
  77. package/dist/cjs/heatmap/track.d.ts +1 -1
  78. package/dist/cjs/heatmap/track.js +87 -16
  79. package/dist/cjs/index.d.ts +17 -63
  80. package/dist/cjs/index.js +104 -92
  81. package/dist/cjs/lifecycle.d.ts +8 -1
  82. package/dist/cjs/lifecycle.js +258 -62
  83. package/dist/cjs/remoteconfig/country.d.ts +4 -0
  84. package/dist/cjs/remoteconfig/country.js +73 -0
  85. package/dist/cjs/remoteconfig/custom.d.ts +7 -7
  86. package/dist/cjs/remoteconfig/custom.js +31 -51
  87. package/dist/cjs/remoteconfig/evaluate.d.ts +3 -3
  88. package/dist/cjs/remoteconfig/evaluate.js +56 -43
  89. package/dist/cjs/remoteconfig/fetch.d.ts +3 -3
  90. package/dist/cjs/remoteconfig/fetch.js +9 -23
  91. package/dist/cjs/remoteconfig/index.d.ts +10 -8
  92. package/dist/cjs/remoteconfig/index.js +65 -39
  93. package/dist/cjs/remoteconfig/store.js +1 -1
  94. package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
  95. package/dist/cjs/remoteconfig/tz-countries.js +13 -0
  96. package/dist/cjs/remotelog/gate.d.ts +2 -11
  97. package/dist/cjs/remotelog/gate.js +9 -35
  98. package/dist/cjs/remotelog/index.d.ts +4 -21
  99. package/dist/cjs/remotelog/logger.d.ts +11 -17
  100. package/dist/cjs/remotelog/logger.js +30 -58
  101. package/dist/cjs/remotelog/send.js +10 -10
  102. package/dist/cjs/remotelogger/index.d.ts +12 -0
  103. package/dist/cjs/remotelogger/index.js +18 -0
  104. package/dist/cjs/scheduler.js +2 -2
  105. package/dist/cjs/types.d.ts +155 -82
  106. package/dist/cjs/user/index.js +11 -6
  107. package/dist/esm/auto-init.d.ts +3 -3
  108. package/dist/esm/auto-init.js +1 -1
  109. package/dist/esm/core/attempts.js +1 -1
  110. package/dist/esm/core/batch.js +2 -1
  111. package/dist/esm/core/callsite.d.ts +6 -0
  112. package/dist/esm/core/callsite.js +25 -4
  113. package/dist/esm/core/consent.d.ts +5 -1
  114. package/dist/esm/core/consent.js +9 -7
  115. package/dist/esm/core/constants.d.ts +35 -10
  116. package/dist/esm/core/constants.js +30 -15
  117. package/dist/esm/core/db.d.ts +12 -4
  118. package/dist/esm/core/db.js +164 -35
  119. package/dist/esm/core/debug.js +1 -1
  120. package/dist/esm/core/env.js +1 -1
  121. package/dist/esm/core/framework-id.d.ts +1 -1
  122. package/dist/esm/core/framework-id.js +3 -3
  123. package/dist/esm/core/guard.d.ts +9 -0
  124. package/dist/esm/core/guard.js +27 -14
  125. package/dist/esm/core/hooks.d.ts +2 -4
  126. package/dist/esm/core/hooks.js +2 -3
  127. package/dist/esm/core/locks.d.ts +9 -0
  128. package/dist/esm/core/locks.js +20 -0
  129. package/dist/esm/core/logger.d.ts +3 -4
  130. package/dist/esm/core/logger.js +4 -40
  131. package/dist/esm/core/state.d.ts +3 -2
  132. package/dist/esm/core/state.js +2 -2
  133. package/dist/esm/core/storage.d.ts +20 -5
  134. package/dist/esm/core/storage.js +113 -18
  135. package/dist/esm/core/stylesheet.d.ts +8 -0
  136. package/dist/esm/core/stylesheet.js +27 -0
  137. package/dist/esm/core/transport.d.ts +2 -0
  138. package/dist/esm/core/transport.js +19 -5
  139. package/dist/esm/core/utils.js +1 -1
  140. package/dist/esm/core/validate.js +2 -2
  141. package/dist/esm/device/appversion.d.ts +1 -6
  142. package/dist/esm/device/appversion.js +13 -2
  143. package/dist/esm/device/index.d.ts +11 -0
  144. package/dist/esm/device/index.js +123 -24
  145. package/dist/esm/engagement/api-rules.d.ts +32 -67
  146. package/dist/esm/engagement/api-rules.js +143 -155
  147. package/dist/esm/engagement/auto-api.js +3 -3
  148. package/dist/esm/engagement/auto-screen.d.ts +2 -0
  149. package/dist/esm/engagement/auto-screen.js +67 -4
  150. package/dist/esm/engagement/defined-events.js +1 -1
  151. package/dist/esm/engagement/send.js +6 -1
  152. package/dist/esm/engagement/session.d.ts +12 -3
  153. package/dist/esm/engagement/session.js +162 -29
  154. package/dist/esm/engagement/track.js +52 -27
  155. package/dist/esm/errors/send.js +1 -1
  156. package/dist/esm/errors/track.d.ts +1 -1
  157. package/dist/esm/errors/track.js +46 -6
  158. package/dist/esm/feedback/controls.d.ts +6 -0
  159. package/dist/esm/feedback/controls.js +31 -2
  160. package/dist/esm/feedback/index.d.ts +8 -7
  161. package/dist/esm/feedback/index.js +34 -13
  162. package/dist/esm/feedback/logs.d.ts +10 -5
  163. package/dist/esm/feedback/logs.js +54 -15
  164. package/dist/esm/feedback/quartz.d.ts +18 -0
  165. package/dist/esm/feedback/quartz.js +99 -0
  166. package/dist/esm/feedback/send.d.ts +3 -0
  167. package/dist/esm/feedback/send.js +28 -5
  168. package/dist/esm/feedback/styles.d.ts +10 -0
  169. package/dist/esm/feedback/styles.js +4 -0
  170. package/dist/esm/feedback/template.d.ts +1 -1
  171. package/dist/esm/feedback/template.js +12 -12
  172. package/dist/esm/feedback/ui.d.ts +14 -6
  173. package/dist/esm/feedback/ui.js +126 -49
  174. package/dist/esm/heatmap/send.js +1 -1
  175. package/dist/esm/heatmap/track.d.ts +1 -1
  176. package/dist/esm/heatmap/track.js +87 -16
  177. package/dist/esm/index.d.ts +17 -63
  178. package/dist/esm/index.js +108 -80
  179. package/dist/esm/lifecycle.d.ts +8 -1
  180. package/dist/esm/lifecycle.js +266 -71
  181. package/dist/esm/remoteconfig/country.d.ts +4 -0
  182. package/dist/esm/remoteconfig/country.js +70 -0
  183. package/dist/esm/remoteconfig/custom.d.ts +7 -7
  184. package/dist/esm/remoteconfig/custom.js +32 -52
  185. package/dist/esm/remoteconfig/evaluate.d.ts +3 -3
  186. package/dist/esm/remoteconfig/evaluate.js +57 -44
  187. package/dist/esm/remoteconfig/fetch.d.ts +3 -3
  188. package/dist/esm/remoteconfig/fetch.js +9 -23
  189. package/dist/esm/remoteconfig/index.d.ts +10 -8
  190. package/dist/esm/remoteconfig/index.js +66 -40
  191. package/dist/esm/remoteconfig/store.js +1 -1
  192. package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
  193. package/dist/esm/remoteconfig/tz-countries.js +11 -0
  194. package/dist/esm/remotelog/gate.d.ts +2 -11
  195. package/dist/esm/remotelog/gate.js +10 -32
  196. package/dist/esm/remotelog/index.d.ts +4 -21
  197. package/dist/esm/remotelog/logger.d.ts +11 -17
  198. package/dist/esm/remotelog/logger.js +32 -60
  199. package/dist/esm/remotelog/send.js +12 -12
  200. package/dist/esm/remotelogger/index.d.ts +12 -0
  201. package/dist/esm/remotelogger/index.js +13 -0
  202. package/dist/esm/scheduler.js +2 -2
  203. package/dist/esm/types.d.ts +155 -82
  204. package/dist/esm/user/index.js +12 -6
  205. package/dist/feedback.esm.min.js +2 -2
  206. package/dist/feedback.esm.min.js.map +1 -1
  207. package/package.json +14 -16
  208. package/dist/chunks/batch-BU_mTpm1.min.js +0 -3
  209. package/dist/chunks/batch-BU_mTpm1.min.js.map +0 -1
  210. package/dist/chunks/logs-Bea5zUW_.min.js +0 -3
  211. package/dist/chunks/logs-Bea5zUW_.min.js.map +0 -1
  212. package/dist/cjs/heatmap/index.d.ts +0 -16
  213. package/dist/cjs/heatmap/index.js +0 -22
  214. package/dist/cjs/remotelog/index.js +0 -55
  215. package/dist/esm/heatmap/index.d.ts +0 -16
  216. package/dist/esm/heatmap/index.js +0 -17
  217. package/dist/esm/remotelog/index.js +0 -51
  218. package/dist/heatmap.esm.min.js +0 -3
  219. package/dist/heatmap.esm.min.js.map +0 -1
  220. 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.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
2
  'use strict';
3
3
 
4
4
  var constants = require('./core/constants.js');
@@ -9,7 +9,6 @@ var storage = require('./core/storage.js');
9
9
  var hooks = require('./core/hooks.js');
10
10
  var debug = require('./core/debug.js');
11
11
  var env = require('./core/env.js');
12
- var consent = require('./core/consent.js');
13
12
  var index = require('./device/index.js');
14
13
  var appversion = require('./device/appversion.js');
15
14
  var session = require('./engagement/session.js');
@@ -18,10 +17,10 @@ var autoScreen = require('./engagement/auto-screen.js');
18
17
  var track = require('./engagement/track.js');
19
18
  var track$1 = require('./errors/track.js');
20
19
  var scheduler = require('./scheduler.js');
21
- require('./remotelog/index.js');
20
+ var send = require('./remotelog/send.js');
22
21
  var index$1 = require('./user/index.js');
23
22
  var attempts = require('./core/attempts.js');
24
- var send = require('./remotelog/send.js');
23
+ var frameworkId = require('./core/framework-id.js');
25
24
 
26
25
  let listenersAttached = false;
27
26
  /** The config init() was last called with; reset() starts again from it. */
@@ -40,16 +39,29 @@ function isBrowser() {
40
39
  }
41
40
  }
42
41
  /**
43
- * Canonical config: serviceUrl, zak, identifier, appversion.
44
- * The console snippet uses older names (base_domain, token), which are accepted here.
45
- * serviceUrl loses any trailing slash, because request paths add their own.
42
+ * The config as the SDK keeps it: base_url, auth_key and app_domain under their inside names
43
+ * (serviceUrl, zak, identifier; see CONFIG_KEYS), and appversion as text. An earlier name of a
44
+ * key is still read when the key itself is not given, with a warning naming the key to use.
45
+ * The base url loses any trailing slash, because request paths add their own.
46
46
  */
47
47
  function normaliseConfig(config) {
48
48
  var settings = Object.assign({}, config);
49
- Object.keys(constants.CONFIG_ALIASES).forEach(function (key) {
50
- if (utils.isEmpty(settings[key]) && !utils.isEmpty(settings[constants.CONFIG_ALIASES[key]])) {
51
- settings[key] = settings[constants.CONFIG_ALIASES[key]];
49
+ Object.keys(constants.CONFIG_KEYS).forEach(function (key) {
50
+ var inside = constants.CONFIG_KEYS[key];
51
+ if (!utils.isEmpty(config[key])) {
52
+ settings[inside] = config[key];
53
+ return;
52
54
  }
55
+ delete settings[inside];
56
+ Object.keys(constants.RETIRED_CONFIG_NAMES).forEach(function (old) {
57
+ if (constants.RETIRED_CONFIG_NAMES[old] !== key || utils.isEmpty(config[old])) {
58
+ return;
59
+ }
60
+ logger.warnAlways('Apptics.init: ' + old + ' is now ' + key + '; rename it in your config'); // no i18n
61
+ if (utils.isEmpty(settings[inside])) {
62
+ settings[inside] = config[old];
63
+ }
64
+ });
53
65
  });
54
66
  if (!utils.isEmpty(settings.serviceUrl)) {
55
67
  settings.serviceUrl = String(settings.serviceUrl).replace(/\/+$/, ''); // no i18n
@@ -75,21 +87,51 @@ const APPVERSION_PLACEHOLDERS = ['YOUR_APP_VERSION', 'YOUR_BUILD_OR_APP_VERSION'
75
87
  function isAppVersionPlaceholder(version) {
76
88
  return APPVERSION_PLACEHOLDERS.indexOf(version.toUpperCase()) !== -1;
77
89
  }
90
+ /**
91
+ * The framework id this SDK reports to serviceUrl. The id built into the package is the one
92
+ * sdk-apptics.zoho.com issued at publish time, and localzoho servers don't know it: any
93
+ * localzoho serviceUrl reports 5970 instead, the id apptics.localzoho.com issued for 2.0.0-beta
94
+ * (Browser), so the same package also runs there.
95
+ */
96
+ const LOCALZOHO_FRAMEWORK_ID = 5970;
97
+ function frameworkIdFor(serviceUrl) {
98
+ var host = serviceUrl;
99
+ try {
100
+ host = new URL(serviceUrl).hostname;
101
+ }
102
+ catch (err) {
103
+ // not a url: judge the text as it is
104
+ }
105
+ return host.indexOf('localzoho') !== -1 ? LOCALZOHO_FRAMEWORK_ID : frameworkId.FRAMEWORK_ID; // no i18n
106
+ }
78
107
  /** Which app this browser's stored data belongs to, before app/addversion has named it. */
79
108
  function appKey(settings) {
80
109
  return settings.serviceUrl + '|' + settings.identifier; // no i18n
81
110
  }
111
+ /**
112
+ * The app version stored data belongs to, read before app/addversion replaces it: what tells a
113
+ * new release of the app from a reload. '' on a first visit.
114
+ */
115
+ var previousAppVersion = ''; // no i18n
82
116
  /**
83
117
  * Data stored for another app on this origin is discarded before anything else reads it,
84
118
  * including the stored app/addversion answer that would otherwise hand out that app's ids.
85
119
  */
86
120
  function discardOtherAppData(settings) {
87
- var stored = storage.user_defaults.item('appkey'); // no i18n
121
+ // which app it is, and which version, are in app_version_info (serviceUrl, identifier and
122
+ // appversion): no keys of their own. 2.0.0-beta's appkey and appversion are read this once
123
+ var info = storage.user_defaults.item('app_version_info') || {}; // no i18n
124
+ var stored = !utils.isEmpty(info.serviceUrl) && !utils.isEmpty(info.identifier)
125
+ ? appKey(info)
126
+ : storage.user_defaults.item('appkey'); // no i18n
127
+ previousAppVersion = String((!utils.isEmpty(info.appversion) ? info.appversion : storage.user_defaults.item('appversion')) || ''); // no i18n
128
+ storage.user_defaults.add('appkey', undefined); // no i18n
129
+ storage.user_defaults.add('appversion', undefined); // no i18n
88
130
  if (!utils.isEmpty(stored) && stored !== appKey(settings)) {
89
131
  logger.Console.log('init: stored data belongs to another app, resetting'); // no i18n
90
132
  clearStorage();
133
+ previousAppVersion = ''; // no i18n
91
134
  }
92
- storage.user_defaults.add('appkey', appKey(settings)); // no i18n
93
135
  }
94
136
  /**
95
137
  * Timing an app may tune in init(). Each has a range: a value outside it is ignored with a
@@ -142,18 +184,18 @@ function clearStorage() {
142
184
  /**
143
185
  * Starts the SDK. The config object is required: pass it directly, or let the console snippet
144
186
  * supply it (auto-init reads window.appticssettings._defaultoptions and calls init with it).
145
- * Returns true when the config has serviceUrl, zak, identifier and appversion and start-up has
187
+ * Returns true when the config has base_url, auth_key, app_domain and appversion and start-up has
146
188
  * begun (see ready()), false when it was already running, the config is missing or incomplete, or
147
189
  * there is no browser. Start-up calls app/addversion with those four, which answers with aaid, the
148
190
  * portal and project ids and the rest; then device/add registers the browser, as in 1.x.
149
191
  */
150
192
  function init(config) {
151
193
  if (!config || typeof config !== 'object') { // no i18n
152
- logger.warnAlways('Apptics.init: a config object is required, e.g. apptics.init({ serviceUrl, zak, identifier, appversion })'); // no i18n
194
+ logger.warnAlways('Apptics.init: a config object is required, e.g. Apptics.init({ base_url, auth_key, app_domain, appversion })'); // no i18n
153
195
  return false;
154
196
  }
155
197
  if (state._ZAP._initialised) {
156
- logger.Console.warn('Apptics.init: already initialised; call apptics.reset() to start again'); // no i18n
198
+ logger.Console.warn('Apptics.init: already initialised; call Apptics.reset() to start again'); // no i18n
157
199
  return false;
158
200
  }
159
201
  if (!isBrowser()) {
@@ -162,7 +204,7 @@ function init(config) {
162
204
  }
163
205
  var settings = normaliseConfig(config);
164
206
  var missing = constants.REQUIRED_CONFIG.filter(function (key) {
165
- return utils.isEmpty(settings[key]);
207
+ return utils.isEmpty(settings[constants.CONFIG_KEYS[key] || key]);
166
208
  });
167
209
  if (missing.length > 0) {
168
210
  logger.warnAlways('Apptics.init: missing required config: ' + missing.join(', ')); // no i18n
@@ -176,16 +218,14 @@ function init(config) {
176
218
  state._ZAP._defaultoptions = settings;
177
219
  // every request goes to serviceUrl; there is no other host to learn about
178
220
  state._ZAP._defaultoptions.base_domain = settings.serviceUrl;
221
+ // decided per init(), so a reset() or a page on another host never keeps the last one
222
+ state._ZAP._frameworkID = frameworkIdFor(settings.serviceUrl);
179
223
  // marks the SDK as started for the checks that need it; app/addversion replaces it with aaid
180
224
  state._ZAP._token = settings.identifier;
181
225
  // console_log: the SDK's own activity in the browser console
182
226
  if (!utils.isEmpty(config.console_log)) {
183
227
  state._ZAP._config.debug = !!config.console_log;
184
228
  }
185
- // track_session: false records no sessions, as Android's automaticSessionsTrackingStatus
186
- state._ZAP._config.track_session = typeof config.track_session === 'boolean' ? config.track_session : constants.AP_CONFIG.track_session; // no i18n
187
- // show_logs: the app's own logger lines in the browser console, as Android's showLogs
188
- state._ZAP._config.show_logs = typeof config.show_logs === 'boolean' ? config.show_logs : constants.AP_CONFIG.show_logs; // no i18n
189
229
  if (typeof config.ignore_do_not_track === 'boolean') { // no i18n
190
230
  state._ZAP._config.ignore_do_not_track = config.ignore_do_not_track;
191
231
  }
@@ -198,10 +238,14 @@ function init(config) {
198
238
  debug.applyDebugConfig(state._ZAP._config.debug_api);
199
239
  // what to call this device in the debug stream; see devicename in core/transport.ts
200
240
  state._ZAP._config.device_name = typeof config.device_name === 'string' ? config.device_name : constants.AP_CONFIG.device_name; // no i18n
201
- // remote logging: the SDK's own diagnostics, sent to console/addlog
202
- state._ZAP._config.remote_log = constants.AP_CONFIG.remote_log;
203
- if (typeof config.remote_log === 'boolean') { // no i18n
204
- state._ZAP._config.remote_log = config.remote_log;
241
+ // the SDK no longer reports its own failures to the Remote Logger page: only the lines an app
242
+ // logs with AppticsLogger go there
243
+ if (config.remote_log !== undefined) {
244
+ logger.warnAlways('Apptics.init: remote_log is no longer read; the Remote Logger page shows the lines your app logs with AppticsLogger'); // no i18n
245
+ }
246
+ // AppticsLogger lines go to the Remote Logger page only, never to the browser console
247
+ if (config.show_logs !== undefined) {
248
+ logger.warnAlways('Apptics.init: show_logs is no longer read; AppticsLogger lines are not printed to the browser console'); // no i18n
205
249
  }
206
250
  // automatic api tracking: the app's own requests, wrapped. Which hosts it records is
207
251
  // configureApiTracking's to say; the two keys that once did it are refused out loud rather
@@ -209,22 +253,38 @@ function init(config) {
209
253
  state._ZAP._config.track_api = constants.AP_CONFIG.track_api;
210
254
  ['track_api_hosts', 'track_api_exclude_hosts'].forEach(function (key) {
211
255
  if (config[key] !== undefined) {
212
- logger.warnAlways('Apptics.init: ' + key + ' is no longer supported; use configureApiTracking(new ApiTrackingBuilder().' // no i18n
256
+ logger.warnAlways('Apptics.init: ' + key + ' is no longer supported; use AppticsApi.configure(new AppticsApi.ApiTrackingBuilder().' // no i18n
213
257
  + (key === 'track_api_hosts' ? 'allowOnlyDomains' : 'ignoreDomains') + '(...).build())'); // no i18n
214
258
  }
215
259
  });
260
+ // screen recording is set up in the console now, and arrives with getsdkconfig
261
+ ['DC', 'quartz_workspace_key', 'quartz_department_key'].forEach(function (key) {
262
+ if (config[key] !== undefined) {
263
+ logger.warnAlways('Apptics.init: ' + key + ' is no longer read; screen recording in the feedback form is set up from the Apptics console'); // no i18n
264
+ }
265
+ });
266
+ // sessions are what everything else is recorded in; an app that wants nothing tracked uses
267
+ // AppticsSettings.setTrackingStatus instead
268
+ if (config.track_session !== undefined) {
269
+ logger.warnAlways('Apptics.init: track_session is no longer read; sessions are always recorded. To stop tracking, use AppticsSettings.setTrackingStatus(AppticsSettings.AP_TRACKING_STATUS.NO_TRACKING)'); // no i18n
270
+ }
216
271
  if (typeof config.track_api === 'boolean') { // no i18n
217
272
  state._ZAP._config.track_api = config.track_api;
218
273
  }
219
274
  autoApi.enableApiTracking(state._ZAP._config.track_api);
220
- // heatmap capture: a no-op unless the app imported the heatmap entry, which sets the hook
275
+ // heatmap capture: switched through the hook heatmap/track.ts sets
221
276
  state._ZAP._config.track_heatmap = typeof config.track_heatmap === 'boolean' // no i18n
222
277
  ? config.track_heatmap
223
278
  : constants.AP_CONFIG.track_heatmap;
224
- hooks.hooks.enableHeatmap(state._ZAP._config.track_heatmap);
279
+ hooks.hooks.trackHeatmap(state._ZAP._config.track_heatmap);
225
280
  // automatic screen tracking, on unless the config says false: switched on by start(), once a
226
281
  // screen view can be stored
227
282
  state._ZAP._config.track_screen = typeof config.track_screen === 'boolean' ? config.track_screen : constants.AP_CONFIG.track_screen; // no i18n
283
+ state._ZAP._config.screen_routes = screenRouteRules(config.screen_routes);
284
+ // a theme in the config wins; without one, what AppticsFeedback.setFeedbackTheme set stays
285
+ if (config.feedback_theme === 'light' || config.feedback_theme === 'dark' || config.feedback_theme === 'auto') { // no i18n
286
+ state._ZAP._config.feedback_theme = config.feedback_theme;
287
+ }
228
288
  applyIntervals(config);
229
289
  lastConfig = config;
230
290
  state._ZAP._initialised = true;
@@ -250,6 +310,23 @@ function init(config) {
250
310
  if (myGeneration !== generation) {
251
311
  return false;
252
312
  }
313
+ if (!registered && appversion.appVersionFailedForNetwork()) {
314
+ // a first visit with no network: start once it is back, keeping what the app
315
+ // tracks until then queued, rather than giving up for the rest of the page
316
+ logger.warnAlways('Apptics.init: offline, so app/addversion is tried again when the browser is back online; calls made until then are kept'); // no i18n
317
+ return registerWhenOnline(myGeneration).then(function (ok) {
318
+ if (myGeneration !== generation) {
319
+ return false;
320
+ }
321
+ if (!ok) {
322
+ state._ZAP._initialised = false;
323
+ state._ZAP._token = ''; // no i18n
324
+ state._ZAP._pending = [];
325
+ return false;
326
+ }
327
+ return start(config, myGeneration);
328
+ });
329
+ }
253
330
  if (!registered) {
254
331
  logger.warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
255
332
  state._ZAP._initialised = false;
@@ -265,42 +342,137 @@ function init(config) {
265
342
  });
266
343
  return true;
267
344
  }
345
+ /** How often a start-up waiting on the network tries app/addversion again, besides on `online`. */
346
+ const OFFLINE_RETRY_MS = 60 * 1000;
347
+ /**
348
+ * app/addversion again once the network is back: on the `online` event, and every minute for a
349
+ * network that came back without one. Resolves true once registered, false when the server
350
+ * refuses it or a reset()/new init() took over.
351
+ */
352
+ function registerWhenOnline(myGeneration) {
353
+ return new Promise(function (resolve) {
354
+ var timer = null;
355
+ var trying = false;
356
+ function done(ok) {
357
+ window.removeEventListener('online', attempt); // no i18n
358
+ if (timer !== null)
359
+ clearInterval(timer);
360
+ resolve(ok);
361
+ }
362
+ function attempt() {
363
+ if (myGeneration !== generation) {
364
+ done(false);
365
+ return;
366
+ }
367
+ if (trying || navigator.onLine === false) {
368
+ return;
369
+ }
370
+ trying = true;
371
+ appversion.registerAppVersion().then(function (registered) {
372
+ trying = false;
373
+ if (registered) {
374
+ done(true);
375
+ }
376
+ else if (!appversion.appVersionFailedForNetwork()) {
377
+ logger.warnAlways('Apptics.init: app/addversion did not return the app ids, not starting'); // no i18n
378
+ done(false);
379
+ }
380
+ });
381
+ }
382
+ window.addEventListener('online', attempt); // no i18n
383
+ timer = setInterval(attempt, OFFLINE_RETRY_MS);
384
+ });
385
+ }
386
+ /**
387
+ * screen_routes as init() keeps it: each entry checked, an exact path cleaned up, a regex given as
388
+ * a string compiled. An entry that cannot be used is left out, and said, rather than matching
389
+ * something its author did not mean.
390
+ */
391
+ function screenRouteRules(routes) {
392
+ if (!Array.isArray(routes)) {
393
+ return [];
394
+ }
395
+ var out = [];
396
+ routes.forEach(function (route) {
397
+ if (typeof route === 'string') { // no i18n
398
+ if (route.trim() !== '') { // no i18n
399
+ out.push({ kind: 'pattern', pattern: route.trim() }); // no i18n
400
+ }
401
+ return;
402
+ }
403
+ var rule = route && typeof route === 'object' ? route : null; // no i18n
404
+ var name = rule && typeof rule.name === 'string' ? rule.name.trim() : ''; // no i18n
405
+ if (rule && name !== '' && typeof rule.equals === 'string' && rule.equals.trim() !== '') { // no i18n
406
+ out.push({ kind: 'equals', path: autoScreen.routePath(rule.equals.trim()), name: name }); // no i18n
407
+ return;
408
+ }
409
+ if (rule && name !== '' && rule.regex !== undefined) {
410
+ var regex = null;
411
+ if (rule.regex instanceof RegExp) {
412
+ regex = rule.regex;
413
+ }
414
+ else if (typeof rule.regex === 'string' && rule.regex !== '') { // no i18n
415
+ try {
416
+ regex = new RegExp(rule.regex);
417
+ }
418
+ catch (err) {
419
+ regex = null;
420
+ }
421
+ }
422
+ if (regex) {
423
+ out.push({ kind: 'regex', regex: regex, name: name }); // no i18n
424
+ return;
425
+ }
426
+ }
427
+ logger.warnAlways('Apptics.init: screen_routes entry ' + safeJson(route) + ' is left out: give a route pattern, { equals, name } or { regex, name }'); // no i18n
428
+ });
429
+ return out;
430
+ }
431
+ /** An entry as the warning shows it: a RegExp as its source, which JSON.stringify would hide. */
432
+ function safeJson(value) {
433
+ try {
434
+ return JSON.stringify(value, function (_key, one) { return one instanceof RegExp ? String(one) : one; }) || String(value);
435
+ }
436
+ catch (err) {
437
+ return String(value);
438
+ }
439
+ }
268
440
  /** Resolves true once start-up has finished, false if init() refused or storage is unusable. */
269
441
  function ready() {
270
442
  return state._ZAP._readyPromise || Promise.resolve(false);
271
443
  }
272
444
  function start(config, myGeneration) {
445
+ // this tab's lock, before any screen view: it is how other tabs tell this one is still open
446
+ session.holdTabLock();
273
447
  // app/addversion named the app; data stored for another one was discarded before it ran
274
448
  state._ZAP._token = String(state._ZAP._defaultoptions.aaid);
275
- storage.user_defaults.add('token', state._ZAP._token); // no i18n
276
449
  // last known server settings (getupdates) apply before anything is tracked
277
450
  index.restoreServerSettings();
278
- // required by init(), so _defaultoptions.appversion is always the app's own version, as the
279
- // text normaliseConfig made of it: there is no default to fall back to, and the stored value
280
- // below is kept only to notice when the version changes
281
- var uuid = storage.user_defaults.item('uuid'); // no i18n
282
- if (utils.isEmpty(uuid)) {
283
- uuid = utils.getNewUUID();
284
- storage.user_defaults.add('uuid', uuid); // no i18n
285
- }
286
- state._ZAP._uuid = uuid;
287
- var stored_appversion = storage.user_defaults.item('appversion'); // no i18n
288
- if (utils.isEmpty(stored_appversion)) {
289
- storage.user_defaults.add('appversion', state._ZAP._defaultoptions.appversion); // no i18n
290
- stored_appversion = state._ZAP._defaultoptions.appversion;
291
- }
292
- var appVersionChanged = stored_appversion != state._ZAP._defaultoptions.appversion;
451
+ // a new release of the app, against the version the stored data was for (see
452
+ // discardOtherAppData): a first visit has none, and is no change
453
+ var appVersionChanged = previousAppVersion !== '' && previousAppVersion != state._ZAP._defaultoptions.appversion; // no i18n
293
454
  state._ZAP._ignoreDnt = state._ZAP._config.ignore_do_not_track;
294
- storage.tracking_preference.update_status(consent.getTrackingStatus());
295
- index.checkAndUpdateDeviceInfo();
296
- // returning visit: ask the server for its settings now instead of waiting 2 minutes
297
- // (first visit: device/add triggers it once the ids exist)
298
- index.refreshServerSettings();
455
+ // the visitor's own stored choice, not getTrackingStatus(): that answers NO_TRACKING while Do
456
+ // Not Track is on, and storing it made one such visit switch tracking off for good
457
+ storage.tracking_preference.update_status(storage.tracking_preference.opt_in_status());
458
+ // the uuid and device info, and a registration on a first visit, under a lock shared with the
459
+ // site's other tabs (see prepareDevice)
460
+ return index.prepareDevice().then(function () {
461
+ if (myGeneration !== generation) {
462
+ return false;
463
+ }
464
+ return startTracking(appVersionChanged, myGeneration);
465
+ });
466
+ }
467
+ /** The rest of start-up, once the device is set up. */
468
+ function startTracking(appVersionChanged, myGeneration) {
469
+ // no getsdkconfig here, straight after app/addversion: it is asked for once device/add has
470
+ // succeeded (registerDeviceWithApptics), then every getupdates_interval. A returning visit
471
+ // starts from the settings saved by the last one (restoreServerSettings, in start())
299
472
  if (appVersionChanged) {
300
473
  state._ZAP._device_info.appversionid = ''; // no i18n
301
474
  index.updateDeviceWithApptics(state._ZAP._device_info).then(function (result) {
302
475
  if (utils.isSuccess(result)) {
303
- storage.user_defaults.add('appversion', state._ZAP._defaultoptions.appversion); // no i18n
304
476
  logger.Console.log('init: appversion updated to ' + state._ZAP._defaultoptions.appversion); // no i18n
305
477
  }
306
478
  }).catch(function (err) {
@@ -351,7 +523,7 @@ function reset() {
351
523
  autoApi.enableApiTracking(false);
352
524
  autoScreen.enableScreenTracking(false);
353
525
  appversion.clearAppVersion();
354
- hooks.hooks.enableHeatmap(false);
526
+ hooks.hooks.trackHeatmap(false);
355
527
  hooks.hooks.resetHeatmap();
356
528
  hooks.hooks.resetRemoteConfig();
357
529
  clearStorage();
@@ -367,7 +539,7 @@ function reset() {
367
539
  state._ZAP._apid = ''; // no i18n
368
540
  state._ZAP._mapid = ''; // no i18n
369
541
  state._ZAP._sending = false;
370
- logger.Console.log('apptics.reset()'); // no i18n
542
+ logger.Console.log('Apptics.reset()'); // no i18n
371
543
  // start again with the config init() was called with
372
544
  if (lastConfig) {
373
545
  init(lastConfig);
@@ -376,7 +548,20 @@ function reset() {
376
548
  function isActive() {
377
549
  return state._ZAP._ready;
378
550
  }
379
- // Declarative click tracking: <el apeventaction="name" apeventprops='{"k":"v"}'> inside <el apgroupaction="group">
551
+ /**
552
+ * A click-tracking attribute, in either spelling: `apeventaction`, or `data-apeventaction`, which
553
+ * is valid HTML and what React and TypeScript accept without declaring the attribute.
554
+ */
555
+ function trackingAttribute(el, name) {
556
+ var value = el.getAttribute(name);
557
+ return value !== null && value !== '' ? value : el.getAttribute('data-' + name); // no i18n
558
+ }
559
+ /** `[name]` and `[data-name]`, empty values left out, for closest(). */
560
+ function trackingSelector(name) {
561
+ return '[' + name + ']:not([' + name + '=""]),[data-' + name + ']:not([data-' + name + '=""])'; // no i18n
562
+ }
563
+ // Declarative click tracking: <el apeventaction="name" apeventprops='{"k":"v"}'> inside <el apgroupaction="group">,
564
+ // or the same attributes with a data- prefix
380
565
  function onDocumentClick(event) {
381
566
  if (!window.appticsReadyStatus || !isActive()) {
382
567
  return;
@@ -388,15 +573,15 @@ function onDocumentClick(event) {
388
573
  if (!start) {
389
574
  return;
390
575
  }
391
- var targetElem = start.closest('[apeventaction]:not([apeventaction=""])'); // no i18n
576
+ var targetElem = start.closest(trackingSelector('apeventaction')); // no i18n
392
577
  if (!targetElem) {
393
578
  return;
394
579
  }
395
- var eventName = targetElem.getAttribute('apeventaction') || ''; // no i18n
396
- var groupElem = targetElem.closest('[apgroupaction]'); // no i18n
397
- var groupName = (groupElem && groupElem.getAttribute('apgroupaction')) || constants.AP_CONFIG.default_group_name; // no i18n
580
+ var eventName = trackingAttribute(targetElem, 'apeventaction') || ''; // no i18n
581
+ var groupElem = targetElem.closest(trackingSelector('apgroupaction')); // no i18n
582
+ var groupName = (groupElem && trackingAttribute(groupElem, 'apgroupaction')) || constants.AP_CONFIG.default_group_name; // no i18n
398
583
  var eventProps = {};
399
- var rawProps = targetElem.getAttribute('apeventprops'); // no i18n
584
+ var rawProps = trackingAttribute(targetElem, 'apeventprops'); // no i18n
400
585
  if (!utils.isEmpty(rawProps)) {
401
586
  try {
402
587
  eventProps = JSON.parse(rawProps);
@@ -502,8 +687,8 @@ function attachListeners() {
502
687
  // until it has them: register before flushing, and flush once that has landed
503
688
  if (index.needsDevice()) {
504
689
  logger.Console.log('back online: registering this device before sending'); // no i18n
505
- index.checkAndUpdateDeviceInfo();
506
- index.deviceReady().then(function () {
690
+ // the same locked path as start-up, so two tabs coming back online register it once
691
+ index.prepareDevice().then(index.deviceReady).then(function () {
507
692
  if (!isActive())
508
693
  return;
509
694
  scheduler.flush();
@@ -525,14 +710,25 @@ function attachListeners() {
525
710
  track$1.trackError(event);
526
711
  }
527
712
  });
713
+ // in the capture phase: a <script>, <img> or <link> that fails to load fires `error` on the
714
+ // element, and it does not bubble, so a bubbling listener never hears of it. One listener
715
+ // hears both kinds, the window's own errors included, so none is counted twice
528
716
  window.addEventListener('error', function (event) {
529
- if (isActive() && state._ZAP._isGlobalErrorTracking) {
530
- track$1.trackError(event);
717
+ if (!isActive() || !state._ZAP._isGlobalErrorTracking) {
718
+ return;
531
719
  }
532
- });
720
+ var target = event.target;
721
+ if (target && target !== window && typeof target.tagName === 'string') { // no i18n
722
+ // named by describeTarget: Event "error" on <script src="...">
723
+ track$1.trackError({ type: 'error', reason: event }); // no i18n
724
+ return;
725
+ }
726
+ track$1.trackError(event);
727
+ }, true);
533
728
  window.addEventListener('click', onDocumentClick, true); // no i18n
534
729
  }
535
730
 
536
731
  exports.init = init;
537
732
  exports.ready = ready;
538
733
  exports.reset = reset;
734
+ exports.screenRouteRules = screenRouteRules;
@@ -0,0 +1,4 @@
1
+ /** The ISO code of a time zone's country, '' for one with none (UTC) or unknown. */
2
+ export declare function timeZoneCountryCode(timeZone: string): string;
3
+ /** The country: the user's, else the time zone's. '' when neither says. */
4
+ export declare function deviceCountry(): string;
@@ -0,0 +1,73 @@
1
+ /*! @zoho_apptics/apptics-js-sdk v2.0.0-beta.1 | (c) Zoho Corporation Private Limited | see LICENSE */
2
+ 'use strict';
3
+
4
+ var state = require('../core/state.js');
5
+ var env = require('../core/env.js');
6
+ var tzCountries = require('./tz-countries.js');
7
+
8
+ /**
9
+ * The country of this device, for remote config's `country` criteria, by its English name as the
10
+ * console lists it ("India").
11
+ *
12
+ * The one AppticsUser.setUser registered comes first. Otherwise it is the country of the
13
+ * browser's time zone, which is the device clock's: Asia/Kolkata (or Chrome's Asia/Calcutta) is
14
+ * India, whatever language the browser is in. A browser has no other source short of asking for
15
+ * the user's location, which is a permission prompt. A zone shared by several countries is the
16
+ * one tzdata names for it.
17
+ */
18
+ /** Zone -> ISO code, unpacked on first use. */
19
+ var byZone = null;
20
+ function zones() {
21
+ if (byZone !== null) {
22
+ return byZone;
23
+ }
24
+ var out = {};
25
+ tzCountries.TZ_COUNTRIES.split(';').forEach(function (entry) {
26
+ var colon = entry.indexOf(':'); // no i18n
27
+ var code = entry.slice(0, colon);
28
+ entry.slice(colon + 1).split('|').forEach(function (group) {
29
+ // "Area/City,City", or bare names with no area ("Cuba,Egypt")
30
+ var slash = group.indexOf('/'); // no i18n
31
+ var area = slash === -1 ? '' : group.slice(0, slash + 1); // no i18n
32
+ group.slice(area.length).split(',').forEach(function (city) {
33
+ out[area + city] = code;
34
+ });
35
+ });
36
+ });
37
+ byZone = out;
38
+ return out;
39
+ }
40
+ /** The ISO code of a time zone's country, '' for one with none (UTC) or unknown. */
41
+ function timeZoneCountryCode(timeZone) {
42
+ var all = zones();
43
+ return Object.prototype.hasOwnProperty.call(all, timeZone) ? all[timeZone] : ''; // no i18n
44
+ }
45
+ /** IN -> India, in English; the code itself where the browser has no region names. */
46
+ function regionName(code) {
47
+ try {
48
+ var names = new Intl.DisplayNames(['en'], { type: 'region' }); // no i18n
49
+ return names.of(code) || code;
50
+ }
51
+ catch (err) {
52
+ return code;
53
+ }
54
+ }
55
+ /** The country: the user's, else the time zone's. '' when neither says. */
56
+ function deviceCountry() {
57
+ var named = String(state._ZAP._user_info.country || '').trim(); // no i18n
58
+ if (named !== '') {
59
+ return named;
60
+ }
61
+ var timeZone = ''; // no i18n
62
+ try {
63
+ timeZone = String(env.getTimeZone() || ''); // no i18n
64
+ }
65
+ catch (err) {
66
+ timeZone = ''; // no i18n
67
+ }
68
+ var code = timeZoneCountryCode(timeZone);
69
+ return code === '' ? '' : regionName(code); // no i18n
70
+ }
71
+
72
+ exports.deviceCountry = deviceCountry;
73
+ exports.timeZoneCountryCode = timeZoneCountryCode;
@@ -1,10 +1,10 @@
1
- /** The value set for a custom criteria key, or undefined when none is. */
2
- export declare function customConditionValue(key: string): string | undefined;
1
+ /** The values set for a condition, by its name; empty when none are. */
2
+ export declare function customConditionValues(conditionname: string): string[];
3
3
  /**
4
- * Sets what this device is for a custom criteria, or removes it with an empty value. Re-applies
5
- * the stored rules as soon as storage is ready, so the next read sees the new result without a
6
- * fetch.
4
+ * Sets what this device is for a custom condition, by its name, as comma separated values; an
5
+ * empty value removes it. Re-applies the stored rules once storage is ready, so the next read sees
6
+ * the new result without a fetch.
7
7
  */
8
- export declare function setCustomConditionValue(key: string, value: string | number | boolean | null | undefined): void;
9
- /** reset(): forgets the values set on this page; the stored ones go with the rest of storage. */
8
+ export declare function setCustomConditionValue(conditionname: string, value: string | number | boolean | null | undefined): void;
9
+ /** reset(): forgets the values set on this page. */
10
10
  export declare function clearCustomConditionValues(): void;