@zoho_apptics/apptics-js-sdk 1.0.2 → 2.0.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -0
- package/LICENSE +13 -0
- package/README.md +747 -11
- package/dist/apptics.esm.min.js +3 -0
- package/dist/apptics.esm.min.js.map +1 -0
- package/dist/apptics.min.js +3 -0
- package/dist/apptics.min.js.map +1 -0
- package/dist/chunks/batch-BU_mTpm1.min.js +3 -0
- package/dist/chunks/batch-BU_mTpm1.min.js.map +1 -0
- package/dist/chunks/logs-Bea5zUW_.min.js +3 -0
- package/dist/chunks/logs-Bea5zUW_.min.js.map +1 -0
- package/dist/cjs/auto-init.d.ts +8 -0
- package/dist/cjs/auto-init.js +32 -0
- package/dist/cjs/core/attempts.d.ts +28 -0
- package/dist/cjs/core/attempts.js +113 -0
- package/dist/cjs/core/batch.d.ts +32 -0
- package/dist/cjs/core/batch.js +115 -0
- package/dist/cjs/core/callsite.d.ts +28 -0
- package/dist/cjs/core/callsite.js +93 -0
- package/dist/cjs/core/consent.d.ts +23 -0
- package/dist/cjs/core/consent.js +130 -0
- package/dist/cjs/core/constants.d.ts +212 -0
- package/dist/cjs/core/constants.js +299 -0
- package/dist/cjs/core/db.d.ts +40 -0
- package/dist/cjs/core/db.js +334 -0
- package/dist/cjs/core/debug.d.ts +22 -0
- package/dist/cjs/core/debug.js +88 -0
- package/dist/cjs/core/env.d.ts +28 -0
- package/dist/cjs/core/env.js +267 -0
- package/dist/cjs/core/framework-id.d.ts +1 -0
- package/dist/cjs/core/framework-id.js +9 -0
- package/dist/cjs/core/guard.d.ts +2 -0
- package/dist/cjs/core/guard.js +72 -0
- package/dist/cjs/core/hooks.d.ts +41 -0
- package/dist/cjs/core/hooks.js +23 -0
- package/dist/cjs/core/logger.d.ts +16 -0
- package/dist/cjs/core/logger.js +86 -0
- package/dist/cjs/core/state.d.ts +69 -0
- package/dist/cjs/core/state.js +78 -0
- package/dist/cjs/core/storage.d.ts +46 -0
- package/dist/cjs/core/storage.js +229 -0
- package/dist/cjs/core/transport.d.ts +83 -0
- package/dist/cjs/core/transport.js +441 -0
- package/dist/cjs/core/utils.d.ts +26 -0
- package/dist/cjs/core/utils.js +175 -0
- package/dist/cjs/core/validate.d.ts +18 -0
- package/dist/cjs/core/validate.js +233 -0
- package/dist/cjs/device/appversion.d.ts +20 -0
- package/dist/cjs/device/appversion.js +158 -0
- package/dist/cjs/device/index.d.ts +23 -0
- package/dist/cjs/device/index.js +372 -0
- package/dist/cjs/engagement/api-rules.d.ts +84 -0
- package/dist/cjs/engagement/api-rules.js +219 -0
- package/dist/cjs/engagement/auto-api.d.ts +7 -0
- package/dist/cjs/engagement/auto-api.js +200 -0
- package/dist/cjs/engagement/auto-screen.d.ts +16 -0
- package/dist/cjs/engagement/auto-screen.js +241 -0
- package/dist/cjs/engagement/defined-events.d.ts +23 -0
- package/dist/cjs/engagement/defined-events.js +58 -0
- package/dist/cjs/engagement/send.d.ts +6 -0
- package/dist/cjs/engagement/send.js +99 -0
- package/dist/cjs/engagement/session.d.ts +42 -0
- package/dist/cjs/engagement/session.js +327 -0
- package/dist/cjs/engagement/track.d.ts +56 -0
- package/dist/cjs/engagement/track.js +350 -0
- package/dist/cjs/errors/send.d.ts +6 -0
- package/dist/cjs/errors/send.js +73 -0
- package/dist/cjs/errors/track.d.ts +4 -0
- package/dist/cjs/errors/track.js +127 -0
- package/dist/cjs/feedback/controls.d.ts +31 -0
- package/dist/cjs/feedback/controls.js +286 -0
- package/dist/cjs/feedback/index.d.ts +8 -0
- package/dist/cjs/feedback/index.js +37 -0
- package/dist/cjs/feedback/logs.d.ts +40 -0
- package/dist/cjs/feedback/logs.js +202 -0
- package/dist/cjs/feedback/send.d.ts +25 -0
- package/dist/cjs/feedback/send.js +178 -0
- package/dist/cjs/feedback/template.d.ts +2 -0
- package/dist/cjs/feedback/template.js +162 -0
- package/dist/cjs/feedback/ui.d.ts +55 -0
- package/dist/cjs/feedback/ui.js +631 -0
- package/dist/cjs/heatmap/index.d.ts +16 -0
- package/dist/cjs/heatmap/index.js +22 -0
- package/dist/cjs/heatmap/send.d.ts +13 -0
- package/dist/cjs/heatmap/send.js +82 -0
- package/dist/cjs/heatmap/track.d.ts +4 -0
- package/dist/cjs/heatmap/track.js +377 -0
- package/dist/cjs/index.d.ts +68 -0
- package/dist/cjs/index.js +150 -0
- package/dist/cjs/lifecycle.d.ts +14 -0
- package/dist/cjs/lifecycle.js +538 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/remoteconfig/custom.d.ts +10 -0
- package/dist/cjs/remoteconfig/custom.js +78 -0
- package/dist/cjs/remoteconfig/evaluate.d.ts +24 -0
- package/dist/cjs/remoteconfig/evaluate.js +171 -0
- package/dist/cjs/remoteconfig/fetch.d.ts +24 -0
- package/dist/cjs/remoteconfig/fetch.js +214 -0
- package/dist/cjs/remoteconfig/index.d.ts +13 -0
- package/dist/cjs/remoteconfig/index.js +90 -0
- package/dist/cjs/remoteconfig/store.d.ts +18 -0
- package/dist/cjs/remoteconfig/store.js +98 -0
- package/dist/cjs/remotelog/gate.d.ts +11 -0
- package/dist/cjs/remotelog/gate.js +54 -0
- package/dist/cjs/remotelog/index.d.ts +25 -0
- package/dist/cjs/remotelog/index.js +55 -0
- package/dist/cjs/remotelog/logger.d.ts +28 -0
- package/dist/cjs/remotelog/logger.js +139 -0
- package/dist/cjs/remotelog/send.d.ts +13 -0
- package/dist/cjs/remotelog/send.js +153 -0
- package/dist/cjs/scheduler.d.ts +8 -0
- package/dist/cjs/scheduler.js +78 -0
- package/dist/cjs/types.d.ts +312 -0
- package/dist/cjs/user/index.d.ts +33 -0
- package/dist/cjs/user/index.js +178 -0
- package/dist/esm/auto-init.d.ts +8 -0
- package/dist/esm/auto-init.js +29 -0
- package/dist/esm/core/attempts.d.ts +28 -0
- package/dist/esm/core/attempts.js +106 -0
- package/dist/esm/core/batch.d.ts +32 -0
- package/dist/esm/core/batch.js +108 -0
- package/dist/esm/core/callsite.d.ts +28 -0
- package/dist/esm/core/callsite.js +89 -0
- package/dist/esm/core/consent.d.ts +23 -0
- package/dist/esm/core/consent.js +120 -0
- package/dist/esm/core/constants.d.ts +212 -0
- package/dist/esm/core/constants.js +277 -0
- package/dist/esm/core/db.d.ts +40 -0
- package/dist/esm/core/db.js +324 -0
- package/dist/esm/core/debug.d.ts +22 -0
- package/dist/esm/core/debug.js +81 -0
- package/dist/esm/core/env.d.ts +28 -0
- package/dist/esm/core/env.js +251 -0
- package/dist/esm/core/framework-id.d.ts +1 -0
- package/dist/esm/core/framework-id.js +7 -0
- package/dist/esm/core/guard.d.ts +2 -0
- package/dist/esm/core/guard.js +70 -0
- package/dist/esm/core/hooks.d.ts +41 -0
- package/dist/esm/core/hooks.js +21 -0
- package/dist/esm/core/logger.d.ts +16 -0
- package/dist/esm/core/logger.js +82 -0
- package/dist/esm/core/state.d.ts +69 -0
- package/dist/esm/core/state.js +73 -0
- package/dist/esm/core/storage.d.ts +46 -0
- package/dist/esm/core/storage.js +219 -0
- package/dist/esm/core/transport.d.ts +83 -0
- package/dist/esm/core/transport.js +425 -0
- package/dist/esm/core/utils.d.ts +26 -0
- package/dist/esm/core/utils.js +161 -0
- package/dist/esm/core/validate.d.ts +18 -0
- package/dist/esm/core/validate.js +226 -0
- package/dist/esm/device/appversion.d.ts +20 -0
- package/dist/esm/device/appversion.js +153 -0
- package/dist/esm/device/index.d.ts +23 -0
- package/dist/esm/device/index.js +362 -0
- package/dist/esm/engagement/api-rules.d.ts +84 -0
- package/dist/esm/engagement/api-rules.js +211 -0
- package/dist/esm/engagement/auto-api.d.ts +7 -0
- package/dist/esm/engagement/auto-api.js +198 -0
- package/dist/esm/engagement/auto-screen.d.ts +16 -0
- package/dist/esm/engagement/auto-screen.js +238 -0
- package/dist/esm/engagement/defined-events.d.ts +23 -0
- package/dist/esm/engagement/defined-events.js +55 -0
- package/dist/esm/engagement/send.d.ts +6 -0
- package/dist/esm/engagement/send.js +96 -0
- package/dist/esm/engagement/session.d.ts +42 -0
- package/dist/esm/engagement/session.js +315 -0
- package/dist/esm/engagement/track.d.ts +56 -0
- package/dist/esm/engagement/track.js +340 -0
- package/dist/esm/errors/send.d.ts +6 -0
- package/dist/esm/errors/send.js +70 -0
- package/dist/esm/errors/track.d.ts +4 -0
- package/dist/esm/errors/track.js +123 -0
- package/dist/esm/feedback/controls.d.ts +31 -0
- package/dist/esm/feedback/controls.js +280 -0
- package/dist/esm/feedback/index.d.ts +8 -0
- package/dist/esm/feedback/index.js +30 -0
- package/dist/esm/feedback/logs.d.ts +40 -0
- package/dist/esm/feedback/logs.js +188 -0
- package/dist/esm/feedback/send.d.ts +25 -0
- package/dist/esm/feedback/send.js +175 -0
- package/dist/esm/feedback/template.d.ts +2 -0
- package/dist/esm/feedback/template.js +159 -0
- package/dist/esm/feedback/ui.d.ts +55 -0
- package/dist/esm/feedback/ui.js +626 -0
- package/dist/esm/heatmap/index.d.ts +16 -0
- package/dist/esm/heatmap/index.js +17 -0
- package/dist/esm/heatmap/send.d.ts +13 -0
- package/dist/esm/heatmap/send.js +79 -0
- package/dist/esm/heatmap/track.d.ts +4 -0
- package/dist/esm/heatmap/track.js +374 -0
- package/dist/esm/index.d.ts +68 -0
- package/dist/esm/index.js +122 -0
- package/dist/esm/lifecycle.d.ts +14 -0
- package/dist/esm/lifecycle.js +534 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/remoteconfig/custom.d.ts +10 -0
- package/dist/esm/remoteconfig/custom.js +74 -0
- package/dist/esm/remoteconfig/evaluate.d.ts +24 -0
- package/dist/esm/remoteconfig/evaluate.js +166 -0
- package/dist/esm/remoteconfig/fetch.d.ts +24 -0
- package/dist/esm/remoteconfig/fetch.js +210 -0
- package/dist/esm/remoteconfig/index.d.ts +13 -0
- package/dist/esm/remoteconfig/index.js +85 -0
- package/dist/esm/remoteconfig/store.d.ts +18 -0
- package/dist/esm/remoteconfig/store.js +91 -0
- package/dist/esm/remotelog/gate.d.ts +11 -0
- package/dist/esm/remotelog/gate.js +48 -0
- package/dist/esm/remotelog/index.d.ts +25 -0
- package/dist/esm/remotelog/index.js +51 -0
- package/dist/esm/remotelog/logger.d.ts +28 -0
- package/dist/esm/remotelog/logger.js +136 -0
- package/dist/esm/remotelog/send.d.ts +13 -0
- package/dist/esm/remotelog/send.js +149 -0
- package/dist/esm/scheduler.d.ts +8 -0
- package/dist/esm/scheduler.js +74 -0
- package/dist/esm/types.d.ts +312 -0
- package/dist/esm/user/index.d.ts +33 -0
- package/dist/esm/user/index.js +170 -0
- package/dist/feedback.esm.min.js +3 -0
- package/dist/feedback.esm.min.js.map +1 -0
- package/dist/heatmap.esm.min.js +3 -0
- package/dist/heatmap.esm.min.js.map +1 -0
- package/dist/style.css +1 -0
- package/package.json +124 -7
- package/src/appticssdk.js +0 -1
package/README.md
CHANGED
|
@@ -1,25 +1,761 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Apptics JavaScript SDK
|
|
2
2
|
|
|
3
|
-
JavaScript
|
|
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
|
-
|
|
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 `serviceUrl`, `zak` and `identifier`.
|
|
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
|
|
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 [Upgrading from 1.0.2](#upgrading-from-102).
|
|
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, AppticsEvents } from '@zoho_apptics/apptics-js-sdk';
|
|
28
|
+
|
|
29
|
+
// Your web app's Apptics configuration, from the Apptics web console
|
|
30
|
+
const appticsConfig = {
|
|
31
|
+
serviceUrl: 'YOUR_SERVICE_URL',
|
|
32
|
+
zak: 'YOUR_TOKEN',
|
|
33
|
+
identifier: 'YOUR_IDENTIFIER',
|
|
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
|
+
AppticsEvents.addEvent('signup', 'onboarding', { plan: 'pro' });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The config object is **required**, with `serviceUrl`, `zak`, `identifier` 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
|
+
The SDK's objects carry the Android SDK's names — `Apptics`, `AppticsEvents`, `AppticsScreenTracker`, `AppticsApiTracker`, `AppticsLogger`, `AppticsRemoteConfig` — so code reads the same on every platform. 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 build
|
|
52
|
+
loads its own stylesheet (`style.css`, from the same CDN folder) for the feedback form.
|
|
53
|
+
|
|
54
|
+
```html
|
|
55
|
+
<script src="https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta/dist/apptics.min.js"></script>
|
|
56
|
+
<script>
|
|
57
|
+
Apptics.init({
|
|
58
|
+
serviceUrl: 'YOUR_SERVICE_URL',
|
|
59
|
+
zak: 'YOUR_TOKEN',
|
|
60
|
+
identifier: 'YOUR_IDENTIFIER',
|
|
61
|
+
appversion: 'YOUR_APPVERSION', // your app's version
|
|
62
|
+
});
|
|
63
|
+
// screens are tracked from the URL automatically; events are yours to send
|
|
64
|
+
AppticsEvents.addEvent('signup', 'onboarding', { plan: 'pro' });
|
|
65
|
+
</script>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The build defines the same names as globals — `Apptics`, `AppticsEvents`, `AppticsScreenTracker`, `AppticsApiTracker`, `AppticsLogger`, `AppticsRemoteConfig`, `AppticsTrackError`, `AppticsSetUser` — so a page's code reads as it would after an import. Everything, every module included (feedback, heatmaps, remote config), is also on `window.apptics`, including `apptics.DefinedEvents` and `apptics.ApiTrackingBuilder`, which have no global of their own. 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`) 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
|
+
### Frameworks
|
|
77
|
+
|
|
78
|
+
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 `AppticsScreenTracker.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.
|
|
79
|
+
|
|
80
|
+
| Framework | Start | Screen on navigation, with `track_screen: false` |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| React, Remix, Gatsby | `useEffect(() => { Apptics.init(config); }, [])` | `useLocation()` in an effect |
|
|
83
|
+
| Vue 3 | a plugin: `app.use(apptics, { router })` | `router.afterEach(to => AppticsScreenTracker.inScreen(to.path))` |
|
|
84
|
+
| Angular | an injectable service | `router.events` filtered to `NavigationEnd` |
|
|
85
|
+
| Svelte, SvelteKit | `src/hooks.client.ts`, or `onMount` | `afterNavigate` |
|
|
86
|
+
| Ember | an instance initializer | `router.on('routeDidChange', …)` |
|
|
87
|
+
| Next.js | `'use client'` component in the layout | `usePathname()` in an effect |
|
|
88
|
+
| Nuxt 3 | `plugins/apptics.client.ts` | `useRouter().afterEach(…)` |
|
|
89
|
+
| Plain JS, jQuery | the `<script>` tag itself | `AppticsScreenTracker.inScreen()` where you change view |
|
|
90
|
+
|
|
91
|
+
**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.
|
|
92
|
+
|
|
93
|
+
## Error Tracking
|
|
94
|
+
|
|
95
|
+
Uncaught errors and unhandled promise rejections are tracked automatically, as JS errors in the console.
|
|
96
|
+
|
|
97
|
+
Report an error your code caught:
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
import { AppticsTrackError } from '@zoho_apptics/apptics-js-sdk';
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
doSomething();
|
|
104
|
+
} catch (error) {
|
|
105
|
+
AppticsTrackError({ type: 'error', error });
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`AppticsTrackError` also accepts `error` and `unhandledrejection` events as they are. Turn the automatic tracking off with `enableGlobalErrorHandler(false)`.
|
|
110
|
+
|
|
111
|
+
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:
|
|
112
|
+
|
|
113
|
+
| Framework | Error hook |
|
|
114
|
+
|---|---|
|
|
115
|
+
| React | an error boundary: `componentDidCatch(error) { AppticsTrackError({ type: 'error', error }); }` |
|
|
116
|
+
| Vue 3, Nuxt 3 | `app.config.errorHandler = (error) => AppticsTrackError({ type: 'error', error })` (Nuxt: `nuxtApp.hook('vue:error', …)`) |
|
|
117
|
+
| Angular | an `ErrorHandler` whose `handleError(error)` calls `AppticsTrackError({ type: 'error', error })`, provided as `{ provide: ErrorHandler, useClass: … }` |
|
|
118
|
+
| SvelteKit | `handleError({ error })` in `src/hooks.client.ts` |
|
|
119
|
+
| Ember | `Ember.onerror = (error) => AppticsTrackError({ type: 'error', error })` |
|
|
120
|
+
|
|
121
|
+
## Event Tracking
|
|
122
|
+
|
|
123
|
+
Track an event, with an optional group and properties:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { AppticsEvents } from '@zoho_apptics/apptics-js-sdk';
|
|
127
|
+
|
|
128
|
+
AppticsEvents.addEvent('eventName', 'eventGroupName', { propKey: 'propValue' });
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Event and group names start with a letter, followed by letters, digits or `_`, at most 100
|
|
132
|
+
characters, and may not start with `ap_` or `AP_`, which is reserved for defined events. As on
|
|
133
|
+
Android, an event with an invalid name or group is not logged, and the console says why. A group
|
|
134
|
+
left out is `default`.
|
|
135
|
+
|
|
136
|
+
### Defined events
|
|
137
|
+
|
|
138
|
+
Apptics' ready-made events, as Android's `DefinedEvents`, each with its own `AP_` group. They are
|
|
139
|
+
not tracked automatically; log the ones that apply:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { AppticsEvents, DefinedEvents } from '@zoho_apptics/apptics-js-sdk';
|
|
143
|
+
|
|
144
|
+
AppticsEvents.addEvent(DefinedEvents.AP_USER_LOGIN);
|
|
145
|
+
AppticsEvents.addEvent(DefinedEvents.AP_SEARCH, { results: 12 });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Group | Events |
|
|
149
|
+
|---|---|
|
|
150
|
+
| `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` |
|
|
151
|
+
| `AP_USER_LIFE_CYCLE` | `AP_USER_SIGNUP`, `AP_USER_LOGIN`, `AP_USER_LOGOUT` |
|
|
152
|
+
| `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` |
|
|
153
|
+
| `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` |
|
|
154
|
+
| `AP_OS` | `AP_OS_UPDATE`, `AP_OS_UNSUPPORTED` |
|
|
155
|
+
|
|
156
|
+
With a `<script>` tag it is `apptics.DefinedEvents`.
|
|
157
|
+
|
|
158
|
+
Clicks can also be tracked from markup, without code:
|
|
159
|
+
|
|
160
|
+
```html
|
|
161
|
+
<section apgroupaction="checkout">
|
|
162
|
+
<button apeventaction="buy_clicked" apeventprops='{"plan":"pro"}'>Buy</button>
|
|
163
|
+
</section>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Clicking the button, or anything inside it, tracks `buy_clicked` in group `checkout`.
|
|
167
|
+
|
|
168
|
+
Properties: keys start with a letter, followed by up to 49 letters, digits or `_`, and may not
|
|
169
|
+
start with `ap_`. Values are numbers, booleans or strings of up to 250 characters; anything else
|
|
170
|
+
(an object, an array) is sent as its JSON text. As on Android, a property that breaks these rules
|
|
171
|
+
is dropped and the event is still logged with the rest. At most 24 properties and 7,666
|
|
172
|
+
characters in all; past that the later ones are dropped. The same rules apply to screen and error
|
|
173
|
+
properties.
|
|
174
|
+
|
|
175
|
+
## Screen Tracking
|
|
176
|
+
|
|
177
|
+
Screens are tracked automatically, from the URL (see [below](#automatic-screen-tracking)). To
|
|
178
|
+
record one yourself — with your own name, or with properties — call `AppticsScreenTracker.inScreen`, as on Android:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
import { AppticsScreenTracker } from '@zoho_apptics/apptics-js-sdk';
|
|
182
|
+
|
|
183
|
+
AppticsScreenTracker.inScreen('checkout');
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Each screen view records how long it lasted. It ends when the next screen starts, when the page
|
|
187
|
+
is hidden (coming back starts a new view of the same screen), or when its session ends.
|
|
188
|
+
`AppticsScreenTracker.inScreen` returns the view's id; hand it to `AppticsScreenTracker.outScreen` to end the view yourself — a dialog
|
|
189
|
+
that closes without the page changing, say — as Android's `AppticsScreenTracker.outScreen` does:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { AppticsScreenTracker } from '@zoho_apptics/apptics-js-sdk';
|
|
193
|
+
|
|
194
|
+
const id = AppticsScreenTracker.inScreen('settings'); // when the screen is shown
|
|
195
|
+
AppticsScreenTracker.outScreen(id); // when it is dismissed
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
`AppticsScreenTracker.inScreen` returns `''` for a name that isn't a valid screen name.
|
|
199
|
+
|
|
200
|
+
Screen names: up to 99 characters of letters, digits, spaces and `. - _ & / :`. Events, errors,
|
|
201
|
+
feedback and heatmap records are attributed to the current screen: the one `AppticsScreenTracker.inScreen()` last
|
|
202
|
+
named, or else the one automatic tracking named from the URL. A heatmap record always has a
|
|
203
|
+
screen view behind it: with automatic tracking off and no `AppticsScreenTracker.inScreen()` call, the first record
|
|
204
|
+
on a page records a view of the page, named the same way from its path, and a new one when the
|
|
205
|
+
path changes.
|
|
206
|
+
|
|
207
|
+
An `AppticsScreenTracker.inScreen()` call made during a navigation — from a router hook, say —
|
|
208
|
+
replaces the automatic view of it, so the navigation counts once, under your name. The automatic view waits
|
|
209
|
+
300 ms for such a call before it is recorded; a call after that is a screen view of its own.
|
|
210
|
+
|
|
211
|
+
### Automatic screen tracking
|
|
212
|
+
|
|
213
|
+
On by default: the SDK records screen views from the URL, with no `AppticsScreenTracker.inScreen()` calls. It records the page the SDK starts on, then every navigation: `history.pushState` / `replaceState`
|
|
214
|
+
(React Router, Vue Router, Angular, Next.js and other single-page routers), back and forward, and
|
|
215
|
+
`#/` hash routes. The screen is named by its path, without the query string, so `/search?q=shoes`
|
|
216
|
+
is `/search` and `/#/orders` is `/orders`. Characters a screen name can't hold become `_`, and a
|
|
217
|
+
name stops at 99 characters. A plain `#anchor` and a navigation that lands on the same path are
|
|
218
|
+
not new views.
|
|
219
|
+
|
|
220
|
+
Paths with ids in them make every record its own screen. To give a route your own name —
|
|
221
|
+
`/orders/123` and `/orders/456` as one `Order detail` — call `AppticsScreenTracker.inScreen()` from your router as
|
|
222
|
+
the route changes: that replaces the automatic view of the navigation.
|
|
223
|
+
|
|
224
|
+
**An app that names its screens from its router** keeps doing so with `AppticsScreenTracker.inScreen()`: a call
|
|
225
|
+
within 300 ms of the navigation replaces the automatic view (see
|
|
226
|
+
[Screen Tracking](#screen-tracking)). To track only the screens you name yourself — screens that
|
|
227
|
+
aren't routes, or hooks that run later than that — turn automatic tracking off:
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
Apptics.init({ ...config, track_screen: false });
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`reset()` takes the history wrappers off; the `Apptics.init()` it runs puts them back.
|
|
234
|
+
|
|
235
|
+
## Track User
|
|
236
|
+
|
|
237
|
+
Register the signed-in user:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { AppticsSetUser } from '@zoho_apptics/apptics-js-sdk';
|
|
241
|
+
|
|
242
|
+
AppticsSetUser('user-id', 'customer-group-id'); // the group is optional
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
`AppticsSetUser` switches tracking to "with PII" (see [Tracking settings](#tracking-settings)). `getUser()` returns the active user.
|
|
246
|
+
|
|
247
|
+
### Track User with properties
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import { AppticsSetUser, users } from '@zoho_apptics/apptics-js-sdk';
|
|
251
|
+
|
|
252
|
+
const properties = new users.UserBuilder()
|
|
253
|
+
.setFirstName('Ana')
|
|
254
|
+
.setEmailAddress('ana@zylker.com')
|
|
255
|
+
.setPlanType('pro')
|
|
256
|
+
.setCustomProperties({ team: 'design' })
|
|
257
|
+
.build();
|
|
258
|
+
|
|
259
|
+
AppticsSetUser('user-id', 'customer-group-id', properties);
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
The builder also has `setLastName`, `setCompanyName`, `setContactNumber`, `setCountry`, `setRegion`, `setCity`, `setGeoLocation`, `setGender`, `setTimezone`, `setLanguage`, `setEngagementScore` and `setDateOfBirth`. `build()` validates the properties against what the server accepts.
|
|
263
|
+
|
|
264
|
+
## Tracking settings
|
|
265
|
+
|
|
266
|
+
Set what the SDK may track for this visitor:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { setTrackingStatus, AP_TRACKING_STATUS } from '@zoho_apptics/apptics-js-sdk';
|
|
270
|
+
|
|
271
|
+
setTrackingStatus(AP_TRACKING_STATUS.USAGE_TRACKING_WITHOUT_PII);
|
|
9
272
|
```
|
|
10
273
|
|
|
11
|
-
|
|
274
|
+
| Status | Value | Meaning |
|
|
275
|
+
|---|---|---|
|
|
276
|
+
| `USAGE_TRACKING_WITH_PII` | 1 | Track, with the user's identity |
|
|
277
|
+
| `USAGE_TRACKING_WITHOUT_PII` | 4 | Track anonymously (the default) |
|
|
278
|
+
| `NO_TRACKING` | -1 | Track nothing |
|
|
279
|
+
|
|
280
|
+
`getTrackingStatus()` returns the current one.
|
|
12
281
|
|
|
13
|
-
|
|
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.
|
|
282
|
+
### Privacy
|
|
15
283
|
|
|
284
|
+
- The browser's Do Not Track setting is honoured unless `ignore_do_not_track: true` is passed to `Apptics.init()`.
|
|
285
|
+
- Nothing is tracked for known crawlers.
|
|
286
|
+
- Heatmaps name an element from an attribute the author chose (`data-ap-label`, `aria-label`, `name`, `id`, `title`); its visible text is used only with `USAGE_TRACKING_WITH_PII`.
|
|
16
287
|
|
|
17
|
-
##
|
|
288
|
+
## API Tracking
|
|
18
289
|
|
|
19
|
-
|
|
20
|
-
|
|
290
|
+
Record the API calls your app makes, for the console's API page. Either let the SDK wrap
|
|
291
|
+
`fetch` and `XMLHttpRequest`:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
Apptics.init({ ...config, track_api: true }); // record every fetch and XHR call
|
|
21
295
|
```
|
|
22
296
|
|
|
297
|
+
`enableApiTracking(on)` switches it later. Which hosts and endpoints are recorded is set with
|
|
298
|
+
[`AppticsApiTracker.configure`](#configuring-api-tracking).
|
|
299
|
+
|
|
300
|
+
or record calls yourself with `AppticsApiTracker.startTrackApi` / `endTrackApi`, as on Android — the
|
|
301
|
+
SDK times the call between the two:
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
import { AppticsApiTracker } from '@zoho_apptics/apptics-js-sdk';
|
|
305
|
+
|
|
306
|
+
const id = AppticsApiTracker.startTrackApi('https://api.example.com/orders/42', 'GET');
|
|
307
|
+
const response = await fetch('https://api.example.com/orders/42');
|
|
308
|
+
AppticsApiTracker.endTrackApi(id, response.status, response.statusText);
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Between them, automatic tracking and `AppticsApiTracker.startTrackApi` / `endTrackApi` are the
|
|
312
|
+
two ways a call is recorded.
|
|
313
|
+
|
|
314
|
+
### Configuring API tracking
|
|
315
|
+
|
|
316
|
+
Build the rules with `ApiTrackingBuilder` — as `UserBuilder` builds user properties, and with
|
|
317
|
+
the names of Android's `AppticsApiTracker.configure` — and apply them once, before the requests
|
|
318
|
+
they should cover. It works before `Apptics.init()`:
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
import { ApiTrackingBuilder, AppticsApiTracker } from '@zoho_apptics/apptics-js-sdk';
|
|
322
|
+
|
|
323
|
+
AppticsApiTracker.configure(new ApiTrackingBuilder()
|
|
324
|
+
.allowOnlyDomains('api.yourapp.com', 'api.yourapp.in', 'api.yourapp.ae')
|
|
325
|
+
.groupDomains('api.yourapp.*')
|
|
326
|
+
.ignoreEndpoint('/health', '/ping', '/internal/**')
|
|
327
|
+
.addPattern('/v2/catalog/{categoryId}/items/{itemId}')
|
|
328
|
+
.preserveSegments('v1', 'v2')
|
|
329
|
+
.build());
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The rules apply to every call, the ones `track_api` catches and the ones you report yourself.
|
|
333
|
+
|
|
334
|
+
**Path normalization**, on by default, so calls group by endpoint rather than by id:
|
|
335
|
+
|
|
336
|
+
| Called | Recorded as |
|
|
337
|
+
|---|---|
|
|
338
|
+
| `GET /users/42/orders` | `/users/*/orders` (numeric id) |
|
|
339
|
+
| `GET /users/0b9e1f2a-…-0c1d2e3f4a5b/profile` | `/users/*/profile` (UUID) |
|
|
340
|
+
| `DELETE /sessions/eyJhbGciOi….….` | `/sessions/*` (JWT) |
|
|
341
|
+
| `GET /products/search?q=shoes` | `/products/search` (the query string is always removed) |
|
|
342
|
+
|
|
343
|
+
| Builder call | Effect |
|
|
344
|
+
|---|---|
|
|
345
|
+
| `ignoreDomains(...)` / `allowOnlyDomains(...)` | Never record these hosts / record only these |
|
|
346
|
+
| `allowOnlyTLDs('.com', ...)` / `ignoreTLDs('.local', ...)` | Record only / never hosts ending in these |
|
|
347
|
+
| `ignoreDomainPrefixes('dev.', ...)` / `ignoreDomainSuffixes('.cdn.net', ...)` | Never record hosts starting or ending with these |
|
|
348
|
+
| `groupDomains('api.myapp.*', ...)` | Record `api.myapp.com`, `.in`, `.eu`… as one host, `api.myapp.*` |
|
|
349
|
+
| `ignoreEndpoint('/health', '/internal/*', '/users/{id}/x', '/debug/**')` | Never record these paths: exact, `*` or `{name}` for one segment, `**` at the end for everything under it |
|
|
350
|
+
| `addPattern('/v1/accounts/{accountId}')` | Record a matching path as the pattern, each `{name}` as `*`. Tried before auto-detection |
|
|
351
|
+
| `preserveSegments('v1', '404', ...)` | Never turn these segments into `*` |
|
|
352
|
+
| `autoDetection(false)` | Normalize only `addPattern` matches; record every other path as it is |
|
|
353
|
+
|
|
354
|
+
An ignore rule always wins over an allow list. A second `AppticsApiTracker.configure()` replaces the
|
|
355
|
+
first.
|
|
356
|
+
|
|
357
|
+
The SDK's own requests are never recorded, and neither are calls to `localhost` or a bare IP
|
|
358
|
+
address: the server only accepts hostnames with a dot and a letters-only suffix, so the SDK drops
|
|
359
|
+
those calls and says so in the console. `enableApiTracking(false)` removes the wrappers again.
|
|
360
|
+
With a `<script>` tag, `AppticsApiTracker` is a global as it is here, alongside
|
|
361
|
+
`apptics.enableApiTracking()` and `new apptics.ApiTrackingBuilder()`.
|
|
362
|
+
|
|
363
|
+
## Remote Config
|
|
364
|
+
|
|
365
|
+
Read parameters set in the console. Its own entry point, so apps that don't use it don't ship it:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
import { AppticsRemoteConfig } from '@zoho_apptics/apptics-js-sdk/remote-config';
|
|
369
|
+
|
|
370
|
+
// one value, or several with their defaults, as on Android
|
|
371
|
+
const banner = await AppticsRemoteConfig.fetchValue('bannerText'); // null if there is none
|
|
372
|
+
const values = await AppticsRemoteConfig.fetchValues({ primaryColor: '#0b6e4f', maxItems: '10' });
|
|
373
|
+
|
|
374
|
+
// what this device is for a custom criteria the console defines
|
|
375
|
+
AppticsRemoteConfig.setCustomConditionValue('plan_tier', 'gold');
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
Values are text, as the console stores them: `Number(values.maxItems)`,
|
|
379
|
+
`values.showBanner === 'true'` or `JSON.parse(...)` turn them into what the app needs.
|
|
380
|
+
|
|
381
|
+
The SDK fetches once on its own when it starts (or when this module is imported after it has),
|
|
382
|
+
so values are this session's without a call of yours.
|
|
383
|
+
|
|
384
|
+
**Fetching.** `fetchValue` and `fetchValues` reuse a fetch from the last five minutes
|
|
385
|
+
rather than asking the server again, so reading a value on every render costs no requests. Pass
|
|
386
|
+
`coldFetch` (`true` as the last argument) to ask the server regardless; that is limited to three
|
|
387
|
+
calls a minute, and a call past it is answered as a failed fetch. Each also takes a callback,
|
|
388
|
+
as on Android, and returns a Promise either way.
|
|
389
|
+
|
|
390
|
+
**When a fetch fails** (offline, or a server error), they answer with the values fetched before.
|
|
391
|
+
`AppticsRemoteConfig.setFallbackWithOfflineValue(false)` makes them answer with `null` — or the defaults
|
|
392
|
+
you passed to `fetchValues` — instead.
|
|
393
|
+
|
|
394
|
+
**Custom conditions.** A console condition can be on a custom criteria — a key with a list of
|
|
395
|
+
values. `setCustomConditionValue(key, value)` says what this device is for that key, and the
|
|
396
|
+
condition matches when the value is one of the listed ones. Values are kept across visits and
|
|
397
|
+
applied at once, without a fetch; an empty value removes one, and `reset()` clears them. It works
|
|
398
|
+
before `Apptics.init()`.
|
|
399
|
+
|
|
400
|
+
Conditions on the user — the customer group, for one — are re-applied on their own once
|
|
401
|
+
`AppticsSetUser` has registered the user, so the next read answers for them.
|
|
402
|
+
|
|
403
|
+
| Call | Notes |
|
|
404
|
+
|---|---|
|
|
405
|
+
| `fetchValue(name, callback?, coldFetch?)` | One parameter for this session; `null` when the console has none. |
|
|
406
|
+
| `fetchValues({ name: default }, callback?, coldFetch?)` | Several at once; each name gets its default when it has no value. |
|
|
407
|
+
| `setFallbackWithOfflineValue(on)` | Whether a failed fetch answers with the values fetched before (default) or with `null` and defaults. |
|
|
408
|
+
| `setCustomConditionValue(key, value)` | What this device is for a custom criteria; an empty value removes it. |
|
|
409
|
+
|
|
410
|
+
With a `<script>` tag, `AppticsRemoteConfig` is a global as it is here.
|
|
411
|
+
|
|
412
|
+
## Remote Logger
|
|
413
|
+
|
|
414
|
+
Send your app's log lines to the console's Remote Logger page with `AppticsLogger`, as on
|
|
415
|
+
Android: five levels, each taking a tag and a message. The logger is disabled by default; enable
|
|
416
|
+
it first:
|
|
417
|
+
|
|
418
|
+
```ts
|
|
419
|
+
import { AppticsLogger } from '@zoho_apptics/apptics-js-sdk';
|
|
420
|
+
|
|
421
|
+
AppticsLogger.enable();
|
|
422
|
+
|
|
423
|
+
AppticsLogger.v('Boot', 'Verbose');
|
|
424
|
+
AppticsLogger.d('Boot', 'Debug');
|
|
425
|
+
AppticsLogger.i('Checkout', 'Payment started');
|
|
426
|
+
AppticsLogger.w('Checkout', 'Retrying the payment');
|
|
427
|
+
AppticsLogger.e('Checkout', 'Payment failed', { orderId: 42 }, err);
|
|
428
|
+
|
|
429
|
+
AppticsLogger.disable(); // stops it again
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
| Call | Level on the Remote Logger page |
|
|
433
|
+
|---|---|
|
|
434
|
+
| `AppticsLogger.v(tag, message, ...details)` | 1, Verbose |
|
|
435
|
+
| `AppticsLogger.d(tag, message, ...details)` | 2, Debug |
|
|
436
|
+
| `AppticsLogger.i(tag, message, ...details)` | 3, Info |
|
|
437
|
+
| `AppticsLogger.w(tag, message, ...details)` | 4, Warning |
|
|
438
|
+
| `AppticsLogger.e(tag, message, ...details)` | 5, Error |
|
|
439
|
+
| `AppticsLogger.enable()` / `AppticsLogger.disable()` / `AppticsLogger.isEnabled()` | Start, stop, and check the logger |
|
|
440
|
+
|
|
441
|
+
The tag leads the message as `[tag] message`; an empty tag adds nothing. Details are appended to the message: an `Error`
|
|
442
|
+
as its stack, anything else as JSON. Each line records the time it was logged and the function,
|
|
443
|
+
file and line that logged it, even when logged before start-up finishes. Lines are stored before
|
|
444
|
+
they are sent and go out in batches of up to 50: a line survives a failed send and a reload, and
|
|
445
|
+
is removed once the server accepts it. At most 1 MB of lines waits at once, oldest dropped first,
|
|
446
|
+
as on Android, and a line is trimmed to 10,000 characters. Nothing is recorded while the server
|
|
447
|
+
has the remote logger switched off, or while tracking is off. `AppticsLogger.disable()` stops it.
|
|
448
|
+
|
|
449
|
+
Unlike Android, which uploads at the next cold start or on a schedule, lines go out about a
|
|
450
|
+
second after they're logged: a browser tab can close at any moment, and waiting would lose them.
|
|
451
|
+
|
|
452
|
+
To see the lines in the browser console as well while you develop — Android's `showLogs` —
|
|
453
|
+
pass `show_logs: true` to `Apptics.init()`. It prints each line at its level (`console.debug`, `info`,
|
|
454
|
+
`warn`, `error`), with objects left expandable, whether or not the remote logger is enabled.
|
|
455
|
+
`console_log` is a different switch: it logs the SDK's own activity, not your lines.
|
|
456
|
+
|
|
457
|
+
### SDK diagnostics
|
|
458
|
+
|
|
459
|
+
The SDK also reports its own failures to the same page — an event refused for a bad name, a batch
|
|
460
|
+
that never left, a session that couldn't open — tagged with the module it happened in (`Session`,
|
|
461
|
+
`Screen`, `Event`, `API`, `Properties`). It answers "this app reports no data, what is the SDK
|
|
462
|
+
doing out there?" and carries no app data. It is off by default and separate from `AppticsLogger`:
|
|
463
|
+
|
|
464
|
+
```ts
|
|
465
|
+
Apptics.init({ ...config, remote_log: true }); // turn the SDK's own reports on
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## In-App Feedback
|
|
469
|
+
|
|
470
|
+
The feedback form is its own entry point, so apps that don't use it don't download it. With a
|
|
471
|
+
`<script>` tag it is already on `window.apptics`.
|
|
472
|
+
|
|
473
|
+
### Show feedback screen
|
|
474
|
+
|
|
475
|
+
```ts
|
|
476
|
+
import { feedbackOpen, feedbackClose } from '@zoho_apptics/apptics-js-sdk/feedback';
|
|
477
|
+
import '@zoho_apptics/apptics-js-sdk/style.css';
|
|
478
|
+
|
|
479
|
+
feedbackOpen(); // show the form
|
|
480
|
+
feedbackClose(); // close it from your own code, when needed
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
With a bundler, import `style.css` as above. The CDN builds (`apptics.min.js` and
|
|
484
|
+
`feedback.esm.min.js`) load it themselves.
|
|
485
|
+
|
|
486
|
+
The form offers Bug, Feedback, Help and Report, with up to 5 attachments (10 MB total). It offers
|
|
487
|
+
screen recording when the page also loads Zoho Quartz and `Apptics.init()` was given `DC`,
|
|
488
|
+
`quartz_workspace_key` and `quartz_department_key` (see [Configuration](#configuration)).
|
|
489
|
+
Otherwise — or with any of them set to `-` — the recording checkbox and its label are not shown.
|
|
490
|
+
|
|
491
|
+
### Send Feedback/Bug Report programmatically
|
|
492
|
+
|
|
493
|
+
```ts
|
|
494
|
+
import { sendFeedback } from '@zoho_apptics/apptics-js-sdk/feedback';
|
|
495
|
+
import { AP_FEEDBACK_TYPE } from '@zoho_apptics/apptics-js-sdk';
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
await sendFeedback(AP_FEEDBACK_TYPE.BUG, 'Checkout is broken');
|
|
499
|
+
// sent
|
|
500
|
+
} catch (err) {
|
|
501
|
+
// not sent: the SDK hasn't started (no Apptics.init()), the browser is offline, or the server refused it
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
`sendFeedback(type?, message?, attachments?, logFiles?, includeDyninfo?, includeLog?)` returns a
|
|
506
|
+
Promise that resolves once the feedback is sent and rejects when it wasn't. `type` is
|
|
507
|
+
`AP_FEEDBACK_TYPE.BUG` (0), `FEEDBACK` (1), `HELP` (2) or `REPORT` (3).
|
|
508
|
+
|
|
509
|
+
### Attach logs and diagnostics with Feedback
|
|
510
|
+
|
|
511
|
+
```ts
|
|
512
|
+
import { addFeedbackLog, addDiagnosticsInfo } from '@zoho_apptics/apptics-js-sdk';
|
|
513
|
+
|
|
514
|
+
addFeedbackLog('token refresh failed, retrying'); // one timestamped line
|
|
515
|
+
addDiagnosticsInfo('Auth', 'scheme', 'oauth'); // grouped key/value
|
|
516
|
+
addDiagnosticsInfo('Auth', 'expiry', 3600);
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
Both are on the main entry point, so recording context costs nothing in bundle size for apps that
|
|
520
|
+
never open the form. Nothing is sent as it happens: entries wait in storage until feedback is sent,
|
|
521
|
+
and then travel with it as two text files.
|
|
522
|
+
|
|
523
|
+
| File | Holds | Sent when |
|
|
524
|
+
|---|---|---|
|
|
525
|
+
| `feedbacklog.txt` | `addFeedbackLog` lines, in order, each prefixed with its ISO timestamp | There are lines, unless `includeLog` is `false` |
|
|
526
|
+
| `dyninfo.txt` | `addDiagnosticsInfo` entries as `[heading]` blocks, then a `[Device Info]` block | There are diagnostics, or `includeDyninfo` is `true` |
|
|
527
|
+
|
|
528
|
+
Only the entries a submission actually carried are removed once the server has them, so anything
|
|
529
|
+
recorded while a submission is in flight waits for the next one. Entries older than
|
|
530
|
+
`batch_request_timeout` days are left out, at most 1,000 are kept (oldest dropped first), a single
|
|
531
|
+
message is truncated at 10,000 characters, and neither file is sent above 2 MB.
|
|
532
|
+
|
|
533
|
+
## Heatmaps
|
|
534
|
+
|
|
535
|
+
Records where people click and how far they scroll, for the console's heatmaps. Its own entry
|
|
536
|
+
point, so an app that doesn't import it ships none of the capture code.
|
|
537
|
+
|
|
538
|
+
```ts
|
|
539
|
+
import { Apptics } from '@zoho_apptics/apptics-js-sdk';
|
|
540
|
+
import { enableHeatmap } from '@zoho_apptics/apptics-js-sdk/heatmap';
|
|
541
|
+
|
|
542
|
+
Apptics.init({ ...config, track_heatmap: true });
|
|
543
|
+
// or turn it on later: enableHeatmap(true)
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
Importing the module is what makes `track_heatmap` do anything — the flag on its own has nothing
|
|
547
|
+
to switch on. With a `<script>` tag it's already there as `apptics.enableHeatmap()`.
|
|
548
|
+
|
|
549
|
+
| Call | Notes |
|
|
550
|
+
|---|---|
|
|
551
|
+
| `enableHeatmap(on?)` | Starts or stops capture. |
|
|
552
|
+
|
|
553
|
+
Heatmap records go out with the rest of the stored data: on the flush timer, or when you call
|
|
554
|
+
`flush()`.
|
|
555
|
+
|
|
556
|
+
A click gives coordinates, normalized position, a label for the element, and whether it was
|
|
557
|
+
clickable, repeated in frustration (`is_rage_click`) or changed nothing (`is_dead_click`).
|
|
558
|
+
Scrolling is coalesced into one record per gesture — direction, duration, velocity and how far
|
|
559
|
+
down the page it left you. At most `heatmap_max_per_session` records per visit (default 500),
|
|
560
|
+
then capture pauses.
|
|
561
|
+
|
|
562
|
+
## Debugging
|
|
563
|
+
|
|
564
|
+
Two tools, both off by default: debug mode, which you follow in the Apptics web console, and
|
|
565
|
+
console logs in the browser.
|
|
566
|
+
|
|
567
|
+
### Debug mode
|
|
568
|
+
|
|
569
|
+
When a payload isn't showing up in the console, turn on debug mode. It sends engagement,
|
|
570
|
+
exception and log payloads to their `debug/` endpoints, which keep a traceable record of each
|
|
571
|
+
request on the server: follow them in the Apptics web console, along with the request id and the
|
|
572
|
+
link to the server's logs for that request.
|
|
573
|
+
|
|
574
|
+
```ts
|
|
575
|
+
Apptics.init({ ...config, debug: true }); // start in debug mode
|
|
576
|
+
|
|
577
|
+
// or turn it on and off at any time
|
|
578
|
+
import { enableDebug, disableDebug } from '@zoho_apptics/apptics-js-sdk';
|
|
579
|
+
|
|
580
|
+
enableDebug(); // turn debug mode on
|
|
581
|
+
disableDebug(); // and off again
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
`enableDebug()` and `disableDebug()` don't need `Apptics.init()`: call it before `Apptics.init()`, while the SDK starts or at any
|
|
585
|
+
time after, and it takes effect at once. What the app chooses this way holds for the rest of the
|
|
586
|
+
page's life, over `debug` in `Apptics.init()` and through `reset()`, so a page whose config turns
|
|
587
|
+
debug mode on can still switch it off with `disableDebug()`. With a `<script>` tag they are
|
|
588
|
+
`apptics.enableDebug()` and `apptics.disableDebug()`.
|
|
589
|
+
|
|
590
|
+
Debug mode changes where payloads go, not what is in them. It switches itself off after an hour
|
|
591
|
+
with nothing tracked, so a forgotten tab stops tracing on its own.
|
|
592
|
+
|
|
593
|
+
| Key / call | Default | Notes |
|
|
594
|
+
|---|---|---|
|
|
595
|
+
| `debug` | `false` | Start in debug mode, unless the app has already called `enableDebug()` or `disableDebug()`. |
|
|
596
|
+
| `enableDebug()` / `disableDebug()` | | Turn debug mode on or off, with or without `Apptics.init()`. |
|
|
597
|
+
| `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. |
|
|
598
|
+
|
|
599
|
+
### Console logs
|
|
600
|
+
|
|
601
|
+
Log the SDK's activity — what it tracks, stores and sends, and the trace of each debug-mode
|
|
602
|
+
request — to the browser console:
|
|
603
|
+
|
|
604
|
+
```ts
|
|
605
|
+
Apptics.init({ ...config, console_log: true });
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## Configuration
|
|
609
|
+
|
|
610
|
+
Everything `Apptics.init(config)` accepts.
|
|
611
|
+
|
|
612
|
+
| Key | Required | Description |
|
|
613
|
+
|---|---|---|
|
|
614
|
+
| `serviceUrl` | yes | The Apptics host every request goes to, from the Apptics web console (a trailing slash is ignored) |
|
|
615
|
+
| `zak` | yes | The app's token, from the Apptics web console |
|
|
616
|
+
| `identifier` | yes | The app identifier registered in the Apptics web console |
|
|
617
|
+
| `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 |
|
|
618
|
+
| `debug` | | Debug mode: follow requests in the Apptics web console (see [Debugging](#debugging)) |
|
|
619
|
+
| `console_log` | | Log the SDK's activity to the browser console |
|
|
620
|
+
| `ignore_do_not_track` | | Track even when the browser sends Do Not Track |
|
|
621
|
+
| `remote_log` | | `true` makes the SDK report its own failures (see [SDK diagnostics](#sdk-diagnostics)). Off by default |
|
|
622
|
+
| `track_session` | | `false` records no sessions, while events, screens and the rest still are — Android's `automaticSessionsTrackingStatus = false`. On by default |
|
|
623
|
+
| `show_logs` | | Also print the app's `AppticsLogger` lines to the browser console, like Android's `showLogs` (see [Remote Logger](#remote-logger)) |
|
|
624
|
+
| `track_api` | | Record the app's own fetch and XHR calls. Off by default; which hosts and endpoints, through `AppticsApiTracker.configure` (see [API Tracking](#api-tracking)) |
|
|
625
|
+
| `track_heatmap`, `heatmap_max_per_session` | | Record clicks and scrolls (see [Heatmaps](#heatmaps)) |
|
|
626
|
+
| `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)) |
|
|
627
|
+
| `device_name` | | Names this device in debug mode (see [Debugging](#debugging)) |
|
|
628
|
+
| `DC`, `quartz_workspace_key`, `quartz_department_key` | | Enable Quartz screen recording in the feedback form (`DC`: US, EU, IN, AU, CN, CA, JP, SA, AE; `-` disables). The recording checkbox is shown only when all three are set and the page has loaded Quartz |
|
|
629
|
+
|
|
630
|
+
The older snippet names are still accepted: `base_domain` for `serviceUrl` and `token` for `zak`.
|
|
631
|
+
`aaid`, `zsoid` and `projectID` are no longer needed, and are ignored if passed.
|
|
632
|
+
|
|
633
|
+
### Timing
|
|
634
|
+
|
|
635
|
+
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.
|
|
636
|
+
|
|
637
|
+
| Key | Default | Range | Description |
|
|
638
|
+
|---|---|---|---|
|
|
639
|
+
| `batch_flush_interval` | `30000` | 1000 – 3600000 | How often stored data is sent, in milliseconds |
|
|
640
|
+
| `getupdates_interval` | `120000` | 10000 – 86400000 | How often the server's tracking settings are checked, in milliseconds; the server can ask for a longer wait |
|
|
641
|
+
| `batch_size` | `50` | 1 – 500 | Most items in one request (also capped at 1 MB) |
|
|
642
|
+
| `batch_request_timeout` | `7` | 1 – 30 | Days an unsent item is kept before it is dropped |
|
|
643
|
+
| `inactiveduration` | `30` | 1 – 1440 | Idle minutes that end a session |
|
|
644
|
+
| `min_session_duration` | `5000` | 0 – 60000 | Milliseconds a visit must last before its session is recorded; anything tracked sooner records it at once. `0` turns the wait off |
|
|
645
|
+
|
|
646
|
+
Data is also sent when you call `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.
|
|
647
|
+
|
|
648
|
+
## API reference
|
|
649
|
+
|
|
650
|
+
| Call | Notes |
|
|
651
|
+
|---|---|
|
|
652
|
+
| `Apptics.init(config)` | Starts the SDK. Returns `true` when start-up has begun. |
|
|
653
|
+
| `ready()` | Promise that resolves `true` once the SDK is running (`false` if it can't start). |
|
|
654
|
+
| `AppticsEvents.addEvent(name, group?, props?)` / `AppticsEvents.addEvent(DefinedEvents.X, props?)` / `DefinedEvents` | See [Event Tracking](#event-tracking). |
|
|
655
|
+
| `AppticsScreenTracker.inScreen(name, props?)` → id / `AppticsScreenTracker.outScreen(id)` | See [Screen Tracking](#screen-tracking). |
|
|
656
|
+
| `AppticsTrackError(event, props?)` / `enableGlobalErrorHandler(on)` | See [Error Tracking](#error-tracking). |
|
|
657
|
+
| `AppticsSetUser(userid, groupid?, props?)` / `getUser()` / `users.UserBuilder` | See [Track User](#track-user). |
|
|
658
|
+
| `setTrackingStatus(status)` / `getTrackingStatus()` | See [Tracking settings](#tracking-settings). |
|
|
659
|
+
| `AppticsApiTracker.startTrackApi(url, method?)` / `endTrackApi(id, code, message?)` / `configure(rules)` / `ApiTrackingBuilder` / `enableApiTracking(on?)` | See [API Tracking](#api-tracking). |
|
|
660
|
+
| `AppticsLogger.enable()` / `disable()` / `isEnabled()` / `v / d / i / w / e(tag, message, …)` | See [Remote Logger](#remote-logger). |
|
|
661
|
+
| `AppticsRemoteConfig.fetchValue` / `fetchValues` / `setCustomConditionValue` / `setFallbackWithOfflineValue` | From `/remote-config`; see [Remote Config](#remote-config). |
|
|
662
|
+
| `addFeedbackLog(message)` / `addDiagnosticsInfo(heading, key, value?)` | See [Attach logs and diagnostics](#attach-logs-and-diagnostics-with-feedback). |
|
|
663
|
+
| `flush()` | Send stored data now (engagement, errors and heatmap records); it's otherwise sent every 30 s (see [Timing](#timing)). |
|
|
664
|
+
| `enableDebug()` / `disableDebug()` | See [Debugging](#debugging). |
|
|
665
|
+
| `reset()` | Clear stored identity, queued data and remote config values, then start again. |
|
|
666
|
+
|
|
667
|
+
Entry points: `@zoho_apptics/apptics-js-sdk` (everything above), `/feedback`, `/remote-config`
|
|
668
|
+
(see [Remote Config](#remote-config)), `/heatmap` and `/style.css`. Types ship with the package:
|
|
669
|
+
`import type { AppticsConfig, TrackingStatus } from '@zoho_apptics/apptics-js-sdk'`.
|
|
670
|
+
|
|
671
|
+
## Loading from a CDN
|
|
672
|
+
|
|
673
|
+
For `<script type="module">`, load the minified ESM build (a few requests instead of one per module) and call `Apptics.init()` yourself:
|
|
674
|
+
|
|
675
|
+
```html
|
|
676
|
+
<script type="module">
|
|
677
|
+
import { Apptics, AppticsEvents } from 'https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta/dist/apptics.esm.min.js';
|
|
678
|
+
// The feedback form is a separate file, so pages without it don't download it
|
|
679
|
+
import { feedbackOpen } from 'https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta/dist/feedback.esm.min.js';
|
|
680
|
+
|
|
681
|
+
Apptics.init({
|
|
682
|
+
serviceUrl: 'YOUR_SERVICE_URL',
|
|
683
|
+
zak: 'YOUR_TOKEN',
|
|
684
|
+
identifier: 'YOUR_IDENTIFIER',
|
|
685
|
+
appversion: 'YOUR_APPVERSION', // your app's version
|
|
686
|
+
});
|
|
687
|
+
// screens are tracked from the URL automatically
|
|
688
|
+
AppticsEvents.addEvent('signup', 'onboarding', { plan: 'pro' });
|
|
689
|
+
</script>
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
| File | For | Size (gzipped) |
|
|
693
|
+
|---|---|---|
|
|
694
|
+
| `dist/apptics.min.js` | `<script>` pages; sets `window.apptics`, includes every module | ~39 kB |
|
|
695
|
+
| `dist/apptics.esm.min.js` | `<script type="module">`; analytics only (with the `dist/chunks/` files it imports) | ~27 kB |
|
|
696
|
+
| `dist/feedback.esm.min.js` | the feedback form, for module pages | ~11 kB more |
|
|
697
|
+
| `dist/heatmap.esm.min.js` | heatmap capture, for module pages | ~3 kB more |
|
|
698
|
+
| `dist/style.css` | the feedback form's styles; the CDN builds load it themselves | ~3 kB |
|
|
699
|
+
|
|
700
|
+
The module files share one copy of the SDK through `dist/chunks/`, so load them all from the same version and path.
|
|
701
|
+
|
|
702
|
+
unpkg works the same way: `https://unpkg.com/@zoho_apptics/apptics-js-sdk@2.0.0-beta/dist/apptics.min.js`. Add `defer` if you don't need the SDK before the page renders. For a subresource-integrity hash:
|
|
703
|
+
|
|
704
|
+
```bash
|
|
705
|
+
curl -s https://cdn.jsdelivr.net/npm/@zoho_apptics/apptics-js-sdk@2.0.0-beta/dist/apptics.min.js \
|
|
706
|
+
| openssl dgst -sha384 -binary | openssl base64 -A
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
```html
|
|
710
|
+
<script src="…/apptics.min.js" integrity="sha384-…" crossorigin="anonymous"></script>
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
## Upgrading from 1.0.2
|
|
714
|
+
|
|
715
|
+
### What you need to change
|
|
716
|
+
|
|
717
|
+
- **The calls have the Android SDK's names.** Rename them everywhere, router hooks included: a
|
|
718
|
+
call to an old name left in place throws.
|
|
719
|
+
|
|
720
|
+
| 1.0.2 | Now |
|
|
721
|
+
|---|---|
|
|
722
|
+
| `apptics.init(config)` | `Apptics.init(config)` |
|
|
723
|
+
| `apptics.trackEvent(name, group, props)` | `AppticsEvents.addEvent(name, group, props)` |
|
|
724
|
+
| `apptics.trackScreen(name, props)` | `AppticsScreenTracker.inScreen(name, props)`, which also returns the view's id for `AppticsScreenTracker.outScreen(id)` |
|
|
725
|
+
| `apptics.trackError(event, props)` | `AppticsTrackError(event, props)` |
|
|
726
|
+
| `apptics.setUser(userid, groupid, props)` | `AppticsSetUser(userid, groupid, props)` |
|
|
727
|
+
|
|
728
|
+
The rest — `setTrackingStatus`, `getUser`, `flush`, `reset`, the feedback form — keep their names.
|
|
729
|
+
|
|
730
|
+
- **`Apptics.init()` takes new config.** Pass `serviceUrl`, `zak`, `identifier` and `appversion`, all required. The SDK gets `aaid` and the portal and project ids from the server itself, so `aaid`/`aaID`, `zsoid` and `projectID` are no longer needed and are ignored if passed. Without a required key, `Apptics.init()` logs which one is missing and returns `false`.
|
|
731
|
+
- **`appversion` is required.** 1.0.2 quietly defaulted it to `1.0`, which attributed every session, error and crash to a version that was never shipped. Pass your real build version.
|
|
732
|
+
- **npm users can import the SDK.** 1.0.2 exported nothing, so it only worked alongside the console snippet. Now:
|
|
733
|
+
|
|
734
|
+
```ts
|
|
735
|
+
import { Apptics } from '@zoho_apptics/apptics-js-sdk';
|
|
736
|
+
Apptics.init({ serviceUrl, zak, identifier, appversion });
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
- **The feedback form is its own entry point for npm users:** `import { feedbackOpen } from '@zoho_apptics/apptics-js-sdk/feedback'`, plus `import '@zoho_apptics/apptics-js-sdk/style.css'`. `addFeedbackLog()` and `addDiagnosticsInfo()` are on the main entry point. The `<script>` build still has everything on `window.apptics`.
|
|
740
|
+
|
|
741
|
+
### Behaviour you may notice
|
|
742
|
+
|
|
743
|
+
- **`debug: true` now turns on debug mode** (requests followed in the Apptics web console). In 1.0.2 it logged to the browser console; that is `console_log: true` now.
|
|
744
|
+
- When the Apptics server tells the SDK to stop tracking an app version, it stops; 1.0.2 ignored that signal.
|
|
745
|
+
- `ignore_do_not_track: true` now ignores the browser's Do Not Track setting; in 1.0.2 it disabled tracking.
|
|
746
|
+
- `mute()` and `unmute()` are gone. To stop tracking, use `setTrackingStatus(AP_TRACKING_STATUS.NO_TRACKING)`; unlike `mute()`, it is stored across visits.
|
|
747
|
+
- **Screens are tracked automatically**, from the URL, including single-page route changes. An `AppticsScreenTracker.inScreen()` called from your router replaces the automatic view of that navigation, so nothing is counted twice; `track_screen: false` turns automatic tracking off. Each screen view now records how long it lasted.
|
|
748
|
+
- **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.
|
|
749
|
+
- Calling `Apptics.init()` a second time is ignored; use `reset()` to start over.
|
|
750
|
+
- `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.
|
|
751
|
+
- `sendFeedback()` returns a Promise and takes `includeLog` as a sixth argument. `includeLog` and `includeDyninfo` are three-way: `true` always sends that file, `false` never does, and leaving it unset sends it when something is stored. The form's own log and diagnostics toggles and its data viewer are gone.
|
|
752
|
+
- `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.
|
|
753
|
+
- The feedback form works without the Quartz script; the screen-recording option is hidden instead.
|
|
754
|
+
- Clicks tracked with `apeventaction` no longer need jQuery.
|
|
755
|
+
- 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.
|
|
756
|
+
- Queued data is sent when the page is hidden or closed, using `pagehide` / `visibilitychange` instead of `beforeunload`.
|
|
757
|
+
- New, and off until you use them: heatmaps, remote config, API tracking, the app logger, and the SDK's own failure reports to the Remote Logger page (`remote_log: true`). Remote config, once imported, fetches its values on start-up.
|
|
758
|
+
|
|
23
759
|
## License
|
|
24
760
|
|
|
25
|
-
|
|
761
|
+
© 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.
|