@deriv-com/analytics 1.36.0 → 1.38.0

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 (75) hide show
  1. package/README.md +853 -72
  2. package/dist/browser/analytics.bundle.global.js +23 -0
  3. package/dist/browser/analytics.bundle.global.js.map +1 -0
  4. package/dist/browser/analytics.esm.mjs +9 -0
  5. package/dist/browser/analytics.esm.mjs.map +1 -0
  6. package/dist/chunk-3LFZFQL4.mjs +3 -0
  7. package/dist/chunk-3LFZFQL4.mjs.map +1 -0
  8. package/dist/chunk-DNCZM4KN.mjs +4 -0
  9. package/dist/chunk-DNCZM4KN.mjs.map +1 -0
  10. package/dist/chunk-EMF3RT6E.mjs +14 -0
  11. package/dist/chunk-EMF3RT6E.mjs.map +1 -0
  12. package/dist/chunk-HQX3Z7PF.mjs +3 -0
  13. package/dist/chunk-HQX3Z7PF.mjs.map +1 -0
  14. package/dist/chunk-KDJ46GSX.mjs +3 -0
  15. package/dist/chunk-KDJ46GSX.mjs.map +1 -0
  16. package/dist/chunk-LKLVBL24.js +3 -0
  17. package/dist/chunk-LKLVBL24.js.map +1 -0
  18. package/dist/chunk-NOEKD4DT.js +4 -0
  19. package/dist/chunk-NOEKD4DT.js.map +1 -0
  20. package/dist/chunk-NZ35MU4O.js +3 -0
  21. package/dist/chunk-NZ35MU4O.js.map +1 -0
  22. package/dist/growthbook-NJGOOJH4.mjs +3 -0
  23. package/dist/growthbook-NJGOOJH4.mjs.map +1 -0
  24. package/dist/index.d.mts +114 -0
  25. package/dist/index.d.ts +114 -0
  26. package/dist/index.js +4 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/index.mjs +4 -0
  29. package/dist/index.mjs.map +1 -0
  30. package/dist/metafile-cjs.json +1 -0
  31. package/dist/metafile-esm.json +1 -0
  32. package/dist/metafile-iife.json +1 -0
  33. package/dist/posthog-D3QL9y8a.d.ts +95 -0
  34. package/dist/posthog-DGiwrEsA.d.mts +95 -0
  35. package/dist/posthog-P3AXUMLM.mjs +6 -0
  36. package/dist/posthog-P3AXUMLM.mjs.map +1 -0
  37. package/dist/providers/growthbook/index.d.mts +73 -0
  38. package/dist/providers/growthbook/index.d.ts +73 -0
  39. package/dist/providers/growthbook/index.js +3 -0
  40. package/dist/providers/growthbook/index.js.map +1 -0
  41. package/dist/providers/growthbook/index.mjs +3 -0
  42. package/dist/providers/growthbook/index.mjs.map +1 -0
  43. package/dist/providers/posthog/index.d.mts +3 -0
  44. package/dist/providers/posthog/index.d.ts +3 -0
  45. package/dist/providers/posthog/index.js +3 -0
  46. package/dist/providers/posthog/index.js.map +1 -0
  47. package/dist/providers/posthog/index.mjs +3 -0
  48. package/dist/providers/posthog/index.mjs.map +1 -0
  49. package/dist/providers/rudderstack/index.d.mts +73 -0
  50. package/dist/providers/rudderstack/index.d.ts +73 -0
  51. package/dist/providers/rudderstack/index.js +2 -0
  52. package/dist/providers/rudderstack/index.js.map +1 -0
  53. package/dist/providers/rudderstack/index.mjs +2 -0
  54. package/dist/providers/rudderstack/index.mjs.map +1 -0
  55. package/{lib/types.d.ts → dist/types-DoMejCXv.d.mts} +18 -29
  56. package/dist/types-DoMejCXv.d.ts +361 -0
  57. package/dist/utils/analytics-cache/index.d.mts +138 -0
  58. package/dist/utils/analytics-cache/index.d.ts +138 -0
  59. package/dist/utils/analytics-cache/index.js +2 -0
  60. package/dist/utils/analytics-cache/index.js.map +1 -0
  61. package/dist/utils/analytics-cache/index.mjs +2 -0
  62. package/dist/utils/analytics-cache/index.mjs.map +1 -0
  63. package/package.json +97 -68
  64. package/dist/analytics.bundle.js +0 -1
  65. package/lib/analytics.d.ts +0 -66
  66. package/lib/analytics.js +0 -418
  67. package/lib/growthbook.d.ts +0 -29
  68. package/lib/growthbook.js +0 -144
  69. package/lib/index.d.ts +0 -2
  70. package/lib/index.js +0 -5
  71. package/lib/posthog.d.ts +0 -28
  72. package/lib/posthog.js +0 -154
  73. package/lib/rudderstack.d.ts +0 -23
  74. package/lib/rudderstack.js +0 -100
  75. package/lib/types.js +0 -2
package/README.md CHANGED
@@ -1,112 +1,893 @@
1
- # `@deriv-com/analytics`
1
+ # @deriv-com/analytics
2
2
 
3
- The analytics package contains all the utility functions used for tracking user events and sending them to the respective platform such as Rudderstack and GrowthBook.
3
+ A modern, tree-shakeable analytics library for tracking user events with RudderStack and PostHog. Designed for optimal performance with advanced caching, batching, and offline support.
4
4
 
5
- **In this document**
5
+ ## Features
6
6
 
7
- - [Analytics](#analytics)
8
- - [What is Analytics?](#what-is-analytics)
9
- - [Installation and initialisation](#installation)
10
- - [Usage](#usage)
7
+ - 📊 **Multi-Provider Support**: RudderStack for event tracking and PostHog for analytics & session recording
8
+ - 🎄 **Tree-Shakeable**: Only bundle what you use - each provider can be imported independently
9
+ - 📡 **Offline-First**: Automatic event caching when offline with replay on reconnection
10
+ - ⚡ **Performance Optimized**: Batching, deduplication, and SendBeacon API for fast tracking
11
+ - 🔐 **Type-Safe**: Full TypeScript support with discriminated unions for event payloads
12
+ - 🔄 **Backward Compatible**: Supports older React, Node.js, and other legacy package versions
13
+ - 💾 **Advanced Caching**: Cookie-based and in-memory caching for robust event delivery
14
+ - 🎥 **Session Recording**: Built-in PostHog session recording with customizable settings
11
15
 
12
- ### What is Analytics?
16
+ > **Note**: GrowthBook support is deprecated and will be removed in a future major version. For A/B testing and feature flags, we recommend using PostHog's built-in feature flag capabilities.
13
17
 
14
- Cross-project, connected user tracking events with A/B testing features
18
+ ## Table of Contents
15
19
 
16
- ### Installation
20
+ - [Installation](#installation)
21
+ - [NPM/Yarn](#npmyarn)
22
+ - [Browser (CDN)](#browser-cdn)
23
+ - [Quick Start](#quick-start)
24
+ - [Framework Integration](#framework-integration)
25
+ - [React](#react-integration)
26
+ - [Next.js](#nextjs-integration)
27
+ - [Vue.js](#vuejs-integration)
28
+ - [Vanilla JavaScript](#vanilla-javascript)
29
+ - [Configuration](#configuration)
30
+ - [RudderStack](#rudderstack-configuration)
31
+ - [PostHog](#posthog-configuration)
32
+ - [Core API](#core-api)
33
+ - [Initialization](#initialization)
34
+ - [Event Tracking](#event-tracking)
35
+ - [User Identification](#user-identification)
36
+ - [Page Views](#page-views)
37
+ - [User Attributes](#user-attributes)
38
+ - [Caching & Offline Support](#caching--offline-support)
39
+ - [Advanced Usage](#advanced-usage)
40
+ - [API Reference](#api-reference)
41
+ - [Performance](#performance)
42
+ - [Troubleshooting](#troubleshooting)
43
+ - [Migration Guide](#migration-guide)
17
44
 
18
- To install the package, run the following command:
45
+ ## Installation
19
46
 
47
+ ### NPM/Yarn
48
+
49
+ ```bash
50
+ # Using npm
51
+ npm install @deriv-com/analytics
52
+
53
+ # Using yarn
54
+ yarn add @deriv-com/analytics
55
+
56
+ # Using pnpm
57
+ pnpm add @deriv-com/analytics
58
+ ```
59
+
60
+ **Core dependencies** (`@rudderstack/analytics-js`, `js-cookie`, and `posthog-js`) are installed automatically.
61
+
62
+ ### Browser (CDN)
63
+
64
+ Use directly in browsers without a build tool:
65
+
66
+ ```html
67
+ <!-- Load from jsdelivr CDN -->
68
+ <script src="https://cdn.jsdelivr.net/npm/@deriv-com/analytics@latest/dist/browser/analytics.bundle.global.js"></script>
69
+
70
+ <script>
71
+ const { Analytics } = window.DerivAnalytics
72
+
73
+ Analytics.initialise({
74
+ rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
75
+ posthogOptions: {
76
+ apiKey: 'YOUR_POSTHOG_KEY',
77
+ config: {
78
+ autocapture: true,
79
+ },
80
+ },
81
+ }).then(() => {
82
+ Analytics.trackEvent('page_view', { page: 'home' })
83
+ })
84
+ </script>
20
85
  ```
21
- $ npm i @deriv-com/analytics
86
+
87
+ **Bundle Size**: ~380 KB minified / ~125 KB gzipped (includes RudderStack + PostHog + all dependencies)
88
+
89
+ ## Quick Start
90
+
91
+ ### Basic Usage (RudderStack Only)
92
+
93
+ ```typescript
94
+ import { Analytics } from '@deriv-com/analytics'
95
+
96
+ // Initialize with RudderStack
97
+ await Analytics.initialise({
98
+ rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
99
+ })
100
+
101
+ // Track events
102
+ Analytics.trackEvent('ce_virtual_signup_form', {
103
+ action: 'signup_done',
104
+ signup_provider: 'email',
105
+ })
106
+
107
+ // Track page views
108
+ Analytics.pageView('/dashboard', 'Deriv App')
109
+
110
+ // Identify users
111
+ Analytics.identifyEvent('CR123456')
112
+ ```
113
+
114
+ ### Using Both RudderStack and PostHog
115
+
116
+ ```typescript
117
+ import { Analytics } from '@deriv-com/analytics'
118
+
119
+ await Analytics.initialise({
120
+ // RudderStack for event tracking (required)
121
+ rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
122
+
123
+ // PostHog for analytics and session recording (optional)
124
+ posthogOptions: {
125
+ apiKey: 'phc_YOUR_POSTHOG_KEY',
126
+ allowedDomains: ['deriv.com', 'deriv.team', 'deriv.ae'],
127
+ config: {
128
+ session_recording: {
129
+ recordCrossOriginIframes: true,
130
+ minimumDurationMilliseconds: 30000,
131
+ },
132
+ autocapture: true,
133
+ },
134
+ },
135
+ })
136
+
137
+ // Events are automatically sent to both providers
138
+ Analytics.trackEvent('ce_login_form', {
139
+ action: 'login_cta',
140
+ login_provider: 'google',
141
+ })
142
+
143
+ // User identification syncs with both providers
144
+ Analytics.identifyEvent('CR123456', {
145
+ language: 'en',
146
+ country_of_residence: 'US',
147
+ })
22
148
  ```
23
149
 
24
- To proper initialisation of the package, pass proper keys in special function in special for init functions place:
150
+ ## Framework Integration
25
151
 
26
- ```js
27
- Analytics?.initialise({
28
- growthbookKey: process.env.GROWTHBOOK_CLIENT_KEY, // optional key to enable A/B tests
29
- growthbookDecryptionKey: process.env.GROWTHBOOK_DECRYPTION_KEY, // optional key to enable A/B tests
30
- // mandatory key to enable userevent tracking
31
- rudderstackKey: RUDDERSTACK_KEY,
152
+ ### React Integration
32
153
 
33
- growthbookOptions: {
34
- // optional options for overriding growthbook default options
35
- // if you want e.g
36
- antiFlicker: false,
37
- navigateDelay: 0,
38
- antiFlickerTimeout: 3500,
39
- subscribeToChanges: true,
40
- enableDevMode: window?.location.hostname.includes('localhost'),
41
- trackingCallback: (experiment, result) => {
42
- // console.log('Tracking callback', experiment, result)
43
- }
44
- navigate: (url) => window.location.href = url,
154
+ Create an analytics initialization hook:
155
+
156
+ ```typescript
157
+ // hooks/useAnalytics.ts
158
+ import { useEffect } from 'react'
159
+ import { Analytics } from '@deriv-com/analytics'
160
+
161
+ export function useAnalytics() {
162
+ useEffect(() => {
163
+ Analytics.initialise({
164
+ rudderstackKey: process.env.REACT_APP_RUDDERSTACK_KEY!,
165
+ posthogOptions: {
166
+ apiKey: process.env.REACT_APP_POSTHOG_KEY!,
167
+ config: {
168
+ autocapture: true,
169
+ },
170
+ },
171
+ })
172
+ }, [])
173
+ }
174
+
175
+ // App.tsx
176
+ import { useAnalytics } from './hooks/useAnalytics'
177
+
178
+ function App() {
179
+ useAnalytics()
180
+
181
+ const handleSignup = () => {
182
+ Analytics.trackEvent('ce_virtual_signup_form', {
183
+ action: 'signup_modal_open',
184
+ form_source: 'header_cta',
185
+ })
45
186
  }
187
+
188
+ return <button onClick={handleSignup}>Sign Up</button>
189
+ }
190
+ ```
191
+
192
+ ### Next.js Integration
193
+
194
+ #### App Router (Next.js 13+)
195
+
196
+ ```typescript
197
+ // app/providers.tsx
198
+ 'use client'
199
+
200
+ import { Analytics } from '@deriv-com/analytics'
201
+ import { useEffect } from 'react'
202
+
203
+ export function AnalyticsProvider({ children }: { children: React.ReactNode }) {
204
+ useEffect(() => {
205
+ Analytics.initialise({
206
+ rudderstackKey: process.env.NEXT_PUBLIC_RUDDERSTACK_KEY!,
207
+ posthogOptions: {
208
+ apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY!,
209
+ },
210
+ })
211
+ }, [])
212
+
213
+ return <>{children}</>
214
+ }
215
+
216
+ // app/layout.tsx
217
+ import { AnalyticsProvider } from './providers'
218
+
219
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
220
+ return (
221
+ <html>
222
+ <body>
223
+ <AnalyticsProvider>{children}</AnalyticsProvider>
224
+ </body>
225
+ </html>
226
+ )
227
+ }
228
+ ```
229
+
230
+ #### Pages Router (Next.js 12 and below)
231
+
232
+ ```typescript
233
+ // pages/_app.tsx
234
+ import { Analytics } from '@deriv-com/analytics'
235
+ import { useEffect } from 'react'
236
+ import type { AppProps } from 'next/app'
237
+
238
+ export default function App({ Component, pageProps }: AppProps) {
239
+ useEffect(() => {
240
+ Analytics.initialise({
241
+ rudderstackKey: process.env.NEXT_PUBLIC_RUDDERSTACK_KEY!,
242
+ posthogOptions: {
243
+ apiKey: process.env.NEXT_PUBLIC_POSTHOG_KEY!,
244
+ },
245
+ })
246
+ }, [])
247
+
248
+ return <Component {...pageProps} />
249
+ }
250
+ ```
251
+
252
+ ### Vue.js Integration
253
+
254
+ ```typescript
255
+ // main.ts or main.js
256
+ import { createApp } from 'vue'
257
+ import { Analytics } from '@deriv-com/analytics'
258
+ import App from './App.vue'
259
+
260
+ // Initialize analytics
261
+ Analytics.initialise({
262
+ rudderstackKey: import.meta.env.VITE_RUDDERSTACK_KEY,
263
+ posthogOptions: {
264
+ apiKey: import.meta.env.VITE_POSTHOG_KEY,
265
+ },
46
266
  })
267
+
268
+ // Make Analytics available globally
269
+ const app = createApp(App)
270
+ app.config.globalProperties.$analytics = Analytics
271
+ app.mount('#app')
272
+
273
+ // Usage in components
274
+ export default {
275
+ methods: {
276
+ handleClick() {
277
+ this.$analytics.trackEvent('button_clicked', { button_name: 'submit' })
278
+ },
279
+ },
280
+ }
281
+ ```
282
+
283
+ ### Vanilla JavaScript
284
+
285
+ ```html
286
+ <!doctype html>
287
+ <html>
288
+ <head>
289
+ <script src="https://cdn.jsdelivr.net/npm/@deriv-com/analytics@latest/dist/browser/analytics.bundle.global.js"></script>
290
+ </head>
291
+ <body>
292
+ <button id="signup-btn">Sign Up</button>
293
+
294
+ <script>
295
+ const { Analytics } = window.DerivAnalytics
296
+
297
+ // Initialize
298
+ Analytics.initialise({
299
+ rudderstackKey: 'YOUR_KEY',
300
+ posthogOptions: {
301
+ apiKey: 'YOUR_POSTHOG_KEY',
302
+ },
303
+ })
304
+
305
+ // Track button clicks
306
+ document.getElementById('signup-btn').addEventListener('click', () => {
307
+ Analytics.trackEvent('ce_signup_button', {
308
+ action: 'click',
309
+ location: 'header',
310
+ })
311
+ })
312
+ </script>
313
+ </body>
314
+ </html>
47
315
  ```
48
316
 
49
- To make good strategy for A/B testing we need to create some condition depends on data:
317
+ ## Configuration
318
+
319
+ ### RudderStack Configuration
50
320
 
51
- ```js
52
- Analytics?.setAttributes({
53
- user_language: getLanguage(),
54
- device_language: navigator?.language,
55
- country: this.country,
321
+ RudderStack is used for event tracking and includes performance optimizations:
322
+
323
+ ```typescript
324
+ await Analytics.initialise({
325
+ rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
56
326
  })
57
327
  ```
58
328
 
59
- And you finally can use the tracking events and A/B testing features
329
+ **Built-in Performance Features:**
60
330
 
61
- ### Usage
331
+ - **Event Batching**: Flushes after 10 events or 10 seconds
332
+ - **SendBeacon API**: Uses `navigator.sendBeacon` for better performance on page unload
333
+ - **Automatic Retry**: Failed requests are automatically retried
334
+ - **Cookie Management**: Automatic anonymous ID generation and persistence (2-year cookie lifetime)
335
+ - **Offline Support**: Events are cached when offline and replayed when connection is restored
62
336
 
63
- To start using it, let's observe on SDK usage examples:
337
+ ### PostHog Configuration
64
338
 
65
- ```js
66
- import { Analytics } from '@deriv-com/analytics';
339
+ PostHog provides powerful analytics, session recording, and feature flags:
67
340
 
68
- // Tracking features:
69
- Analytics?.identifyEvent() // inentify the user
70
- Analytics?.pageView() // track that page is showed for user
71
- const user_id = Analytics?.getId() // get an user anon or real id
341
+ ```typescript
342
+ await Analytics.initialise({
343
+ rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
344
+ posthogOptions: {
345
+ // Required: API key
346
+ apiKey: 'phc_YOUR_KEY',
72
347
 
348
+ // Optional: Domain allowlist for security
349
+ allowedDomains: ['deriv.com', 'deriv.team', 'deriv.ae'],
73
350
 
74
- // Track Event
75
- Analytics?.trackEvent('ce_virtual_signup_form', {
76
- action: 'open',
77
- form_name: 'default_diel_deriv',
78
- form_source: document?.referrer,
79
- signup_provider: 'email',
351
+ // Optional: PostHog configuration
352
+ config: {
353
+ // API endpoints (defaults shown)
354
+ api_host: 'https://ph.deriv.com',
355
+ ui_host: 'https://us.posthog.com',
356
+
357
+ // Session recording
358
+ session_recording: {
359
+ recordCrossOriginIframes: true,
360
+ maskAllInputs: false,
361
+ minimumDurationMilliseconds: 30000, // Only save sessions longer than 30 seconds
362
+ },
363
+
364
+ // Feature capture
365
+ autocapture: true, // Automatically capture clicks, form submissions, etc.
366
+ capture_pageview: true, // Automatically capture page views
367
+ capture_pageleave: true, // Capture when users leave pages
368
+
369
+ // Console log recording (useful for debugging)
370
+ enable_recording_console_log: true,
371
+
372
+ // Disable features if needed
373
+ disable_session_recording: false,
374
+ disable_surveys: false,
375
+
376
+ // Custom event filtering
377
+ before_send: event => {
378
+ // Custom logic to filter or modify events
379
+ return event
380
+ },
381
+ },
382
+ },
80
383
  })
384
+ ```
81
385
 
82
- // the same as example below, to not to add repetable properties again and again
83
- const analytics_data: Parameters<typeof Analytics.trackEvent>[1] = {
84
- form_name: 'default_diel_deriv',
386
+ #### Domain Allowlisting
387
+
388
+ For security, PostHog can be configured to only send events from specific domains:
389
+
390
+ ```typescript
391
+ posthogOptions: {
392
+ apiKey: 'phc_YOUR_KEY',
393
+ allowedDomains: ['deriv.com', 'deriv.team', 'deriv.ae'],
85
394
  }
86
- Analytics?.trackEvent('ce_virtual_signup_form', {
87
- action: 'open',
88
- signup_provider: 'email',
89
- ...analytics_data
395
+ ```
396
+
397
+ Events from `app.deriv.com`, `staging.deriv.team`, etc. will be sent. Events from other domains will be blocked.
398
+
399
+ #### Session Recording Customization
400
+
401
+ ```typescript
402
+ posthogOptions: {
403
+ apiKey: 'phc_YOUR_KEY',
404
+ config: {
405
+ session_recording: {
406
+ // Record content from iframes
407
+ recordCrossOriginIframes: true,
408
+
409
+ // Mask sensitive input fields
410
+ maskAllInputs: true,
411
+ maskInputOptions: {
412
+ password: true,
413
+ email: true,
414
+ },
415
+
416
+ // Only save sessions longer than 1 minute
417
+ minimumDurationMilliseconds: 60000,
418
+
419
+ // Sampling (record only 50% of sessions)
420
+ sessionRecordingSampleRate: 0.5,
421
+ },
422
+ },
423
+ }
424
+ ```
425
+
426
+ ## Core API
427
+
428
+ ### Initialization
429
+
430
+ Initialize the analytics instance before tracking events:
431
+
432
+ ```typescript
433
+ await Analytics.initialise({
434
+ rudderstackKey: 'YOUR_RUDDERSTACK_KEY',
435
+ posthogOptions: {
436
+ apiKey: 'phc_YOUR_POSTHOG_KEY',
437
+ config: {
438
+ autocapture: true,
439
+ },
440
+ },
441
+ })
442
+ ```
443
+
444
+ ### Event Tracking
445
+
446
+ Track custom events with associated data. Supports both V1 (flat) and V2 (structured) formats:
447
+
448
+ #### V1 Event Format (Flat Structure)
449
+
450
+ ```typescript
451
+ Analytics.trackEvent('ce_login_form', {
452
+ action: 'login_cta',
453
+ login_provider: 'email',
454
+ form_name: 'main_login',
455
+ })
456
+ ```
457
+
458
+ #### V2 Event Format (Structured with Metadata)
459
+
460
+ ```typescript
461
+ Analytics.trackEvent('ce_get_start_page', {
462
+ action: 'click',
463
+ form_name: 'signup_form',
464
+ cta_information: {
465
+ cta_name: 'get_started',
466
+ section_name: 'hero',
467
+ },
468
+ event_metadata: {
469
+ page_name: '/home',
470
+ user_language: 'en',
471
+ },
472
+ })
473
+ ```
474
+
475
+ ### User Identification
476
+
477
+ Identify users and sync traits across analytics providers:
478
+
479
+ #### Simple Identification
480
+
481
+ ```typescript
482
+ // Identify user with ID only
483
+ Analytics.identifyEvent('CR123456')
484
+ ```
485
+
486
+ #### Identification with Custom Traits
487
+
488
+ ```typescript
489
+ // Send same traits to both RudderStack and PostHog
490
+ Analytics.identifyEvent('CR123456', {
491
+ language: 'en',
492
+ country_of_residence: 'US',
493
+ account_type: 'real',
494
+ })
495
+
496
+ // Send different traits to each provider
497
+ Analytics.identifyEvent('CR123456', {
498
+ rudderstack: {
499
+ language: 'en',
500
+ custom_field: 'value',
501
+ },
502
+ posthog: {
503
+ language: 'en',
504
+ country_of_residence: 'US',
505
+ subscription_tier: 'premium',
506
+ },
507
+ })
508
+ ```
509
+
510
+ **How it works:**
511
+
512
+ - If you pass a simple object (e.g., `{ language: 'en' }`), the same traits are sent to both providers
513
+ - If you pass an object with `rudderstack` or `posthog` keys, provider-specific traits are used
514
+ - Queues identify calls if provider not yet initialized
515
+ - PostHog automatically handles aliasing between anonymous and identified users
516
+
517
+ ### Page Views
518
+
519
+ Track page navigation:
520
+
521
+ ```typescript
522
+ // Basic page view
523
+ Analytics.pageView('/dashboard')
524
+
525
+ // With custom platform name
526
+ Analytics.pageView('/dashboard', 'Deriv Trader')
527
+
528
+ // With additional properties
529
+ Analytics.pageView('/trade', 'Deriv App', {
530
+ section: 'multipliers',
531
+ instrument: 'forex',
532
+ })
533
+ ```
534
+
535
+ **Note**: PostHog automatically captures page views when `capture_pageview: true` is set in config. Manual page view tracking is primarily for RudderStack.
536
+
537
+ ### User Attributes
538
+
539
+ Set user and context attributes that are automatically included in all events:
540
+
541
+ ```typescript
542
+ Analytics.setAttributes({
543
+ country: 'US',
544
+ user_language: 'en',
545
+ account_type: 'real',
546
+ device_type: 'mobile',
547
+ account_currency: 'USD',
548
+ account_mode: 'demo',
549
+ residence_country: 'US',
550
+ loggedIn: true,
551
+ })
552
+ ```
553
+
554
+ **All subsequent events will include these attributes automatically.**
555
+
556
+ ### Reset User Session
557
+
558
+ Clear user session from all providers (e.g., on logout):
559
+
560
+ ```typescript
561
+ Analytics.reset()
562
+ ```
563
+
564
+ ## Caching & Offline Support
565
+
566
+ The package includes robust caching mechanisms to ensure no events are lost:
567
+
568
+ ### Cookie-Based Caching
569
+
570
+ Events are cached in cookies when:
571
+
572
+ - **RudderStack SDK hasn't loaded yet** - Events are stored and replayed once the SDK initializes
573
+ - **User is offline** - Events are queued and sent when connection is restored
574
+
575
+ ```typescript
576
+ // Automatic - no configuration needed
577
+ Analytics.trackEvent('button_clicked', { button: 'submit' })
578
+ // ↓ If offline or SDK not ready, stored in cookies
579
+ // ↓ Automatically sent when online/SDK ready
580
+ ```
581
+
582
+ **Technical Details:**
583
+
584
+ - Cookies have a 7-day expiration
585
+ - Maximum 10 cached events (oldest events dropped if exceeded)
586
+ - Automatic cleanup after successful replay
587
+ - SameSite=Lax for security
588
+
589
+ ### In-Memory Caching
590
+
591
+ In addition to cookie caching, events are cached in memory when the user is offline but the SDK is initialized:
592
+
593
+ ```typescript
594
+ // While offline
595
+ Analytics.trackEvent('offline_event', { data: 'cached' })
596
+ // ↓ Stored in memory
597
+ // ↓ Sent immediately when online
598
+
599
+ // Check offline status
600
+ window.addEventListener('online', () => {
601
+ // Cached events automatically sent
602
+ })
603
+ ```
604
+
605
+ ### Page View Caching
606
+
607
+ Page views are also cached using the same mechanism:
608
+
609
+ ```typescript
610
+ // While SDK is loading
611
+ Analytics.pageView('/dashboard')
612
+ // ↓ Cached in cookies
613
+ // ↓ Replayed once SDK is ready
614
+ ```
615
+
616
+ ### Advanced Caching Utilities
617
+
618
+ For complex scenarios requiring more control, use the advanced caching utilities:
619
+
620
+ ```typescript
621
+ import { cacheTrackEvents } from '@deriv-com/analytics'
622
+
623
+ // Track events with automatic caching before SDK loads
624
+ cacheTrackEvents.track({
625
+ name: 'ce_login_form',
626
+ properties: { action: 'open' },
90
627
  })
91
- Analytics?.trackEvent('ce_virtual_signup_form', {
92
- action: 'close',
93
- signup_provider: 'google',
94
- ...analytics_data
628
+
629
+ // Add click event listeners with auto-retry
630
+ cacheTrackEvents.addEventHandler([
631
+ {
632
+ element: '.signup-button',
633
+ event: {
634
+ name: 'ce_button_click',
635
+ properties: { button_name: 'signup' },
636
+ },
637
+ cache: true, // Cache if SDK not ready
638
+ },
639
+ ])
640
+
641
+ // Track page-specific events
642
+ cacheTrackEvents.pageLoadEvent([
643
+ {
644
+ pages: ['dashboard', 'profile'],
645
+ event: {
646
+ name: 'ce_page_load',
647
+ properties: { page_type: 'authenticated' },
648
+ },
649
+ },
650
+ ])
651
+
652
+ // Automatic pageview tracking
653
+ cacheTrackEvents.pageView()
654
+ ```
655
+
656
+ ## Advanced Usage
657
+
658
+ ### Independent Package Usage
659
+
660
+ Each provider can be used independently for maximum flexibility:
661
+
662
+ #### PostHog Only
663
+
664
+ ```typescript
665
+ import { Posthog } from '@deriv-com/analytics/posthog'
666
+
667
+ const posthog = Posthog.getPosthogInstance({
668
+ apiKey: 'phc_YOUR_KEY',
669
+ allowedDomains: ['deriv.com'],
670
+ config: {
671
+ autocapture: true,
672
+ session_recording: {
673
+ recordCrossOriginIframes: true,
674
+ },
675
+ },
95
676
  })
96
677
 
97
- // A/B testing features
98
- const test_toggle_aa_test = Analytics?.getFeatureState('test-toggle-aa-test') // returns value of experiment
99
- const common_test = Analytics?.getFeatureValue('common-test', 'fallback') // returns feature flag's boolen
678
+ // Track events
679
+ posthog.capture('button_clicked', { button_name: 'signup' })
680
+
681
+ // Identify users
682
+ posthog.identifyEvent('CR123', { language: 'en' })
683
+
684
+ // Check feature flags
685
+ const isEnabled = posthog.isFeatureEnabled('new-feature')
686
+ const variant = posthog.getFeatureFlag('button-color')
100
687
  ```
101
688
 
102
- If you need to get entire instance directly:
689
+ #### RudderStack Only
690
+
691
+ ```typescript
692
+ import { RudderStack } from '@deriv-com/analytics/rudderstack'
693
+
694
+ const rudderstack = RudderStack.getRudderStackInstance('YOUR_KEY', () => {
695
+ console.log('RudderStack loaded')
696
+ })
697
+
698
+ // Track events
699
+ rudderstack.track('button_clicked', { button: 'signup' })
103
700
 
104
- ```js
105
- const { ab, tracking } = Analytics?.getInstances()
701
+ // Identify users
702
+ rudderstack.identifyEvent('CR123', { language: 'en' })
703
+
704
+ // Track page views
705
+ rudderstack.pageView('/dashboard', 'Deriv App', 'CR123')
106
706
  ```
107
707
 
108
- If you want to check your ID
708
+ ### Access Provider Instances
709
+
710
+ Access raw provider instances for advanced use cases:
109
711
 
110
- ```js
111
- window.getMyId()
712
+ ```typescript
713
+ const { tracking, posthog } = Analytics.getInstances()
714
+
715
+ // Access PostHog directly
716
+ if (posthog?.has_initialized) {
717
+ posthog.capture('custom_event', { property: 'value' })
718
+
719
+ // Access PostHog feature flags
720
+ const isEnabled = posthog.isFeatureEnabled('new-feature')
721
+ }
722
+
723
+ // Access RudderStack directly
724
+ if (tracking?.has_initialized) {
725
+ const userId = tracking.getUserId()
726
+ const anonId = tracking.getAnonymousId()
727
+ }
112
728
  ```
729
+
730
+ ## API Reference
731
+
732
+ ### `initialise(options: Options): Promise<void>`
733
+
734
+ Initialize the analytics instance.
735
+
736
+ **Parameters:**
737
+
738
+ ```typescript
739
+ interface Options {
740
+ rudderstackKey?: string
741
+ posthogOptions?: {
742
+ apiKey: string
743
+ allowedDomains?: string[]
744
+ config?: PostHogConfig
745
+ }
746
+ }
747
+ ```
748
+
749
+ ### `trackEvent<T>(event: T, payload: TAllEvents[T]): void`
750
+
751
+ Track a typed event.
752
+
753
+ ### `pageView(url: string, platform?: string, properties?: Record<string, unknown>): void`
754
+
755
+ Track page navigation.
756
+
757
+ ### `identifyEvent(userId?: string, traits?: Record<string, any>): void`
758
+
759
+ Link anonymous session to a user ID with optional traits.
760
+
761
+ ### `setAttributes(attributes: TCoreAttributes): void`
762
+
763
+ Update user attributes that flow to all providers.
764
+
765
+ ### `reset(): void`
766
+
767
+ Clear user session from all providers.
768
+
769
+ ### `getId(): string`
770
+
771
+ Get the current user ID.
772
+
773
+ ### `getAnonymousId(): string`
774
+
775
+ Get the anonymous user ID.
776
+
777
+ ### `getInstances(): { tracking, posthog }`
778
+
779
+ Access raw provider instances.
780
+
781
+ ## Performance
782
+
783
+ ### Benchmarks
784
+
785
+ - **Event tracking**: <5ms (average)
786
+ - **Page view tracking**: <3ms (average)
787
+ - **Initialization**: ~200ms (with both providers)
788
+ - **Offline cache replay**: <50ms for 10 events
789
+
790
+ ### Optimizations
791
+
792
+ - **Tree-Shaking**: Unused providers completely removed from bundle
793
+ - **Lazy Loading**: PostHog loaded dynamically only when configured
794
+ - **Event Batching**: RudderStack batches events (10 events or 10 seconds)
795
+ - **SendBeacon**: Uses `navigator.sendBeacon` for reliable event delivery on page unload
796
+ - **Deduplication**: Prevents duplicate events from being sent
797
+
798
+ ### Bundle Sizes
799
+
800
+ Estimated sizes (minified + gzipped):
801
+
802
+ - **Core (RudderStack + PostHog)**: ~32 KB
803
+ - **RudderStack Only**: ~18 KB
804
+ - **PostHog Only**: ~20 KB
805
+ - **Browser Bundle (all included)**: ~125 KB gzipped
806
+
807
+ ## Troubleshooting
808
+
809
+ ### Events not appearing in RudderStack
810
+
811
+ 1. **Verify API key**: Check that `rudderstackKey` is correct
812
+ 2. **Check network requests**: Open DevTools → Network tab → Look for requests to RudderStack dataplane
813
+ 3. **Verify initialization**: Run `Analytics.getInstances().tracking.has_initialized` in console
814
+ 4. **Check batching**: Events are batched - wait ~10 seconds or send 10 events
815
+
816
+ ### PostHog not receiving events
817
+
818
+ 1. **Verify API key**: Check that PostHog API key is correct (starts with `phc_`)
819
+ 2. **Check domain allowlist**: Verify your domain is in the `allowedDomains` list
820
+ 3. **Check initialization**: Run `Analytics.getInstances().posthog?.has_initialized` in console
821
+ 4. **Verify network requests**: Check DevTools for requests to `ph.deriv.com` or your PostHog host
822
+ 5. **Check browser console**: Look for PostHog errors or warnings
823
+
824
+ ### Session recording not working
825
+
826
+ 1. **Verify config**: Ensure `disable_session_recording: false` (or omit it)
827
+ 2. **Check minimum duration**: Sessions shorter than `minimumDurationMilliseconds` are not saved
828
+ 3. **Verify domain**: Check that PostHog is initialized and domain is allowed
829
+ 4. **Check PostHog dashboard**: Recordings may take a few minutes to appear
830
+
831
+ ### Events being cached but not sent
832
+
833
+ 1. **Check online status**: Run `navigator.onLine` in console
834
+ 2. **Verify SDK loaded**: Run `Analytics.getInstances().tracking.has_initialized`
835
+ 3. **Check cookies**: Look for `rudder_*` and `analytics_cached_*` cookies in DevTools
836
+ 4. **Clear cache manually**: Clear cookies or run `Analytics.reset()`
837
+
838
+ ## Migration Guide
839
+
840
+ ### From v1.x to v2.x
841
+
842
+ #### Breaking Changes
843
+
844
+ 1. **identifyEvent signature changed**:
845
+
846
+ ```typescript
847
+ // Old (v1.x) - hardcoded traits
848
+ Analytics.identifyEvent('CR123')
849
+
850
+ // New (v2.x) - custom traits
851
+ Analytics.identifyEvent('CR123', {
852
+ language: 'en',
853
+ country_of_residence: 'US',
854
+ })
855
+
856
+ // Or provider-specific
857
+ Analytics.identifyEvent('CR123', {
858
+ rudderstack: { language: 'en' },
859
+ posthog: { language: 'en', country_of_residence: 'US' },
860
+ })
861
+ ```
862
+
863
+ 2. **GrowthBook deprecated**: Migrate to PostHog feature flags
864
+
865
+ ```typescript
866
+ // Old (GrowthBook)
867
+ const isEnabled = Analytics.isFeatureOn('new-feature')
868
+
869
+ // New (PostHog)
870
+ const { posthog } = Analytics.getInstances()
871
+ const isEnabled = posthog?.isFeatureEnabled('new-feature')
872
+ ```
873
+
874
+ ## Contributing
875
+
876
+ Contributions are welcome! Please follow these guidelines:
877
+
878
+ 1. Fork the repository
879
+ 2. Create a feature branch
880
+ 3. Write tests for your changes
881
+ 4. Run `npm test` and `npm run build`
882
+ 5. Submit a pull request
883
+
884
+ ## License
885
+
886
+ MIT
887
+
888
+ ## Support
889
+
890
+ For issues and questions:
891
+
892
+ - **GitHub Issues**: https://github.com/binary-com/deriv-analytics/issues
893
+ - **Documentation**: https://github.com/binary-com/deriv-analytics