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