@behindthescenes/analytics 0.0.9 → 0.0.10
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/README.md +480 -162
- package/dist/manifest.json +2 -2
- package/docs/advanced-setup.md +499 -0
- package/docs/api-reference.md +498 -0
- package/docs/frameworks.md +936 -0
- package/package.json +2 -1
package/dist/manifest.json
CHANGED
|
@@ -0,0 +1,499 @@
|
|
|
1
|
+
# Advanced Setup Guide
|
|
2
|
+
|
|
3
|
+
Advanced configuration and use cases for the BTS Analytics SDK.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Custom Endpoint](#custom-endpoint)
|
|
8
|
+
- [Request Signing](#request-signing)
|
|
9
|
+
- [Google Analytics Integration](#google-analytics-integration)
|
|
10
|
+
- [Attribution Control](#attribution-control)
|
|
11
|
+
- [SPA Configuration](#spa-configuration)
|
|
12
|
+
- [Custom Auto-Tracking](#custom-auto-tracking)
|
|
13
|
+
- [Error Handling](#error-handling)
|
|
14
|
+
- [Performance Optimization](#performance-optimization)
|
|
15
|
+
- [Privacy and Compliance](#privacy-and-compliance)
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Custom Endpoint
|
|
20
|
+
|
|
21
|
+
Override the default analytics endpoint for local development or custom proxies.
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
const analytics = createBTSAnalytics({
|
|
25
|
+
siteKey: "your-public-site-key",
|
|
26
|
+
endpoint: "https://your-proxy.com/v2/website/analytics",
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Note:** Production use should use the default endpoint. Only override for specific development or proxy scenarios.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Request Signing
|
|
35
|
+
|
|
36
|
+
Add custom headers for request validation and security.
|
|
37
|
+
|
|
38
|
+
### Basic Header Addition
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const analytics = createBTSAnalytics({
|
|
42
|
+
siteKey: "your-public-site-key",
|
|
43
|
+
requestHeaders: {
|
|
44
|
+
"X-Custom-Header": "value",
|
|
45
|
+
"X-Source": "my-website",
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Important:** When `requestHeaders` is configured, the SDK uses `fetch(..., { keepalive: true })` instead of `sendBeacon` for page-unload events.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Google Analytics Integration
|
|
55
|
+
|
|
56
|
+
### Detection Mode (Default)
|
|
57
|
+
|
|
58
|
+
The SDK automatically detects existing GA4 installations:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
const analytics = createBTSAnalytics({
|
|
62
|
+
siteKey: "your-public-site-key",
|
|
63
|
+
// GA detection is automatic, no configuration needed
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Detected GA data is included with every event:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"ga_client_id": "123456789.987654321",
|
|
72
|
+
"ga_tag_installed": true,
|
|
73
|
+
"googleAnalytics": {
|
|
74
|
+
"tagInstalled": true,
|
|
75
|
+
"clientId": "123456789.987654321",
|
|
76
|
+
"measurementId": "G-ABC123"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Proxy Mode
|
|
82
|
+
|
|
83
|
+
Load GA tag for scanner visibility while forwarding events through BTS:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const analytics = createBTSAnalytics({
|
|
87
|
+
siteKey: "your-public-site-key",
|
|
88
|
+
googleAnalytics: {
|
|
89
|
+
loadTag: true,
|
|
90
|
+
measurementId: "G-XXXXXXXXXX",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
This:
|
|
96
|
+
1. Loads the Google tag script with `send_page_view: false`
|
|
97
|
+
2. Allows GA scanners to detect an installed tag
|
|
98
|
+
3. Forwards all events server-side through BTS
|
|
99
|
+
|
|
100
|
+
### Disable GA Detection
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
const analytics = createBTSAnalytics({
|
|
104
|
+
siteKey: "your-public-site-key",
|
|
105
|
+
googleAnalytics: false,
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Attribution Control
|
|
112
|
+
|
|
113
|
+
### Understanding Attribution
|
|
114
|
+
|
|
115
|
+
The SDK automatically captures and persists:
|
|
116
|
+
|
|
117
|
+
**UTM Parameters:**
|
|
118
|
+
- `utm_source`, `utm_medium`, `utm_campaign`
|
|
119
|
+
- `utm_term`, `utm_content`
|
|
120
|
+
|
|
121
|
+
**Click IDs:**
|
|
122
|
+
- `fbclid`, `gclid`, `gbraid`, `wbraid`
|
|
123
|
+
- `li_fat_id`, `msclkid`, `ttclid`
|
|
124
|
+
|
|
125
|
+
**Cookies:**
|
|
126
|
+
- `_fbp`, `_fbc`
|
|
127
|
+
|
|
128
|
+
### Attribution Data Structure
|
|
129
|
+
|
|
130
|
+
Attribution data is automatically attached to every event:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"attribution": {
|
|
135
|
+
"utm": {
|
|
136
|
+
"utm_source": "facebook",
|
|
137
|
+
"utm_medium": "paid",
|
|
138
|
+
"utm_campaign": "summer_sale"
|
|
139
|
+
},
|
|
140
|
+
"clickIds": {
|
|
141
|
+
"fbclid": "abc123",
|
|
142
|
+
"fbp": "fb.1.1234567890.987654321"
|
|
143
|
+
},
|
|
144
|
+
"landingUrl": "https://yoursite.com/landing?utm_source=facebook",
|
|
145
|
+
"referrer": "https://facebook.com"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Custom Attribution
|
|
151
|
+
|
|
152
|
+
Add custom attribution data to specific events:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
analytics.track("custom_campaign_click", {
|
|
156
|
+
attribution: {
|
|
157
|
+
utm: {
|
|
158
|
+
utm_source: "newsletter",
|
|
159
|
+
utm_medium: "email",
|
|
160
|
+
},
|
|
161
|
+
customCampaignId: "campaign_123",
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## SPA Configuration
|
|
169
|
+
|
|
170
|
+
### React Router / Vue Router / etc.
|
|
171
|
+
|
|
172
|
+
For frameworks with client-side routing, disable automatic pageviews and handle manually:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const analytics = createBTSAnalytics({
|
|
176
|
+
siteKey: "your-public-site-key",
|
|
177
|
+
autoPageviews: false,
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### React Router v6
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
import { useEffect } from "react";
|
|
185
|
+
import { useLocation } from "react-router-dom";
|
|
186
|
+
|
|
187
|
+
function AnalyticsRouteTracker() {
|
|
188
|
+
const location = useLocation();
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
analytics.page(location.pathname + location.search);
|
|
192
|
+
}, [location]);
|
|
193
|
+
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Vue Router
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { watch } from "vue";
|
|
202
|
+
import { useRoute } from "vue-router";
|
|
203
|
+
|
|
204
|
+
const route = useRoute();
|
|
205
|
+
|
|
206
|
+
watch(
|
|
207
|
+
() => route.fullPath,
|
|
208
|
+
(fullPath) => {
|
|
209
|
+
analytics.page(fullPath);
|
|
210
|
+
},
|
|
211
|
+
{ immediate: true }
|
|
212
|
+
);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Selective Auto-Tracking
|
|
216
|
+
|
|
217
|
+
Enable only specific auto-tracking features:
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
const analytics = createBTSAnalytics({
|
|
221
|
+
siteKey: "your-public-site-key",
|
|
222
|
+
autoTrack: {
|
|
223
|
+
pageviews: false, // Disable - handle manually
|
|
224
|
+
history: false, // Disable SPA hooks
|
|
225
|
+
outboundLinks: true, // Enable external link tracking
|
|
226
|
+
buttonClicks: true, // Enable button tracking
|
|
227
|
+
formSubmissions: true, // Enable form tracking
|
|
228
|
+
search: true, // Enable search tracking
|
|
229
|
+
viewContent: true, // Enable content view tracking
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Custom Auto-Tracking
|
|
237
|
+
|
|
238
|
+
### Track Specific Elements Only
|
|
239
|
+
|
|
240
|
+
Use CSS selectors or data attributes for precise tracking:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// Track only specific buttons
|
|
244
|
+
analytics.track("cta_clicked", {
|
|
245
|
+
buttonId: "buy-now",
|
|
246
|
+
section: "pricing",
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// Track with custom attributes
|
|
250
|
+
document.querySelectorAll("[data-track-custom]").forEach((el) => {
|
|
251
|
+
el.addEventListener("click", () => {
|
|
252
|
+
const eventName = el.getAttribute("data-track-custom");
|
|
253
|
+
analytics.track(eventName, {
|
|
254
|
+
elementId: el.id,
|
|
255
|
+
context: el.getAttribute("data-context"),
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Dynamic Content Views
|
|
262
|
+
|
|
263
|
+
For dynamically loaded content (infinite scroll, etc.):
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
// The SDK automatically observes DOM changes via MutationObserver
|
|
267
|
+
// Just add the data attribute to new elements:
|
|
268
|
+
const newCard = document.createElement("article");
|
|
269
|
+
newCard.setAttribute("data-bts-view-content", `video_${videoId}`);
|
|
270
|
+
newCard.setAttribute("data-bts-content-type", "video");
|
|
271
|
+
newCard.setAttribute("data-bts-content-title", videoTitle);
|
|
272
|
+
container.appendChild(newCard);
|
|
273
|
+
// SDK will automatically start tracking visibility
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Error Handling
|
|
279
|
+
|
|
280
|
+
### Request Errors
|
|
281
|
+
|
|
282
|
+
The SDK automatically handles network errors:
|
|
283
|
+
|
|
284
|
+
1. Failed batches are requeued
|
|
285
|
+
2. Retry is scheduled
|
|
286
|
+
3. Events are preserved in memory
|
|
287
|
+
|
|
288
|
+
### Custom Error Handling
|
|
289
|
+
|
|
290
|
+
```typescript
|
|
291
|
+
// For contact form submissions
|
|
292
|
+
try {
|
|
293
|
+
await analytics.submitContactForm({
|
|
294
|
+
locationId: "loc-123",
|
|
295
|
+
email: "fan@example.com",
|
|
296
|
+
});
|
|
297
|
+
} catch (error) {
|
|
298
|
+
console.error("Form submission failed:", error);
|
|
299
|
+
// Show user-friendly error message
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// For journey operations
|
|
303
|
+
try {
|
|
304
|
+
const { journeyToken } = await analytics.startFunnel();
|
|
305
|
+
} catch (error) {
|
|
306
|
+
console.error("Failed to start funnel:", error);
|
|
307
|
+
// Continue without attribution
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Debug Mode
|
|
312
|
+
|
|
313
|
+
Enable debug logging to troubleshoot:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const analytics = createBTSAnalytics({
|
|
317
|
+
siteKey: "your-public-site-key",
|
|
318
|
+
debug: true, // Logs all events and network requests
|
|
319
|
+
});
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Performance Optimization
|
|
325
|
+
|
|
326
|
+
### Batch Size
|
|
327
|
+
|
|
328
|
+
Events are batched automatically (flush every 300ms or 50 events). For high-traffic sites:
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
const analytics = createBTSAnalytics({
|
|
332
|
+
siteKey: "your-public-site-key",
|
|
333
|
+
flushIntervalMs: 500, // Increase interval for more batching
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Lazy Loading
|
|
338
|
+
|
|
339
|
+
Load the SDK only when needed:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Dynamic import for code splitting
|
|
343
|
+
async function initAnalytics() {
|
|
344
|
+
const { createBTSAnalytics } = await import("@behindthescenes/analytics");
|
|
345
|
+
return createBTSAnalytics({
|
|
346
|
+
siteKey: "your-public-site-key",
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Conditional Tracking
|
|
352
|
+
|
|
353
|
+
Skip tracking for bots or specific conditions:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
const isBot = /bot|crawler|spider/i.test(navigator.userAgent);
|
|
357
|
+
const isPrerender = navigator.userAgent.includes("Prerender");
|
|
358
|
+
|
|
359
|
+
if (!isBot && !isPrerender) {
|
|
360
|
+
const analytics = createBTSAnalytics({
|
|
361
|
+
siteKey: "your-public-site-key",
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Privacy and Compliance
|
|
369
|
+
|
|
370
|
+
### GDPR / CCPA Compliance
|
|
371
|
+
|
|
372
|
+
The SDK respects user privacy preferences:
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
// Check for consent before initializing
|
|
376
|
+
const hasConsent = localStorage.getItem("analytics_consent") === "true";
|
|
377
|
+
|
|
378
|
+
if (hasConsent) {
|
|
379
|
+
const analytics = createBTSAnalytics({
|
|
380
|
+
siteKey: "your-public-site-key",
|
|
381
|
+
});
|
|
382
|
+
} else {
|
|
383
|
+
// Load with minimal tracking or don't load at all
|
|
384
|
+
const analytics = createBTSAnalytics({
|
|
385
|
+
siteKey: "your-public-site-key",
|
|
386
|
+
autoTrack: false, // Disable all auto-tracking
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Consent Management
|
|
392
|
+
|
|
393
|
+
Integrate with your consent management platform:
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
// Example: OneTrust integration
|
|
397
|
+
function onConsentChange(consent) {
|
|
398
|
+
if (consent.analytics) {
|
|
399
|
+
// Initialize or re-enable analytics
|
|
400
|
+
window.analytics = createBTSAnalytics({
|
|
401
|
+
siteKey: "your-public-site-key",
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
// Destroy analytics instance
|
|
405
|
+
window.analytics?.destroy();
|
|
406
|
+
window.analytics = null;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Data Minimization
|
|
412
|
+
|
|
413
|
+
Send only necessary data:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
analytics.track("purchase", {
|
|
417
|
+
orderId: "order_123",
|
|
418
|
+
monetaryValue: 99,
|
|
419
|
+
currency: "USD",
|
|
420
|
+
// Don't include: user email, full name, address, etc.
|
|
421
|
+
});
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
### PII Handling
|
|
425
|
+
|
|
426
|
+
Never send personally identifiable information in tracking calls:
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
// ❌ Bad - includes PII
|
|
430
|
+
analytics.track("form_submit", {
|
|
431
|
+
email: "john@example.com",
|
|
432
|
+
phone: "+1234567890",
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// ✅ Good - use userId only
|
|
436
|
+
analytics.identify("user_123", {
|
|
437
|
+
// Optional traits - keep minimal
|
|
438
|
+
plan: "pro",
|
|
439
|
+
country: "US",
|
|
440
|
+
});
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Best Practices
|
|
446
|
+
|
|
447
|
+
### 1. Initialize Once
|
|
448
|
+
|
|
449
|
+
Create a singleton instance and reuse:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
// analytics.ts
|
|
453
|
+
import { createBTSAnalytics } from "@behindthescenes/analytics";
|
|
454
|
+
|
|
455
|
+
export const analytics = createBTSAnalytics({
|
|
456
|
+
siteKey: "your-public-site-key",
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### 2. Handle Navigation Properly
|
|
461
|
+
|
|
462
|
+
For SPAs, always track route changes:
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
// In your router
|
|
466
|
+
router.afterEach((to) => {
|
|
467
|
+
analytics.page(to.fullPath);
|
|
468
|
+
});
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### 3. Test Your Implementation
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
// Development testing
|
|
475
|
+
if (process.env.NODE_ENV === "development") {
|
|
476
|
+
window.testAnalytics = () => {
|
|
477
|
+
analytics.track("test_event", { timestamp: Date.now() });
|
|
478
|
+
analytics.flushNow();
|
|
479
|
+
console.log("Test event sent - check the network tab");
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### 4. Monitor Event Volume
|
|
485
|
+
|
|
486
|
+
Track your event volume to avoid unexpected costs:
|
|
487
|
+
|
|
488
|
+
```typescript
|
|
489
|
+
let eventCount = 0;
|
|
490
|
+
|
|
491
|
+
const originalTrack = analytics.track.bind(analytics);
|
|
492
|
+
analytics.track = (...args) => {
|
|
493
|
+
eventCount++;
|
|
494
|
+
if (eventCount % 100 === 0) {
|
|
495
|
+
console.log(`Analytics events this session: ${eventCount}`);
|
|
496
|
+
}
|
|
497
|
+
return originalTrack(...args);
|
|
498
|
+
};
|
|
499
|
+
```
|