@zoho_apptics/apptics-js-sdk 1.0.2 → 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 (243) hide show
  1. package/CHANGELOG.md +193 -0
  2. package/LICENSE +13 -0
  3. package/README.md +846 -11
  4. package/dist/apptics.esm.min.js +3 -0
  5. package/dist/apptics.esm.min.js.map +1 -0
  6. package/dist/apptics.min.js +3 -0
  7. package/dist/apptics.min.js.map +1 -0
  8. package/dist/chunks/lifecycle-VYr43KcB.min.js +3 -0
  9. package/dist/chunks/lifecycle-VYr43KcB.min.js.map +1 -0
  10. package/dist/cjs/auto-init.d.ts +8 -0
  11. package/dist/cjs/auto-init.js +32 -0
  12. package/dist/cjs/core/attempts.d.ts +28 -0
  13. package/dist/cjs/core/attempts.js +113 -0
  14. package/dist/cjs/core/batch.d.ts +32 -0
  15. package/dist/cjs/core/batch.js +116 -0
  16. package/dist/cjs/core/callsite.d.ts +34 -0
  17. package/dist/cjs/core/callsite.js +115 -0
  18. package/dist/cjs/core/consent.d.ts +27 -0
  19. package/dist/cjs/core/consent.js +132 -0
  20. package/dist/cjs/core/constants.d.ts +237 -0
  21. package/dist/cjs/core/constants.js +315 -0
  22. package/dist/cjs/core/db.d.ts +48 -0
  23. package/dist/cjs/core/db.js +467 -0
  24. package/dist/cjs/core/debug.d.ts +22 -0
  25. package/dist/cjs/core/debug.js +88 -0
  26. package/dist/cjs/core/env.d.ts +28 -0
  27. package/dist/cjs/core/env.js +267 -0
  28. package/dist/cjs/core/framework-id.d.ts +1 -0
  29. package/dist/cjs/core/framework-id.js +9 -0
  30. package/dist/cjs/core/guard.d.ts +11 -0
  31. package/dist/cjs/core/guard.js +86 -0
  32. package/dist/cjs/core/hooks.d.ts +39 -0
  33. package/dist/cjs/core/hooks.js +22 -0
  34. package/dist/cjs/core/locks.d.ts +9 -0
  35. package/dist/cjs/core/locks.js +22 -0
  36. package/dist/cjs/core/logger.d.ts +15 -0
  37. package/dist/cjs/core/logger.js +50 -0
  38. package/dist/cjs/core/state.d.ts +70 -0
  39. package/dist/cjs/core/state.js +78 -0
  40. package/dist/cjs/core/storage.d.ts +61 -0
  41. package/dist/cjs/core/storage.js +327 -0
  42. package/dist/cjs/core/stylesheet.d.ts +8 -0
  43. package/dist/cjs/core/stylesheet.js +29 -0
  44. package/dist/cjs/core/transport.d.ts +85 -0
  45. package/dist/cjs/core/transport.js +456 -0
  46. package/dist/cjs/core/utils.d.ts +26 -0
  47. package/dist/cjs/core/utils.js +175 -0
  48. package/dist/cjs/core/validate.d.ts +18 -0
  49. package/dist/cjs/core/validate.js +233 -0
  50. package/dist/cjs/device/appversion.d.ts +15 -0
  51. package/dist/cjs/device/appversion.js +170 -0
  52. package/dist/cjs/device/index.d.ts +34 -0
  53. package/dist/cjs/device/index.js +472 -0
  54. package/dist/cjs/engagement/api-rules.d.ts +49 -0
  55. package/dist/cjs/engagement/api-rules.js +206 -0
  56. package/dist/cjs/engagement/auto-api.d.ts +7 -0
  57. package/dist/cjs/engagement/auto-api.js +200 -0
  58. package/dist/cjs/engagement/auto-screen.d.ts +18 -0
  59. package/dist/cjs/engagement/auto-screen.js +305 -0
  60. package/dist/cjs/engagement/defined-events.d.ts +23 -0
  61. package/dist/cjs/engagement/defined-events.js +58 -0
  62. package/dist/cjs/engagement/send.d.ts +6 -0
  63. package/dist/cjs/engagement/send.js +104 -0
  64. package/dist/cjs/engagement/session.d.ts +51 -0
  65. package/dist/cjs/engagement/session.js +463 -0
  66. package/dist/cjs/engagement/track.d.ts +56 -0
  67. package/dist/cjs/engagement/track.js +375 -0
  68. package/dist/cjs/errors/send.d.ts +6 -0
  69. package/dist/cjs/errors/send.js +73 -0
  70. package/dist/cjs/errors/track.d.ts +4 -0
  71. package/dist/cjs/errors/track.js +167 -0
  72. package/dist/cjs/feedback/controls.d.ts +37 -0
  73. package/dist/cjs/feedback/controls.js +316 -0
  74. package/dist/cjs/feedback/index.d.ts +9 -0
  75. package/dist/cjs/feedback/index.js +56 -0
  76. package/dist/cjs/feedback/logs.d.ts +45 -0
  77. package/dist/cjs/feedback/logs.js +241 -0
  78. package/dist/cjs/feedback/quartz.d.ts +18 -0
  79. package/dist/cjs/feedback/quartz.js +105 -0
  80. package/dist/cjs/feedback/send.d.ts +28 -0
  81. package/dist/cjs/feedback/send.js +203 -0
  82. package/dist/cjs/feedback/styles.d.ts +10 -0
  83. package/dist/cjs/feedback/styles.js +8 -0
  84. package/dist/cjs/feedback/template.d.ts +2 -0
  85. package/dist/cjs/feedback/template.js +162 -0
  86. package/dist/cjs/feedback/ui.d.ts +63 -0
  87. package/dist/cjs/feedback/ui.js +709 -0
  88. package/dist/cjs/heatmap/send.d.ts +13 -0
  89. package/dist/cjs/heatmap/send.js +82 -0
  90. package/dist/cjs/heatmap/track.d.ts +4 -0
  91. package/dist/cjs/heatmap/track.js +448 -0
  92. package/dist/cjs/index.d.ts +22 -0
  93. package/dist/cjs/index.js +162 -0
  94. package/dist/cjs/lifecycle.d.ts +21 -0
  95. package/dist/cjs/lifecycle.js +734 -0
  96. package/dist/cjs/package.json +3 -0
  97. package/dist/cjs/remoteconfig/country.d.ts +4 -0
  98. package/dist/cjs/remoteconfig/country.js +73 -0
  99. package/dist/cjs/remoteconfig/custom.d.ts +10 -0
  100. package/dist/cjs/remoteconfig/custom.js +58 -0
  101. package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
  102. package/dist/cjs/remoteconfig/evaluate.js +184 -0
  103. package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
  104. package/dist/cjs/remoteconfig/fetch.js +200 -0
  105. package/dist/cjs/remoteconfig/index.d.ts +15 -0
  106. package/dist/cjs/remoteconfig/index.js +116 -0
  107. package/dist/cjs/remoteconfig/store.d.ts +18 -0
  108. package/dist/cjs/remoteconfig/store.js +98 -0
  109. package/dist/cjs/remoteconfig/tz-countries.d.ts +8 -0
  110. package/dist/cjs/remoteconfig/tz-countries.js +13 -0
  111. package/dist/cjs/remotelog/gate.d.ts +2 -0
  112. package/dist/cjs/remotelog/gate.js +28 -0
  113. package/dist/cjs/remotelog/index.d.ts +8 -0
  114. package/dist/cjs/remotelog/logger.d.ts +22 -0
  115. package/dist/cjs/remotelog/logger.js +111 -0
  116. package/dist/cjs/remotelog/send.d.ts +13 -0
  117. package/dist/cjs/remotelog/send.js +153 -0
  118. package/dist/cjs/remotelogger/index.d.ts +12 -0
  119. package/dist/cjs/remotelogger/index.js +18 -0
  120. package/dist/cjs/scheduler.d.ts +8 -0
  121. package/dist/cjs/scheduler.js +78 -0
  122. package/dist/cjs/types.d.ts +385 -0
  123. package/dist/cjs/user/index.d.ts +33 -0
  124. package/dist/cjs/user/index.js +183 -0
  125. package/dist/esm/auto-init.d.ts +8 -0
  126. package/dist/esm/auto-init.js +29 -0
  127. package/dist/esm/core/attempts.d.ts +28 -0
  128. package/dist/esm/core/attempts.js +106 -0
  129. package/dist/esm/core/batch.d.ts +32 -0
  130. package/dist/esm/core/batch.js +109 -0
  131. package/dist/esm/core/callsite.d.ts +34 -0
  132. package/dist/esm/core/callsite.js +110 -0
  133. package/dist/esm/core/consent.d.ts +27 -0
  134. package/dist/esm/core/consent.js +122 -0
  135. package/dist/esm/core/constants.d.ts +237 -0
  136. package/dist/esm/core/constants.js +292 -0
  137. package/dist/esm/core/db.d.ts +48 -0
  138. package/dist/esm/core/db.js +453 -0
  139. package/dist/esm/core/debug.d.ts +22 -0
  140. package/dist/esm/core/debug.js +81 -0
  141. package/dist/esm/core/env.d.ts +28 -0
  142. package/dist/esm/core/env.js +251 -0
  143. package/dist/esm/core/framework-id.d.ts +1 -0
  144. package/dist/esm/core/framework-id.js +7 -0
  145. package/dist/esm/core/guard.d.ts +11 -0
  146. package/dist/esm/core/guard.js +83 -0
  147. package/dist/esm/core/hooks.d.ts +39 -0
  148. package/dist/esm/core/hooks.js +20 -0
  149. package/dist/esm/core/locks.d.ts +9 -0
  150. package/dist/esm/core/locks.js +20 -0
  151. package/dist/esm/core/logger.d.ts +15 -0
  152. package/dist/esm/core/logger.js +46 -0
  153. package/dist/esm/core/state.d.ts +70 -0
  154. package/dist/esm/core/state.js +73 -0
  155. package/dist/esm/core/storage.d.ts +61 -0
  156. package/dist/esm/core/storage.js +314 -0
  157. package/dist/esm/core/stylesheet.d.ts +8 -0
  158. package/dist/esm/core/stylesheet.js +27 -0
  159. package/dist/esm/core/transport.d.ts +85 -0
  160. package/dist/esm/core/transport.js +439 -0
  161. package/dist/esm/core/utils.d.ts +26 -0
  162. package/dist/esm/core/utils.js +161 -0
  163. package/dist/esm/core/validate.d.ts +18 -0
  164. package/dist/esm/core/validate.js +226 -0
  165. package/dist/esm/device/appversion.d.ts +15 -0
  166. package/dist/esm/device/appversion.js +164 -0
  167. package/dist/esm/device/index.d.ts +34 -0
  168. package/dist/esm/device/index.js +461 -0
  169. package/dist/esm/engagement/api-rules.d.ts +49 -0
  170. package/dist/esm/engagement/api-rules.js +199 -0
  171. package/dist/esm/engagement/auto-api.d.ts +7 -0
  172. package/dist/esm/engagement/auto-api.js +198 -0
  173. package/dist/esm/engagement/auto-screen.d.ts +18 -0
  174. package/dist/esm/engagement/auto-screen.js +301 -0
  175. package/dist/esm/engagement/defined-events.d.ts +23 -0
  176. package/dist/esm/engagement/defined-events.js +55 -0
  177. package/dist/esm/engagement/send.d.ts +6 -0
  178. package/dist/esm/engagement/send.js +101 -0
  179. package/dist/esm/engagement/session.d.ts +51 -0
  180. package/dist/esm/engagement/session.js +448 -0
  181. package/dist/esm/engagement/track.d.ts +56 -0
  182. package/dist/esm/engagement/track.js +365 -0
  183. package/dist/esm/errors/send.d.ts +6 -0
  184. package/dist/esm/errors/send.js +70 -0
  185. package/dist/esm/errors/track.d.ts +4 -0
  186. package/dist/esm/errors/track.js +163 -0
  187. package/dist/esm/feedback/controls.d.ts +37 -0
  188. package/dist/esm/feedback/controls.js +309 -0
  189. package/dist/esm/feedback/index.d.ts +9 -0
  190. package/dist/esm/feedback/index.js +51 -0
  191. package/dist/esm/feedback/logs.d.ts +45 -0
  192. package/dist/esm/feedback/logs.js +227 -0
  193. package/dist/esm/feedback/quartz.d.ts +18 -0
  194. package/dist/esm/feedback/quartz.js +99 -0
  195. package/dist/esm/feedback/send.d.ts +28 -0
  196. package/dist/esm/feedback/send.js +198 -0
  197. package/dist/esm/feedback/styles.d.ts +10 -0
  198. package/dist/esm/feedback/styles.js +4 -0
  199. package/dist/esm/feedback/template.d.ts +2 -0
  200. package/dist/esm/feedback/template.js +159 -0
  201. package/dist/esm/feedback/ui.d.ts +63 -0
  202. package/dist/esm/feedback/ui.js +703 -0
  203. package/dist/esm/heatmap/send.d.ts +13 -0
  204. package/dist/esm/heatmap/send.js +79 -0
  205. package/dist/esm/heatmap/track.d.ts +4 -0
  206. package/dist/esm/heatmap/track.js +445 -0
  207. package/dist/esm/index.d.ts +22 -0
  208. package/dist/esm/index.js +150 -0
  209. package/dist/esm/lifecycle.d.ts +21 -0
  210. package/dist/esm/lifecycle.js +729 -0
  211. package/dist/esm/package.json +3 -0
  212. package/dist/esm/remoteconfig/country.d.ts +4 -0
  213. package/dist/esm/remoteconfig/country.js +70 -0
  214. package/dist/esm/remoteconfig/custom.d.ts +10 -0
  215. package/dist/esm/remoteconfig/custom.js +54 -0
  216. package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
  217. package/dist/esm/remoteconfig/evaluate.js +179 -0
  218. package/dist/esm/remoteconfig/fetch.d.ts +24 -0
  219. package/dist/esm/remoteconfig/fetch.js +196 -0
  220. package/dist/esm/remoteconfig/index.d.ts +15 -0
  221. package/dist/esm/remoteconfig/index.js +111 -0
  222. package/dist/esm/remoteconfig/store.d.ts +18 -0
  223. package/dist/esm/remoteconfig/store.js +91 -0
  224. package/dist/esm/remoteconfig/tz-countries.d.ts +8 -0
  225. package/dist/esm/remoteconfig/tz-countries.js +11 -0
  226. package/dist/esm/remotelog/gate.d.ts +2 -0
  227. package/dist/esm/remotelog/gate.js +26 -0
  228. package/dist/esm/remotelog/index.d.ts +8 -0
  229. package/dist/esm/remotelog/logger.d.ts +22 -0
  230. package/dist/esm/remotelog/logger.js +108 -0
  231. package/dist/esm/remotelog/send.d.ts +13 -0
  232. package/dist/esm/remotelog/send.js +149 -0
  233. package/dist/esm/remotelogger/index.d.ts +12 -0
  234. package/dist/esm/remotelogger/index.js +13 -0
  235. package/dist/esm/scheduler.d.ts +8 -0
  236. package/dist/esm/scheduler.js +74 -0
  237. package/dist/esm/types.d.ts +385 -0
  238. package/dist/esm/user/index.d.ts +33 -0
  239. package/dist/esm/user/index.js +176 -0
  240. package/dist/feedback.esm.min.js +3 -0
  241. package/dist/feedback.esm.min.js.map +1 -0
  242. package/package.json +122 -7
  243. package/src/appticssdk.js +0 -1
package/README.md CHANGED
@@ -1,25 +1,860 @@
1
- # @zoho_apptics/apptics-js-sdk
1
+ # Apptics JavaScript SDK
2
2
 
3
- JavaScript SDK for Zoho Apptics Analytics
3
+ JavaScript/TypeScript library for [Zoho Apptics](https://www.zoho.com/apptics/) on the web: usage analytics, error tracking, API tracking, remote config, remote logging, in-app feedback and heatmaps.
4
4
 
5
- ## Installation
5
+ ### Prerequisites
6
+
7
+ - A browser app: Chrome 80+, Edge 80+, Firefox 74+ or Safari 14+.
8
+ - A Browser app registered in the Apptics web console (Quick start → Browser), which gives you its `base_url`, `auth_key` and `app_domain`. You also need your app's `appversion`.
9
+
10
+ The SDK is plain TypeScript with no framework dependency. It works with any bundler (Vite, webpack, Next.js, …) and from a `<script>` tag.
11
+
12
+ ## Install Apptics
6
13
 
7
14
  ```bash
8
- npm install @zoho_apptics/apptics-js-sdk
15
+ npm install @zoho_apptics/apptics-js-sdk@2.0.0-beta.1
16
+ ```
17
+
18
+ A plain `npm install @zoho_apptics/apptics-js-sdk` still installs 1.0.2, the current `latest`. Upgrading from 1.0.2? `Apptics.init()` takes different config now; see [Migrating from 1.0.2 to 2.0.0-beta.1](#migrating-from-102-to-200-beta1).
19
+
20
+ ## Initialize Apptics
21
+
22
+ Start Apptics once, as early as your app runs, with the values from the Apptics web console.
23
+
24
+ ### With a bundler
25
+
26
+ ```ts
27
+ import { Apptics, AppticsEvent } from '@zoho_apptics/apptics-js-sdk';
28
+
29
+ // Your web app's Apptics configuration, from the Apptics web console
30
+ const appticsConfig = {
31
+ base_url: 'YOUR_BASE_URL',
32
+ auth_key: 'YOUR_AUTH_KEY',
33
+ app_domain: 'YOUR_APP_DOMAIN',
34
+ appversion: 'YOUR_APPVERSION', // your app's version
35
+ };
36
+
37
+ // Initialize Apptics. Screens are tracked from the URL from here on, route changes included
38
+ Apptics.init(appticsConfig);
39
+
40
+ AppticsEvent.addEvent('signup', 'onboarding', { plan: 'pro' });
41
+ ```
42
+
43
+ The config object is **required**, with `base_url`, `auth_key`, `app_domain` and `appversion`: `Apptics.init()` won't start without them, and logs a warning naming whatever is missing.
44
+
45
+ `Apptics.init()` returns right away. The SDK finishes starting once its storage has loaded, usually within a few milliseconds. Calls you make before that are queued and run in order, so you don't have to wait. If you need to know when it's running, use `await Apptics.ready()`.
46
+
47
+ Every call belongs to an object, named after its feature, so you import only the object and code reads much as it does on Android: `Apptics.init()`, `AppticsEvent.addEvent()`, `AppticsError.trackError()`, `AppticsFeedback.feedbackFormOpen()`. See [The API at a glance](#the-api-at-a-glance). CommonJS works too (`const { Apptics } = require('@zoho_apptics/apptics-js-sdk')`). Importing the SDK is safe during server-side rendering: nothing runs until `Apptics.init()` is called in the browser.
48
+
49
+ ### With a `<script>` tag (CDN)
50
+
51
+ Load the browser build and call `Apptics.init()` after it. One `<script>` tag is all it takes: the feedback
52
+ form's styles are inside the build, so there is no stylesheet to add.
53
+
54
+ ```html
55
+ <script src="https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta.1/dist/apptics.min.js"></script>
56
+ <script>
57
+ Apptics.init({
58
+ base_url: 'YOUR_BASE_URL',
59
+ auth_key: 'YOUR_AUTH_KEY',
60
+ app_domain: 'YOUR_APP_DOMAIN',
61
+ appversion: 'YOUR_APPVERSION', // your app's version
62
+ });
63
+ // screens are tracked from the URL automatically; events are yours to send
64
+ AppticsEvent.addEvent('signup', 'onboarding', { plan: 'pro' });
65
+ </script>
66
+ ```
67
+
68
+ The build defines the same objects as globals (`Apptics`, `AppticsEvent`, `AppticsScreen`, `AppticsApi`, `AppticsError`, `AppticsUser`, `AppticsSettings`, `AppticsFeedback`, `AppticsLogger`, `AppticsRemoteConfig`, `AppticsHeatmap`), so a page's code reads as it would after an import. Every module is included (feedback, heatmaps, remote config, the remote logger), and everything is also on `window.apptics`. Define `window.appticsReady = () => { … }` to run code once it's ready.
69
+
70
+ `Apptics.init()` runs exactly once; a second call logs a warning and returns `false`. Calls that track
71
+ or send something are ignored until `Apptics.init()` has run, and the first such call logs a console
72
+ warning saying so.
73
+
74
+ **Pin the version** (`@2.0.0-beta.1`) rather than a tag or range such as `@beta`, so a release can't change what your pages load. See [Loading from a CDN](#loading-from-a-cdn) for module builds, unpkg and subresource integrity.
75
+
76
+ ### The API at a glance
77
+
78
+ Each feature's calls are on one object:
79
+
80
+ | Object | Calls | Entry point |
81
+ |---|---|---|
82
+ | `Apptics` | `init(config)`, `ready()`, `flush()`, `reset()`, `enableDebug()`, `disableDebug()` | main |
83
+ | `AppticsEvent` | `addEvent(eventName, groupName?, properties?)`, `addEvent(DefinedEvents.X, properties?)`, `DefinedEvents` | main |
84
+ | `AppticsScreen` | `inScreen(screenName, properties?)` → id, `outScreen(id)` | main |
85
+ | `AppticsError` | `trackError(error, properties?)`, `trackGlobalErrorHandler(enabled?)` | main |
86
+ | `AppticsUser` | `setUser(userid, groupid?, properties?)`, `getUser()`, `UserBuilder` | main |
87
+ | `AppticsSettings` | `setTrackingStatus(status)`, `getTrackingStatus()`, the `AP_TRACKING_STATUS` constants | main |
88
+ | `AppticsApi` | `startTrackApi(url, method?)` → id, `endTrackApi(id, responseCode, responseMessage?)`, `configure(config)`, `enableApiTracking(enabled?)`, `ApiTrackingBuilder` | main |
89
+ | `AppticsHeatmap` | `trackHeatmap(on?)` | main |
90
+ | `AppticsLogger` | `verbose(message, properties?)`, `debug(message, properties?)`, `info(message, properties?)`, `warn(message, properties?)`, `error(message, properties?)` | `/remote-logger` |
91
+ | `AppticsFeedback` | `feedbackFormOpen(options?)`, `feedbackFormClose()`, `sendFeedback(options?)`, `addFeedbackLog(message)`, `addDiagnosticsInfo(heading, key, value)`, `setFeedbackTheme(theme)`, the `AP_FEEDBACK_TYPE` constants | `/feedback` |
92
+ | `AppticsRemoteConfig` | `fetchValue(name, callback?)`, `fetchValues(names?, callback?)`, `setCustomConditionValue(conditionname, value)` | `/remote-config` |
93
+
94
+ The package exports only these objects: `DefinedEvents` is `AppticsEvent.DefinedEvents`, and the builders are `AppticsApi.ApiTrackingBuilder` and `AppticsUser.UserBuilder`. Android's `AppticsScreenTracker` and `AppticsApiTracker` are `AppticsScreen` and `AppticsApi` here.
95
+
96
+ ### Frameworks
97
+
98
+ React, Vue, Angular, Svelte, Ember, Solid, jQuery and hand-written JS all start the same way, and every one of them needs only that: the SDK follows the route itself ([automatic screen tracking](#automatic-screen-tracking)). To name screens from your router instead, call `AppticsScreen.inScreen()` from the hook in the last column: a call made as the route changes replaces the automatic view, and `track_screen: false` turns the automatic one off altogether, which is what the recipes in `examples/frameworks` do.
99
+
100
+ | Framework | Start | Screen on navigation, with `track_screen: false` |
101
+ |---|---|---|
102
+ | React, Remix, Gatsby | `useEffect(() => { Apptics.init(config); }, [])` | `useLocation()` in an effect |
103
+ | Vue 3 | a plugin: `app.use(apptics, { router })` | `router.afterEach(to => AppticsScreen.inScreen(to.path))` |
104
+ | Angular | an injectable service | `router.events` filtered to `NavigationEnd` |
105
+ | Svelte, SvelteKit | `src/hooks.client.ts`, or `onMount` | `afterNavigate` |
106
+ | Ember | an instance initializer | `router.on('routeDidChange', …)` |
107
+ | Next.js | `'use client'` component in the layout | `usePathname()` in an effect |
108
+ | Nuxt 3 | `plugins/apptics.client.ts` | `useRouter().afterEach(…)` |
109
+ | Plain JS, jQuery | the `<script>` tag itself | `AppticsScreen.inScreen()` where you change view |
110
+
111
+ **Server-side rendering** (Next, Nuxt, SvelteKit, Remix, Angular Universal): importing the SDK on the server is safe (`Apptics.init()` returns `false` instead of throwing), but it tracks nothing there. Start it from a browser-only hook, as the recipes do. There is no Node/server-side tracking: `engines.node` in `package.json` is the floor for building the package and running its CLI.
112
+
113
+ ## Error Tracking
114
+
115
+ Uncaught errors and unhandled promise rejections are tracked automatically, as JS errors in the console.
116
+
117
+ Report an error your code caught:
118
+
119
+ ```ts
120
+ import { AppticsError } from '@zoho_apptics/apptics-js-sdk';
121
+
122
+ try {
123
+ doSomething();
124
+ } catch (error) {
125
+ AppticsError.trackError({ type: 'error', error });
126
+ }
127
+ ```
128
+
129
+ `AppticsError.trackError` also accepts `error` and `unhandledrejection` events as they are, and properties as a second argument. Turn the automatic tracking off with `AppticsError.trackGlobalErrorHandler(false)`.
130
+
131
+ React, Vue, Angular, SvelteKit and Ember catch render errors themselves, so those never reach `window.onerror`. Report them from the framework's own error hook:
132
+
133
+ | Framework | Error hook |
134
+ |---|---|
135
+ | React | an error boundary: `componentDidCatch(error) { AppticsError.trackError({ type: 'error', error }); }` |
136
+ | Vue 3, Nuxt 3 | `app.config.errorHandler = (error) => AppticsError.trackError({ type: 'error', error })` (Nuxt: `nuxtApp.hook('vue:error', …)`) |
137
+ | Angular | an `ErrorHandler` whose `handleError(error)` calls `AppticsError.trackError({ type: 'error', error })`, provided as `{ provide: ErrorHandler, useClass: … }` |
138
+ | SvelteKit | `handleError({ error })` in `src/hooks.client.ts` |
139
+ | Ember | `Ember.onerror = (error) => AppticsError.trackError({ type: 'error', error })` |
140
+
141
+ ## Event Tracking
142
+
143
+ Track an event, with an optional group and properties:
144
+
145
+ ```ts
146
+ import { AppticsEvent } from '@zoho_apptics/apptics-js-sdk';
147
+
148
+ AppticsEvent.addEvent('eventName', 'eventGroupName', { propKey: 'propValue' });
149
+ ```
150
+
151
+ Event and group names start with a letter, followed by letters, digits or `_`, at most 100
152
+ characters, and may not start with `ap_` or `AP_`, which is reserved for defined events. As on
153
+ Android, an event with an invalid name or group is not logged, and the console says why. A group
154
+ left out is `default`.
155
+
156
+ ### Defined events
157
+
158
+ Apptics' ready-made events, as Android's `DefinedEvents`, each with its own `AP_` group. They are
159
+ not tracked automatically; log the ones that apply:
160
+
161
+ ```ts
162
+ import { AppticsEvent } from '@zoho_apptics/apptics-js-sdk';
163
+
164
+ const { DefinedEvents } = AppticsEvent;
165
+
166
+ AppticsEvent.addEvent(DefinedEvents.AP_USER_LOGIN);
167
+ AppticsEvent.addEvent(DefinedEvents.AP_SEARCH, { results: 12 });
168
+ ```
169
+
170
+ | Group | Events |
171
+ |---|---|
172
+ | `AP_APP_LIFE_CYCLE` | `AP_APP_INSTALL`, `AP_APP_UNINSTALL`, `AP_APP_UPDATE`, `AP_APP_CRACKED`, `AP_APP_OPEN`, `AP_APP_CLEAR_DATA`, `AP_APP_FOREGROUND`, `AP_APP_BACKGROUND`, `AP_APP_OUT_OF_MEMORY`, `AP_APP_FIRST_OPEN`, `AP_APP_LAUNCHING`, `AP_APP_RESIGN_ACTIVE`, `AP_APP_WILL_CONNECT` |
173
+ | `AP_USER_LIFE_CYCLE` | `AP_USER_SIGNUP`, `AP_USER_LOGIN`, `AP_USER_LOGOUT` |
174
+ | `AP_APPLICATION` | `AP_DEEP_LINK_OPEN`, `AP_DEEP_LINK_UPDATE`, `AP_DEEP_LINK_FIRST_OPEN`, `AP_IN_APP_PURCHASE`, `AP_NOTIFICATION_RECEIVE`, `AP_NOTIFICATION_OPEN`, `AP_NOTIFICATION_DISMISS`, `AP_NOTIFICATION_FOREGROUND`, `AP_SEARCH`, `AP_BATTERY_LOW`, `AP_BATTERY_FULL`, `AP_LOW_POWER_MODE_ON`, `AP_LOW_POWER_MODE_OFF` |
175
+ | `AP_OTHERS` | `AP_NETWORK_REACHABILITY_CHANGE`, `AP_NETWORK_BANDWIDTH_CHANGE`, `AP_SWITCH_THEME_LIGHT`, `AP_SWITCH_THEME_DARK`, `AP_SWITCH_THEME_CUSTOM`, `AP_SWITCH_ORIENTATION_LANDSCAPE`, `AP_SWITCH_ORIENTATION_PORTRAIT` |
176
+ | `AP_OS` | `AP_OS_UPDATE`, `AP_OS_UNSUPPORTED` |
177
+
178
+ With a `<script>` tag it is `AppticsEvent.DefinedEvents` too.
179
+
180
+ Clicks can also be tracked from markup, without code:
181
+
182
+ ```html
183
+ <section apgroupaction="checkout">
184
+ <button apeventaction="buy_clicked" apeventprops='{"plan":"pro"}'>Buy</button>
185
+ </section>
186
+ ```
187
+
188
+ Clicking the button, or anything inside it, tracks `buy_clicked` in group `checkout`. The
189
+ `data-` spellings work too (`data-apeventaction`, `data-apgroupaction`, `data-apeventprops`), which
190
+ React and TypeScript accept as they are.
191
+
192
+ Properties: keys start with a letter, followed by up to 49 letters, digits or `_`, and may not
193
+ start with `ap_`. Values are numbers, booleans or strings of up to 250 characters; anything else
194
+ (an object, an array) is sent as its JSON text. As on Android, a property that breaks these rules
195
+ is dropped and the event is still logged with the rest. At most 24 properties and 7,666
196
+ characters in all; past that the later ones are dropped. The same rules apply to screen and error
197
+ properties.
198
+
199
+ ## Screen Tracking
200
+
201
+ Screens are tracked automatically, from the URL (see [below](#automatic-screen-tracking)). To
202
+ record one yourself, with your own name or with properties, call `AppticsScreen.inScreen`, as on Android:
203
+
204
+ ```ts
205
+ import { AppticsScreen } from '@zoho_apptics/apptics-js-sdk';
206
+
207
+ AppticsScreen.inScreen('checkout');
208
+ ```
209
+
210
+ Each screen view records how long it lasted. It ends when the next screen starts, when the page
211
+ is hidden (coming back starts a new view of the same screen), or when its session ends.
212
+ `AppticsScreen.inScreen` returns the view's id; hand it to `AppticsScreen.outScreen` to end the view yourself (a dialog
213
+ that closes without the page changing, say), as Android's `AppticsScreenTracker.outScreen` does:
214
+
215
+ ```ts
216
+ import { AppticsScreen } from '@zoho_apptics/apptics-js-sdk';
217
+
218
+ const id = AppticsScreen.inScreen('settings'); // when the screen is shown
219
+ AppticsScreen.outScreen(id); // when it is dismissed
220
+ ```
221
+
222
+ `AppticsScreen.inScreen` returns `''` for a name that isn't a valid screen name.
223
+
224
+ Screen names: up to 99 characters of letters, digits, spaces and `. - _ & / : *`. Events, errors,
225
+ feedback and heatmap records are attributed to the current screen: the one `AppticsScreen.inScreen()` last
226
+ named, or else the one automatic tracking named from the URL. A heatmap record always has a
227
+ screen view behind it: with automatic tracking off and no `AppticsScreen.inScreen()` call, the first record
228
+ on a page records a view of the page, named the same way from its path, and a new one when the
229
+ path changes.
230
+
231
+ An `AppticsScreen.inScreen()` call made during a navigation, from a router hook for example,
232
+ replaces the automatic view of it, so the navigation counts once, under your name. The automatic view waits
233
+ 300 ms for such a call before it is recorded; a call after that is a screen view of its own.
234
+
235
+ ### Automatic screen tracking
236
+
237
+ On by default: the SDK records screen views from the URL, with no `AppticsScreen.inScreen()` calls. It records the page the SDK starts on, then every navigation: `history.pushState` / `replaceState`
238
+ (React Router, Vue Router, Angular, Next.js and other single-page routers), back and forward, and
239
+ `#/` hash routes. The screen is named by its path, without the query string, so `/search?q=shoes`
240
+ is `/search` and `/#/orders` is `/orders`. Characters a screen name can't hold become `_`, and a
241
+ name stops at 99 characters. A plain `#anchor` and a navigation that lands on the same path are
242
+ not new views.
243
+
244
+ Paths with ids in them make every record its own screen. `screen_routes` names them instead. Each
245
+ entry is one of three kinds, tried in order; the first that matches names the screen, and a path
246
+ none matches keeps its own name:
247
+
248
+ | Entry | Matches | Recorded as |
249
+ |---|---|---|
250
+ | `'/users/{id}/orders'` | a route pattern: `{id}`, `:id` and `*` each match one path segment | the pattern, each variable segment as `*` |
251
+ | `{ equals: '/', name: 'Home' }` | that path exactly (a trailing slash does not matter) | `name` |
252
+ | `{ regex: /^\/orders\/\d+$/, name: 'Order detail' }` | any path the regular expression matches (a `RegExp` or its source as a string) | `name` |
253
+
254
+ ```ts
255
+ Apptics.init({
256
+ ...config,
257
+ screen_routes: [
258
+ { equals: '/', name: 'Home' },
259
+ { regex: /^\/orders\/\d+$/, name: 'Order detail' },
260
+ '/users/{id}/{id}/orders',
261
+ '/products/:sku',
262
+ ],
263
+ });
264
+ ```
265
+
266
+ | Opened | Recorded as |
267
+ |---|---|
268
+ | `/` | `Home` |
269
+ | `/orders/123?tab=items` | `Order detail` (paths are matched without the query string) |
270
+ | `/users/12/34/orders` | `/users/*/*/orders` |
271
+ | `/products/sku-889` | `/products/*` |
272
+ | `/users/12/34/settings` | `/users/12/34/settings` (no entry matches, so the name is kept) |
273
+
274
+ An entry that can't be used (no `name`, a regex that does not compile) is left out, and
275
+ `Apptics.init()` says which in the console.
276
+
277
+ **An app that names its screens from its router** keeps doing so with `AppticsScreen.inScreen()`: a call
278
+ within 300 ms of the navigation replaces the automatic view (see
279
+ [Screen Tracking](#screen-tracking)). To track only the screens you name yourself (screens that
280
+ aren't routes, or hooks that run later than that), turn automatic tracking off:
281
+
282
+ ```ts
283
+ Apptics.init({ ...config, track_screen: false });
284
+ ```
285
+
286
+ `Apptics.reset()` takes the history wrappers off; the `Apptics.init()` it runs puts them back.
287
+
288
+ ## Track User
289
+
290
+ Register the signed-in user:
291
+
292
+ ```ts
293
+ import { AppticsUser } from '@zoho_apptics/apptics-js-sdk';
294
+
295
+ AppticsUser.setUser('user-id', 'customer-group-id'); // the group is optional
296
+ ```
297
+
298
+ `AppticsUser.setUser` switches tracking to "with PII" (see [Tracking settings](#tracking-settings)). `AppticsUser.getUser()` returns the active user.
299
+
300
+ ### Track User with properties
301
+
302
+ ```ts
303
+ import { AppticsUser } from '@zoho_apptics/apptics-js-sdk';
304
+
305
+ const properties = new AppticsUser.UserBuilder()
306
+ .setFirstName('Ana')
307
+ .setEmailAddress('ana@zylker.com')
308
+ .setPlanType('pro')
309
+ .setCustomProperties({ team: 'design' })
310
+ .build();
311
+
312
+ AppticsUser.setUser('user-id', 'customer-group-id', properties);
313
+ ```
314
+
315
+ The builder also has `setLastName`, `setCompanyName`, `setContactNumber`, `setCountry`, `setRegion`, `setCity`, `setGeoLocation`, `setGender`, `setTimezone`, `setLanguage`, `setEngagementScore` and `setDateOfBirth`, and `addStringProperty(key, value)`,
316
+ `addNumberProperty` and `addBooleanProperty` for one custom property at a time. `build()` validates the properties against what the server accepts.
317
+
318
+ ## Tracking settings
319
+
320
+ Set what the SDK may track for this visitor:
321
+
322
+ ```ts
323
+ import { AppticsSettings } from '@zoho_apptics/apptics-js-sdk';
324
+
325
+ AppticsSettings.setTrackingStatus(AppticsSettings.AP_TRACKING_STATUS.USAGE_TRACKING_WITHOUT_PII);
326
+ ```
327
+
328
+ | `AppticsSettings.AP_TRACKING_STATUS.` | Value | Meaning |
329
+ |---|---|---|
330
+ | `USAGE_TRACKING_WITH_PII` | 1 | Track, with the user's identity |
331
+ | `USAGE_TRACKING_WITHOUT_PII` | 4 | Track anonymously (the default) |
332
+ | `NO_TRACKING` | -1 | Track nothing |
333
+
334
+ `AppticsSettings.getTrackingStatus()` returns the current one.
335
+
336
+ ### Privacy
337
+
338
+ - The browser's Do Not Track setting is honoured unless `ignore_do_not_track: true` is passed to `Apptics.init()`.
339
+ - Nothing is tracked for known crawlers.
340
+ - Heatmaps name an element from an attribute the author chose (`data-ap-label`, `apeventaction` or `data-apeventaction`, `aria-label`, `name`, `id`, `title`); its visible text is used only with `USAGE_TRACKING_WITH_PII`.
341
+
342
+ ## API Tracking
343
+
344
+ Record the API calls your app makes, for the console's API page. Either let the SDK wrap
345
+ `fetch` and `XMLHttpRequest`:
346
+
347
+ ```ts
348
+ Apptics.init({ ...config, track_api: true }); // record every fetch and XHR call
349
+ ```
350
+
351
+ `AppticsApi.enableApiTracking(enabled)` switches it later. Which hosts and endpoints are recorded is set with
352
+ [`AppticsApi.configure`](#configuring-api-tracking).
353
+
354
+ or record calls yourself with `AppticsApi.startTrackApi` / `endTrackApi`, as on Android. The
355
+ SDK times the call between the two:
356
+
357
+ ```ts
358
+ import { AppticsApi } from '@zoho_apptics/apptics-js-sdk';
359
+
360
+ const id = AppticsApi.startTrackApi('https://api.example.com/orders/42', 'GET');
361
+ const response = await fetch('https://api.example.com/orders/42');
362
+ AppticsApi.endTrackApi(id, response.status, response.statusText);
363
+ ```
364
+
365
+ Between them, automatic tracking and `AppticsApi.startTrackApi` / `endTrackApi` are the
366
+ two ways a call is recorded.
367
+
368
+ ### Configuring API tracking
369
+
370
+ Build the rules with `AppticsApi.ApiTrackingBuilder` (as `AppticsUser.UserBuilder` builds user
371
+ properties) and apply them once, before the requests they should cover. It works before
372
+ `Apptics.init()`:
373
+
374
+ ```ts
375
+ import { AppticsApi } from '@zoho_apptics/apptics-js-sdk';
376
+
377
+ AppticsApi.configure(new AppticsApi.ApiTrackingBuilder()
378
+ .allowRegex(/^https:\/\/api\.yourapp\.(com|in|ae)\//) // only your API hosts
379
+ .allowEquals('https://auth.yourapp.com/token') // and this one endpoint
380
+ .ignoreEquals('https://api.yourapp.com/health') // never this one
381
+ .ignoreRegex(/\/internal\//) // nor anything under /internal/
382
+ .build());
383
+ ```
384
+
385
+ Four rules, two kinds by two: an exact URL (`Equals`) or a regular expression (`Regex`), to allow
386
+ or to ignore. Each is tried on the call's URL as `scheme://host[:port]/path`, without its query
387
+ string or fragment, so one rule can name a host, a path or a single endpoint.
388
+
389
+ | Builder call | Effect |
390
+ |---|---|
391
+ | `allowEquals(...urls)` | Record these URLs exactly, as `'https://api.yourapp.com/orders'` |
392
+ | `allowRegex(...patterns)` | Record the URLs these regular expressions match, as `/^https:\/\/api\.yourapp\.com\//` |
393
+ | `ignoreEquals(...urls)` | Never record these URLs exactly |
394
+ | `ignoreRegex(...patterns)` | Never record the URLs these regular expressions match |
395
+
396
+ - **Ignore always wins.** Ignore rules are tried first; then, when there are any allow rules, a call
397
+ must match one of them. With no rules at all, every call is recorded.
398
+ - **Equals** compares whole URLs: the scheme and host are not case-sensitive, a trailing slash and
399
+ the default port do not matter, a port that is not the default does. It takes full URLs only.
400
+ - **Regex** takes a `RegExp` or its source as a string, and works with any flags.
401
+ - A URL that is not a full http(s) URL, or a regex that does not compile, is left out and the
402
+ console says why. A second `AppticsApi.configure()` replaces the first.
403
+
404
+ The rules apply to every call, the ones `track_api` catches and the ones you report yourself.
405
+
406
+ **Path normalization**, always on, so calls group by endpoint rather than by id:
407
+
408
+ | Called | Recorded as |
409
+ |---|---|
410
+ | `GET /users/42/orders` | `/users/*/orders` (numeric id) |
411
+ | `GET /users/0b9e1f2a-…-0c1d2e3f4a5b/profile` | `/users/*/profile` (UUID) |
412
+ | `DELETE /sessions/eyJhbGciOi….….` | `/sessions/*` (JWT) |
413
+ | `GET /products/search?q=shoes` | `/products/search` (the query string is always removed) |
414
+
415
+ The SDK's own requests are never recorded, and neither are calls to `localhost` or a bare IP
416
+ address: the server only accepts hostnames with a dot and a letters-only suffix, so the SDK drops
417
+ those calls and says so in the console. `AppticsApi.enableApiTracking(false)` removes the wrappers again.
418
+ With a `<script>` tag, `AppticsApi` is a global as it is here, `AppticsApi.ApiTrackingBuilder`
419
+ included.
420
+
421
+ ## Remote Config
422
+
423
+ Read parameters set in the console. Its own entry point, so apps that don't use it don't ship it:
424
+
425
+ ```ts
426
+ import { AppticsRemoteConfig } from '@zoho_apptics/apptics-js-sdk/remote-config';
427
+
428
+ const banner = await AppticsRemoteConfig.fetchValue('bannerText'); // null if there is none
429
+ const some = await AppticsRemoteConfig.fetchValues(['primaryColor', 'maxItems']);
430
+ // → { primaryColor: '#0b6e4f', maxItems: '10' }
431
+ const all = await AppticsRemoteConfig.fetchValues(); // every parameter
432
+
433
+ // for a custom condition: its name, then this device's values, comma separated
434
+ AppticsRemoteConfig.setCustomConditionValue('is_custom', 'custom_color');
435
+ ```
436
+
437
+ A name the console doesn't have is `null`.
438
+
439
+ **How a value is chosen.** The SDK goes through the conditions in the order the console lists them
440
+ (`all_condition`), skipping those that don't apply to the parameter. The first one that matches
441
+ this device gives the parameter's value for it; when none matches, the parameter's default value.
442
+ A condition matches when all of its criteria match this device.
443
+
444
+ Values are text, as the console stores them: `Number(values.maxItems)`,
445
+ `values.showBanner === 'true'` or `JSON.parse(...)` turn them into what the app needs.
446
+
447
+ The SDK fetches once on its own when it starts (or when this module is imported after it has),
448
+ so values are this session's without a call of yours.
449
+
450
+ **Fetching.** `fetchValue` and `fetchValues` reuse a fetch from the last five minutes
451
+ rather than asking the server again, so reading a value on every render costs no requests; once
452
+ that fetch is older, the next call asks the server. Each also takes a callback, as on Android,
453
+ and returns a Promise either way.
454
+
455
+ **When a fetch fails** (offline, or a server error), they answer with the values fetched before;
456
+ with none fetched yet, `null` (and `fetchValues()` answers with no parameters).
457
+
458
+ **Custom conditions.** `setCustomConditionValue(conditionname, value)` says what this device is
459
+ for the custom condition with that name in the console, with several values comma separated
460
+ (`'gold,platinum'`). The condition matches when one of them is one of the values it lists. The
461
+ stored rules are applied again at once, without a fetch; an empty value removes it. Values are for
462
+ this page, so set them as the app starts; it works before `Apptics.init()`, and `Apptics.reset()`
463
+ clears them.
464
+
465
+ Conditions on the user, such as the customer group, are re-applied on their own once
466
+ `AppticsUser.setUser` has registered the user, so the next read answers for them.
467
+
468
+ | Call | Notes |
469
+ |---|---|
470
+ | `fetchValue(name, callback?)` | One parameter for this session; `null` when the console has none. |
471
+ | `fetchValues(names?, callback?)` | The parameters named in the list, `null` for a missing one; with no names, every parameter. `fetchValues(callback)` works too. |
472
+ | `setCustomConditionValue(conditionname, value)` | This device's values for a custom condition, comma separated (`'gold,platinum'`); an empty value removes them. |
473
+
474
+ With a `<script>` tag, `AppticsRemoteConfig` is a global as it is here.
475
+
476
+ ## Remote Logger
477
+
478
+ Send your app's log lines to the console's Remote Logger page with `AppticsLogger`, from its own
479
+ entry point (`@zoho_apptics/apptics-js-sdk/remote-logger`) so apps that don't log don't ship it.
480
+ Nothing is logged on its own: only the lines your app writes, at five levels, as Android's
481
+ `v` / `d` / `i` / `w` / `e` with their names in full. A line is recorded as it is written, with no
482
+ switch to turn on first:
483
+
484
+ ```ts
485
+ import { AppticsLogger } from '@zoho_apptics/apptics-js-sdk/remote-logger';
486
+
487
+ AppticsLogger.verbose('Cache warmed');
488
+ AppticsLogger.debug('Cart loaded', { items: 3 });
489
+ AppticsLogger.info('Payment started', { orderId: 42, method: 'card' });
490
+ AppticsLogger.warn('Retrying the payment', { attempt: 2 });
491
+ AppticsLogger.error('Payment failed', err); // an Error as properties: sent with its stack
492
+ ```
493
+
494
+ | Call | Level on the Remote Logger page |
495
+ |---|---|
496
+ | `AppticsLogger.verbose(message, properties?)` | 1, Verbose |
497
+ | `AppticsLogger.debug(message, properties?)` | 2, Debug |
498
+ | `AppticsLogger.info(message, properties?)` | 3, Info |
499
+ | `AppticsLogger.warn(message, properties?)` | 4, Warning |
500
+ | `AppticsLogger.error(message, properties?)` | 5, Error |
501
+
502
+ `properties` is appended to the message: an object as JSON (`Payment started {"orderId":42,"method":"card"}`),
503
+ an `Error` as its stack. Each line records the time it was logged and the function, file and line
504
+ that logged it, even when logged before start-up finishes. Lines are stored before they are sent
505
+ and go out in batches of up to 50: a line survives a failed send and a reload, and is removed once
506
+ the server accepts it. At most 1 MB of lines waits at once, oldest dropped first, as on Android,
507
+ and a line is trimmed to 10,000 characters. Nothing is recorded while the server has the remote
508
+ logger switched off for the app, or while tracking is off.
509
+
510
+ Unlike Android, which uploads at the next cold start or on a schedule, lines go out about a
511
+ second after they're logged: a browser tab can close at any moment, and waiting would lose them.
512
+
513
+ ## In-App Feedback
514
+
515
+ The feedback form is its own entry point, so apps that don't use it don't download it. Its
516
+ `AppticsFeedback` has every feedback call: the form, sending without it, and the log and
517
+ diagnostics calls. With a `<script>` tag it is the `AppticsFeedback` global.
518
+
519
+ ### Show feedback screen
520
+
521
+ ```ts
522
+ import { AppticsFeedback } from '@zoho_apptics/apptics-js-sdk/feedback';
523
+
524
+ AppticsFeedback.feedbackFormOpen(); // show the form
525
+ AppticsFeedback.feedbackFormClose(); // close it from your own code, when needed
526
+ ```
527
+
528
+ `feedbackFormOpen` takes one options object, which fills the form in for the visitor. Every key is
529
+ optional:
530
+
531
+ ```ts
532
+ AppticsFeedback.feedbackFormOpen({
533
+ type: AppticsFeedback.AP_FEEDBACK_TYPE.BUG, // the tab it opens on: BUG, FEEDBACK, HELP or REPORT
534
+ message: 'The checkout button does nothing', // text already in the description
535
+ includeLog: true, // start with the log toggle on
536
+ includeDiagnostics: true, // start with the diagnostics toggle on
537
+ attachments: [screenshotFile], // files already attached (a FileList or File[])
538
+ logFiles: [appLogFile], // log files already attached
539
+ isAnonymous: false, // true starts the user picker on Anonymous
540
+ });
541
+ ```
542
+
543
+ Passing values one after another, as `feedbackFormOpen(type, message)`, opens the form with its
544
+ defaults and a console warning.
545
+
546
+ The form brings its own styles, with the npm package and the CDN builds alike: the first
547
+ `feedbackFormOpen()` adds them to the page, so there is no stylesheet to import or link.
548
+
549
+ **Theme:** the form is `light`, `dark`, or `auto` (the default), which follows the visitor's system setting:
550
+
551
+ ```ts
552
+ Apptics.init({ ...config, feedback_theme: 'dark' });
553
+ ```
554
+
555
+ `auto` follows the operating system, not the page. A page with its own light and dark modes tells
556
+ the form which one it shows, whenever that changes; a form that is open is restyled at once:
557
+
558
+ ```ts
559
+ AppticsFeedback.setFeedbackTheme(isDark ? 'dark' : 'light');
560
+ ```
561
+
562
+ The colors are CSS variables on the form, so a page can restyle it without any SDK option. Override them
563
+ where the form is shown, for example to set a brand color:
564
+
565
+ ```css
566
+ .zasdk-modal { --zasdk-primary-color: #0b5d8f; }
567
+ ```
568
+
569
+ The main ones are `--zasdk-background-color`, `--zasdk-text-color`, `--zasdk-border-color`,
570
+ `--zasdk-input-background`, `--zasdk-primary-color` (the Send button), `--zasdk-button-text-color`,
571
+ `--zasdk-link-color` and `--zasdk-shadow`.
572
+
573
+ The form is a modal dialog for keyboards and screen readers: it takes focus when it opens, Escape or
574
+ the close control (with Enter or Space) closes it, and it honours `prefers-reduced-motion`.
575
+
576
+ The form offers Feedback (selected by default), Bug, Help and Report, with up to 5 attachments (10 MB total).
577
+
578
+ ### Send Feedback/Bug Report programmatically
579
+
580
+ ```ts
581
+ import { AppticsFeedback } from '@zoho_apptics/apptics-js-sdk/feedback';
582
+
583
+ try {
584
+ await AppticsFeedback.sendFeedback({
585
+ type: AppticsFeedback.AP_FEEDBACK_TYPE.BUG, // BUG (0), FEEDBACK (1, the default), HELP (2), REPORT (3)
586
+ message: 'Checkout is broken',
587
+ attachments: [screenshotFile], // at most 5, 10 MB in all
588
+ includeLog: true, // the addFeedbackLog() lines
589
+ includeDiagnostics: true, // the addDiagnosticsInfo() entries and the device details
590
+ isAnonymous: false, // true sends it from nobody
591
+ });
592
+ // sent
593
+ } catch (err) {
594
+ // not sent: the SDK hasn't started (no Apptics.init()), the browser is offline, or the server refused it
595
+ }
596
+ ```
597
+
598
+ `AppticsFeedback.sendFeedback(options?)` takes `type`, `message`, `attachments`, `logFiles`,
599
+ `includeDiagnostics`, `includeLog` and `isAnonymous`, all optional. It is sent from the user
600
+ `AppticsUser.setUser` set, or from nobody with `isAnonymous: true`. It returns a Promise that resolves once the
601
+ feedback is sent and rejects when it wasn't. Passing values one after another, as
602
+ `sendFeedback(type, message)`, rejects rather than send an empty report.
603
+
604
+ Feedback is sent even when tracking is off (`NO_TRACKING`): the visitor is choosing to send it.
605
+ Only the feedback goes; nothing else is tracked.
606
+
607
+ ### Attach logs and diagnostics with Feedback
608
+
609
+ ```ts
610
+ import { AppticsFeedback } from '@zoho_apptics/apptics-js-sdk/feedback';
611
+
612
+ AppticsFeedback.addFeedbackLog('token refresh failed, retrying'); // one timestamped line
613
+ AppticsFeedback.addDiagnosticsInfo('Auth', 'scheme', 'oauth'); // grouped key/value
614
+ AppticsFeedback.addDiagnosticsInfo('Auth', 'expiry', 3600);
615
+ ```
616
+
617
+ `addDiagnosticsInfo` needs all three: a heading, a key and a value (`0` and `false` count). An
618
+ entry without one is not recorded, and the console says why.
619
+
620
+ Both calls are on the `/feedback` entry's `AppticsFeedback`, which is where every feedback call
621
+ lives; the main entry point has no `AppticsFeedback`. Nothing is sent as it happens: entries wait in storage until feedback is sent,
622
+ and then travel with it as two text files.
623
+
624
+ | File | Holds | Sent when |
625
+ |---|---|---|
626
+ | `feedbacklog.txt` | `addFeedbackLog` lines, in order, each prefixed with its ISO timestamp | There are lines, unless `includeLog` is `false` |
627
+ | `dyninfo.txt` | `addDiagnosticsInfo` entries as `[heading]` blocks, then a `[Device Info]` block | There are diagnostics, or `includeDiagnostics` is `true` |
628
+
629
+ Only the entries a submission actually carried are removed once the server has them, so anything
630
+ recorded while a submission is in flight waits for the next one. Entries older than
631
+ `batch_request_timeout` days are left out, at most 1,000 are kept (oldest dropped first), a single
632
+ message is truncated at 10,000 characters, and neither file is sent above 2 MB.
633
+
634
+ ## Heatmaps
635
+
636
+ Records where people click and how far they scroll, for the console's heatmaps. It is on the
637
+ main entry point and off until you turn it on.
638
+
639
+ ```ts
640
+ import { Apptics, AppticsHeatmap } from '@zoho_apptics/apptics-js-sdk';
641
+
642
+ Apptics.init({ ...config, track_heatmap: true });
643
+ // or turn it on later: AppticsHeatmap.trackHeatmap(true)
644
+ ```
645
+
646
+ `track_heatmap: true` in `Apptics.init()` starts capture with the SDK. With a `<script>` tag it's
647
+ `AppticsHeatmap.trackHeatmap()` on the `AppticsHeatmap` global.
648
+
649
+ | Call | Notes |
650
+ |---|---|
651
+ | `AppticsHeatmap.trackHeatmap(on?)` | Starts or stops capture; `on` defaults to `true`. |
652
+
653
+ Heatmap records go out with the rest of the stored data: on the flush timer, or when you call
654
+ `Apptics.flush()`.
655
+
656
+ A click gives coordinates, normalized position, a label for the element, and whether it was
657
+ clickable, repeated in frustration (`is_rage_click`) or changed nothing (`is_dead_click`).
658
+ Scrolling is coalesced into one record per gesture: direction, duration, velocity and how far
659
+ down the page it left you. At most `heatmap_max_per_session` records per visit (default 500),
660
+ then capture pauses.
661
+
662
+ ## Debugging
663
+
664
+ Debug mode is off by default; you follow it in the Apptics web console.
665
+
666
+ ### Debug mode
667
+
668
+ When a payload isn't showing up in the console, turn on debug mode. It sends engagement,
669
+ exception and log payloads to their `debug/` endpoints, which keep a traceable record of each
670
+ request on the server: follow them in the Apptics web console, along with the request id and the
671
+ link to the server's logs for that request.
672
+
673
+ ```ts
674
+ Apptics.init({ ...config, debug: true }); // start in debug mode
675
+
676
+ // or turn it on and off at any time
677
+ Apptics.enableDebug(); // turn debug mode on
678
+ Apptics.disableDebug(); // and off again
679
+ ```
680
+
681
+ `Apptics.enableDebug()` and `Apptics.disableDebug()` don't need `Apptics.init()`: call it before `Apptics.init()`, while the SDK starts or at any
682
+ time after, and it takes effect at once. What the app chooses this way holds for the rest of the
683
+ page's life, over `debug` in `Apptics.init()` and through `Apptics.reset()`, so a page whose config turns
684
+ debug mode on can still switch it off with `Apptics.disableDebug()`. With a `<script>` tag they are
685
+ the same calls on the `Apptics` global.
686
+
687
+ Debug mode changes where payloads go, not what is in them. It switches itself off after an hour
688
+ with nothing tracked, so a forgotten tab stops tracing on its own.
689
+
690
+ | Key / call | Default | Notes |
691
+ |---|---|---|
692
+ | `debug` | `false` | Start in debug mode, unless the app has already called `Apptics.enableDebug()` or `Apptics.disableDebug()`. |
693
+ | `Apptics.enableDebug()` / `Apptics.disableDebug()` | | Turn debug mode on or off, with or without `Apptics.init()`. |
694
+ | `device_name` | unset | Names this device in the debug stream, so testers on the same build can tell their requests apart. Without it, while debug mode is on, a name is made from the browser and OS. |
695
+
696
+ ## Configuration
697
+
698
+ Everything `Apptics.init(config)` accepts.
699
+
700
+ | Key | Required | Description |
701
+ |---|---|---|
702
+ | `base_url` | yes | The Apptics host every request goes to, from the Apptics web console (a trailing slash is ignored) |
703
+ | `auth_key` | yes | The app's auth key, from the Apptics web console |
704
+ | `app_domain` | yes | The app's domain registered in the Apptics web console, e.g. `com.zylker.web` |
705
+ | `appversion` | yes | Your app's version, e.g. `2.4.0`. No default: everything the SDK reports is tied to the build it came from. Changing it registers the new version |
706
+ | `debug` | | Debug mode: follow requests in the Apptics web console (see [Debugging](#debugging)). Off by default |
707
+ | `ignore_do_not_track` | | Track even when the browser sends Do Not Track. Off by default |
708
+ | `track_api` | | Record the app's own fetch and XHR calls. Off by default; which hosts and endpoints, through `AppticsApi.configure` (see [API Tracking](#api-tracking)) |
709
+ | `track_heatmap` | | Record clicks and scrolls (see [Heatmaps](#heatmaps)). Off by default |
710
+ | `heatmap_max_per_session` | | Most heatmap records one visit may produce: 500 by default, 0 to 10000. Capture pauses past it |
711
+ | `track_screen` | | Record a screen view for every navigation, named by its path. On by default; `false` turns it off (see [Automatic screen tracking](#automatic-screen-tracking)) |
712
+ | `screen_routes` | | How automatic screen tracking names a path: route patterns (`'/users/{id}/orders'`), `{ equals, name }` and `{ regex, name }` rules, tried in order (see [Automatic screen tracking](#automatic-screen-tracking)) |
713
+ | `feedback_theme` | | The feedback form's theme: `'light'`, `'dark'` or `'auto'` (the default, which follows the visitor's system setting). See [Show feedback screen](#show-feedback-screen) |
714
+ | `device_name` | | Names this device in debug mode (see [Debugging](#debugging)) |
715
+
716
+ The earlier names still work, with a console warning naming the key to use: `serviceUrl` and
717
+ `base_domain` for `base_url`, `zak` and `token` for `auth_key`, and `identifier` for `app_domain`.
718
+ `aaid`, `zsoid` and `projectID` are no longer needed, and are ignored if passed.
719
+
720
+ ### Timing
721
+
722
+ Data is stored first and sent on a timer, so these control how often the SDK talks to the server. All are optional; a value outside its range is ignored with a warning and the default is used.
723
+
724
+ | Key | Default | Range | Description |
725
+ |---|---|---|---|
726
+ | `batch_flush_interval` | `30000` | 1000 to 3600000 | How often stored data is sent, in milliseconds |
727
+ | `getupdates_interval` | `120000` | 10000 to 86400000 | How often the server's tracking settings are checked, in milliseconds; the server can ask for a longer wait |
728
+ | `batch_size` | `50` | 1 to 500 | Most items in one request (also capped at 1 MB) |
729
+ | `batch_request_timeout` | `7` | 1 to 30 | Days an unsent item is kept before it is dropped |
730
+ | `inactiveduration` | `30` | 1 to 1440 | Idle minutes that end a session |
731
+ | `min_session_duration` | `5000` | 0 to 60000 | Milliseconds a visit must last before its session is recorded; anything tracked sooner records it at once. `0` turns the wait off |
732
+
733
+ Data is also sent when you call `Apptics.flush()`, when the page is hidden, when the tab closes, and when the browser comes back online. The interval is the floor, not the only trigger.
734
+
735
+ ## API reference
736
+
737
+ | Call | Notes |
738
+ |---|---|
739
+ | `Apptics.init(config)` | Starts the SDK. Returns `true` when start-up has begun. |
740
+ | `Apptics.ready()` | Promise that resolves `true` once the SDK is running (`false` if it can't start). |
741
+ | `AppticsEvent.addEvent(eventName, groupName?, properties?)` / `AppticsEvent.addEvent(AppticsEvent.DefinedEvents.X, properties?)` | See [Event Tracking](#event-tracking). |
742
+ | `AppticsScreen.inScreen(screenName, properties?)` → id / `AppticsScreen.outScreen(id)` | See [Screen Tracking](#screen-tracking). |
743
+ | `AppticsError.trackError(error, properties?)` / `AppticsError.trackGlobalErrorHandler(enabled?)` | See [Error Tracking](#error-tracking). |
744
+ | `AppticsUser.setUser(userid, groupid?, properties?)` / `AppticsUser.getUser()` / `AppticsUser.UserBuilder` | See [Track User](#track-user). |
745
+ | `AppticsSettings.setTrackingStatus(status)` / `AppticsSettings.getTrackingStatus()` / `AppticsSettings.AP_TRACKING_STATUS` | See [Tracking settings](#tracking-settings). |
746
+ | `AppticsApi.startTrackApi(url, method?)` / `endTrackApi(id, responseCode, responseMessage?)` / `configure(config)` / `enableApiTracking(enabled?)` / `AppticsApi.ApiTrackingBuilder` | See [API Tracking](#api-tracking). |
747
+ | `AppticsLogger.verbose / debug / info / warn / error(message, properties?)` | From `/remote-logger`; see [Remote Logger](#remote-logger). |
748
+ | `AppticsRemoteConfig.fetchValue(name, callback?)` / `fetchValues(names?, callback?)` / `setCustomConditionValue(conditionname, value)` | From `/remote-config`; see [Remote Config](#remote-config). |
749
+ | `AppticsFeedback.addFeedbackLog(message)` / `AppticsFeedback.addDiagnosticsInfo(heading, key, value)` | From `/feedback`; see [Attach logs and diagnostics](#attach-logs-and-diagnostics-with-feedback). |
750
+ | `AppticsFeedback.feedbackFormOpen(options?)` / `feedbackFormClose()` / `sendFeedback(options?)` / `setFeedbackTheme(theme)` / `AppticsFeedback.AP_FEEDBACK_TYPE` | From `/feedback`; see [In-App Feedback](#in-app-feedback). |
751
+ | `AppticsHeatmap.trackHeatmap(on?)` | See [Heatmaps](#heatmaps). |
752
+ | `Apptics.flush()` | Send stored data now (engagement, errors and heatmap records); it's otherwise sent every 30 s (see [Timing](#timing)). |
753
+ | `Apptics.enableDebug()` / `Apptics.disableDebug()` | See [Debugging](#debugging). |
754
+ | `Apptics.reset()` | Clear stored identity, queued data and remote config values, then start again. |
755
+
756
+ Entry points: `@zoho_apptics/apptics-js-sdk` (everything above except what follows), `/feedback`, `/remote-config`
757
+ (see [Remote Config](#remote-config)), `/remote-logger` (see [Remote Logger](#remote-logger)). Types ship with the package:
758
+ `import type { AppticsConfig, TrackingStatus } from '@zoho_apptics/apptics-js-sdk'`.
759
+
760
+ ## Loading from a CDN
761
+
762
+ For `<script type="module">`, load the minified ESM build (a few requests instead of one per module) and call `Apptics.init()` yourself:
763
+
764
+ ```html
765
+ <script type="module">
766
+ import { Apptics, AppticsEvent } from 'https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta.1/dist/apptics.esm.min.js';
767
+ // The feedback form is a separate file, so pages without it don't download it
768
+ import { AppticsFeedback } from 'https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta.1/dist/feedback.esm.min.js';
769
+
770
+ Apptics.init({
771
+ base_url: 'YOUR_BASE_URL',
772
+ auth_key: 'YOUR_AUTH_KEY',
773
+ app_domain: 'YOUR_APP_DOMAIN',
774
+ appversion: 'YOUR_APPVERSION', // your app's version
775
+ });
776
+ // screens are tracked from the URL automatically
777
+ AppticsEvent.addEvent('signup', 'onboarding', { plan: 'pro' });
778
+ </script>
9
779
  ```
10
780
 
11
- #### intialization
781
+ | File | For | Size (gzipped) |
782
+ |---|---|---|
783
+ | `dist/apptics.min.js` | `<script>` pages; sets `window.apptics`, includes every module | ~53 kB |
784
+ | `dist/apptics.esm.min.js` | `<script type="module">`; analytics only (with the `dist/chunks/` files it imports) | ~30 kB |
785
+ | `dist/feedback.esm.min.js` | the feedback form, for module pages | ~17 kB more |
12
786
 
13
- 1. Register your app and copy `apptics snippet` from Apptics web console.
14
- 2. Once you have created the Project and added your app, copy the apptics snippet, and move the file into the project index file.
787
+ The module files share one copy of the SDK through `dist/chunks/`, so load them all from the same version and path.
15
788
 
789
+ unpkg works the same way: `https://unpkg.com/@zoho_apptics/apptics-js-sdk@2.0.0-beta.1/dist/apptics.min.js`. Add `defer` if you don't need the SDK before the page renders. For a subresource-integrity hash:
16
790
 
17
- ## Usage
791
+ ```bash
792
+ curl -s https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta.1/dist/apptics.min.js \
793
+ | openssl dgst -sha384 -binary | openssl base64 -A
794
+ ```
18
795
 
19
- ```javascript
20
- const Apptics = require('@zoho_apptics/apptics-js-sdk');
796
+ ```html
797
+ <script src="…/apptics.min.js" integrity="sha384-…" crossorigin="anonymous"></script>
21
798
  ```
22
799
 
800
+ ## Migrating from 1.0.2 to 2.0.0-beta.1
801
+
802
+ ### What you need to change
803
+
804
+ - **Every call is on the object of its feature.** Rename them everywhere, router hooks included: a
805
+ call to an old name left in place throws.
806
+
807
+ | 1.0.2 | 2.0.0-beta.1 |
808
+ |---|---|
809
+ | `apptics.init(config)` | `Apptics.init(config)` |
810
+ | `apptics.trackEvent(name, group, props)` | `AppticsEvent.addEvent(eventName, groupName, properties)` |
811
+ | `apptics.trackScreen(name, props)` | `AppticsScreen.inScreen(screenName, properties)`, which also returns the view's id for `AppticsScreen.outScreen(id)` |
812
+ | `apptics.trackError(event, props)` | `AppticsError.trackError(error, properties)` |
813
+ | `apptics.enableGlobalErrorHandler(on)` | `AppticsError.trackGlobalErrorHandler(enabled)` |
814
+ | `apptics.setUser(userid, groupid, props)` | `AppticsUser.setUser(userid, groupid, properties)` |
815
+ | `apptics.getUser()`, `new users.UserBuilder()` | `AppticsUser.getUser()`, `new AppticsUser.UserBuilder()` |
816
+ | `apptics.setTrackingStatus(s)`, `getTrackingStatus()` | `AppticsSettings.setTrackingStatus(status)`, `AppticsSettings.getTrackingStatus()` |
817
+ | `apptics.flush()`, `reset()`, `ready()` | `Apptics.flush()`, `Apptics.reset()`, `Apptics.ready()` |
818
+ | `apptics.feedbackOpen()`, `feedbackClose()` | `AppticsFeedback.feedbackFormOpen(options)`, `AppticsFeedback.feedbackFormClose()` |
819
+ | `apptics.sendFeedback(type, message, …)` | `AppticsFeedback.sendFeedback({ type, message, … })`: one options object |
820
+ | `apptics.addFeedbackLog()`, `addDiagnosticsInfo()` | `AppticsFeedback.addFeedbackLog(message)`, `AppticsFeedback.addDiagnosticsInfo(heading, key, value)` |
821
+
822
+ - **`Apptics.init()` takes new config.** Pass `base_url`, `auth_key`, `app_domain` and `appversion`, all
823
+ required (1.0.2's `base_domain` and `token` still work, with a warning). The SDK gets `aaid` and the portal and project ids from the server itself, so
824
+ `aaid`/`aaID`, `zsoid` and `projectID` are no longer needed and are ignored if passed. Without a
825
+ required key, `Apptics.init()` logs which one is missing and returns `false`.
826
+ - **`appversion` is required.** 1.0.2 quietly defaulted it to `1.0`, which attributed every session,
827
+ error and crash to a version that was never shipped. Pass your real build version.
828
+ - **npm users can import the SDK.** 1.0.2 exported nothing, so it only worked alongside the console
829
+ snippet. Now:
830
+
831
+ ```ts
832
+ import { Apptics } from '@zoho_apptics/apptics-js-sdk';
833
+ Apptics.init({ base_url, auth_key, app_domain, appversion });
834
+ ```
835
+
836
+ - **The optional modules are their own entry points for npm users**, so an app ships only what it
837
+ uses: `AppticsFeedback` from `/feedback` (every feedback call, logs and diagnostics included; the
838
+ form adds its own styles), `AppticsRemoteConfig` from `/remote-config` and `AppticsLogger` from
839
+ `/remote-logger`. The `<script>` build has everything, as globals and on `window.apptics`.
840
+
841
+ ### Behaviour you may notice
842
+
843
+ - **`debug: true` now turns on debug mode** (requests followed in the Apptics web console). In 1.0.2 it logged to the browser console.
844
+ - When the Apptics server tells the SDK to stop tracking an app version, it stops; 1.0.2 ignored that signal.
845
+ - `ignore_do_not_track: true` now ignores the browser's Do Not Track setting; in 1.0.2 it disabled tracking. Do Not Track is decided on every visit and never stored.
846
+ - `mute()` and `unmute()` are gone. To stop tracking, use `AppticsSettings.setTrackingStatus(AppticsSettings.AP_TRACKING_STATUS.NO_TRACKING)`; unlike `mute()`, it is stored across visits. Feedback is still sent with tracking off.
847
+ - **Screens are tracked automatically**, from the URL, including single-page route changes. An `AppticsScreen.inScreen()` called from your router replaces the automatic view of that navigation, so nothing is counted twice; `track_screen: false` turns automatic tracking off, and `screen_routes` names paths. Each screen view now records how long it lasted.
848
+ - **Events and properties follow the Android rules**: an event with an invalid group is not logged (a missing group is still `default`), names and keys may not start with `ap_`, and a bad property is dropped on its own rather than taking the others with it. Booleans arrive as Boolean, and `1` / `0` as Number. An invalid name is said in the console.
849
+ - Calling `Apptics.init()` a second time is ignored; use `Apptics.reset()` to start over.
850
+ - `Apptics.init()` still returns at once, but start-up finishes a few milliseconds later, once storage loads, so `window.appticsReady()` fires slightly later than in 1.0.2. Calls made in between are queued and replayed; `await Apptics.ready()` waits for it. A first visit with no network starts once the browser is back online.
851
+ - `AppticsFeedback.sendFeedback()` returns a Promise. `includeLog` and `includeDiagnostics` are three-way: `true` always sends that file, `false` never does, and leaving it unset sends it when something is stored.
852
+ - `dyninfo.txt` carries the diagnostics entries and a `[Device Info]` block. Stored feedback logs and diagnostics are capped at 1,000 entries and 10,000 characters per message.
853
+ - Clicks tracked with `apeventaction` (or `data-apeventaction`) no longer need jQuery.
854
+ - Data moves from localStorage to IndexedDB on the first visit (device identity, consent, unsent events, feedback log entries), and the old keys are removed. Where IndexedDB is unavailable, the SDK keeps using localStorage.
855
+ - Queued data is sent when the page is hidden or closed, using `pagehide` / `visibilitychange` instead of `beforeunload`.
856
+ - New, and off until you use them: heatmaps (`AppticsHeatmap.trackHeatmap()`), remote config, API tracking and the remote logger (`AppticsLogger`). Remote config, once imported, fetches its values on start-up.
857
+
23
858
  ## License
24
859
 
25
- MIT © Zoho Apptics
860
+ © Zoho Corporation Private Limited. The SDK is licensed for use in your websites and web applications with Zoho Apptics; the full terms are in the package's LICENSE file.