@mostly-good-metrics/javascript 0.7.0 → 0.8.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.
- package/LICENSE +21 -0
- package/README.md +198 -68
- package/dist/cjs/client.js +181 -83
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/index.js +4 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/storage.js +52 -1
- package/dist/cjs/storage.js.map +1 -1
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils.js +1 -0
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/client.js +182 -84
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/storage.js +49 -1
- package/dist/esm/storage.js.map +1 -1
- package/dist/esm/types.js +3 -0
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils.js +1 -0
- package/dist/esm/utils.js.map +1 -1
- package/dist/types/client.d.ts +73 -27
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/storage.d.ts +23 -1
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/types/types.d.ts +30 -1
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client.test.ts +584 -1
- package/src/client.ts +202 -87
- package/src/index.ts +9 -1
- package/src/storage.ts +58 -1
- package/src/types.ts +39 -1
- package/src/utils.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Mostly Good Metrics
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
A lightweight JavaScript/TypeScript SDK for tracking analytics events with [MostlyGoodMetrics](https://mostlygoodmetrics.com).
|
|
4
4
|
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Requirements](#requirements)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Quick Start](#quick-start)
|
|
10
|
+
- [User Identification](#user-identification)
|
|
11
|
+
- [Configuration Options](#configuration-options)
|
|
12
|
+
- [Automatic Events](#automatic-events)
|
|
13
|
+
- [Automatic Properties](#automatic-properties)
|
|
14
|
+
- [Automatic Context](#automatic-context)
|
|
15
|
+
- [Event Naming](#event-naming)
|
|
16
|
+
- [Properties](#properties)
|
|
17
|
+
- [Manual Flush](#manual-flush)
|
|
18
|
+
- [A/B Testing (Experiments)](#ab-testing-experiments)
|
|
19
|
+
- [Automatic Behavior](#automatic-behavior)
|
|
20
|
+
- [Debug Logging](#debug-logging)
|
|
21
|
+
- [Framework Integration](#framework-integration)
|
|
22
|
+
- [Custom Storage](#custom-storage)
|
|
23
|
+
- [License](#license)
|
|
24
|
+
|
|
5
25
|
## Requirements
|
|
6
26
|
|
|
7
27
|
- Node.js 16+ (for build tools)
|
|
@@ -61,46 +81,44 @@ That's it! Events are automatically batched and sent.
|
|
|
61
81
|
|
|
62
82
|
## User Identification
|
|
63
83
|
|
|
64
|
-
The SDK automatically generates and persists an anonymous
|
|
65
|
-
- Is auto-generated on first visit
|
|
66
|
-
- Persists across sessions (stored in cookies and localStorage)
|
|
67
|
-
- Is included in every event as `user_id`
|
|
68
|
-
|
|
69
|
-
When you call `identify()`, the identified user ID takes precedence over the anonymous ID.
|
|
84
|
+
The SDK automatically generates and persists an anonymous user ID (UUID) for each user. This anonymous ID is stored in both cookies and localStorage, persisting across sessions.
|
|
70
85
|
|
|
71
86
|
```typescript
|
|
72
|
-
// Before identify(): user_id = "550e8400-e29b-41d4-a716-446655440000"
|
|
87
|
+
// Before identify(): user_id = auto-generated UUID (e.g., "550e8400-e29b-41d4-a716-446655440000")
|
|
73
88
|
MostlyGoodMetrics.identify('user_123');
|
|
74
89
|
// After identify(): user_id = "user_123"
|
|
75
90
|
|
|
76
91
|
MostlyGoodMetrics.resetIdentity();
|
|
77
|
-
// After reset: user_id =
|
|
92
|
+
// After reset: user_id = new auto-generated UUID
|
|
78
93
|
```
|
|
79
94
|
|
|
80
95
|
### Cross-Subdomain Tracking
|
|
81
96
|
|
|
82
|
-
|
|
97
|
+
To track users across subdomains (e.g., `app.example.com` and `blog.example.com`), configure a shared cookie domain:
|
|
83
98
|
|
|
84
99
|
```typescript
|
|
85
100
|
MostlyGoodMetrics.configure({
|
|
86
101
|
apiKey: 'mgm_proj_your_api_key',
|
|
87
|
-
cookieDomain: '.
|
|
102
|
+
cookieDomain: '.example.com', // Share ID across all *.example.com subdomains
|
|
88
103
|
});
|
|
89
104
|
```
|
|
90
105
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
### Privacy Mode (No Cookies)
|
|
106
|
+
### Privacy Mode
|
|
94
107
|
|
|
95
|
-
For
|
|
108
|
+
For privacy-focused implementations or GDPR compliance, you can disable cookies entirely:
|
|
96
109
|
|
|
97
110
|
```typescript
|
|
98
111
|
MostlyGoodMetrics.configure({
|
|
99
112
|
apiKey: 'mgm_proj_your_api_key',
|
|
100
|
-
disableCookies: true, //
|
|
113
|
+
disableCookies: true, // Uses localStorage only, no cookies
|
|
101
114
|
});
|
|
102
115
|
```
|
|
103
116
|
|
|
117
|
+
When `disableCookies: true`:
|
|
118
|
+
- Anonymous ID stored in localStorage only
|
|
119
|
+
- No cookies are set
|
|
120
|
+
- Cross-subdomain tracking is disabled
|
|
121
|
+
|
|
104
122
|
## Configuration Options
|
|
105
123
|
|
|
106
124
|
For more control, pass additional configuration:
|
|
@@ -116,6 +134,8 @@ MostlyGoodMetrics.configure({
|
|
|
116
134
|
maxStoredEvents: 10000,
|
|
117
135
|
enableDebugLogging: process.env.NODE_ENV === 'development',
|
|
118
136
|
trackAppLifecycleEvents: true,
|
|
137
|
+
cookieDomain: '.yourdomain.com',
|
|
138
|
+
disableCookies: false,
|
|
119
139
|
});
|
|
120
140
|
```
|
|
121
141
|
|
|
@@ -132,10 +152,11 @@ MostlyGoodMetrics.configure({
|
|
|
132
152
|
| `trackAppLifecycleEvents` | `false` | Auto-track lifecycle events ($app_opened, etc.) |
|
|
133
153
|
| `bundleId` | auto-detected | Custom bundle identifier |
|
|
134
154
|
| `cookieDomain` | - | Cookie domain for cross-subdomain tracking (e.g., `.example.com`) |
|
|
135
|
-
| `disableCookies` | `false` | Disable cookies
|
|
136
|
-
| `anonymousId` | auto-generated | Override anonymous ID (for wrapper SDKs
|
|
137
|
-
| `storage` | auto-detected | Custom storage adapter |
|
|
155
|
+
| `disableCookies` | `false` | Disable cookies entirely (uses localStorage only) |
|
|
156
|
+
| `anonymousId` | auto-generated | Override anonymous ID (for wrapper SDKs) |
|
|
157
|
+
| `storage` | auto-detected | Custom storage adapter (see [Custom Storage](#custom-storage)) |
|
|
138
158
|
| `networkClient` | fetch-based | Custom network client |
|
|
159
|
+
| `experimentStorage` | auto-detected | Custom key-value storage for the experiments cache (see [A/B Testing](#ab-testing-experiments)) |
|
|
139
160
|
|
|
140
161
|
## Automatic Events
|
|
141
162
|
|
|
@@ -154,12 +175,54 @@ When `trackAppLifecycleEvents` is enabled, the SDK automatically tracks:
|
|
|
154
175
|
|
|
155
176
|
The SDK automatically includes these properties with every event:
|
|
156
177
|
|
|
157
|
-
| Property | Description |
|
|
158
|
-
|
|
159
|
-
| `$device_type` | Device
|
|
160
|
-
| `$device_model` | Browser name and version |
|
|
178
|
+
| Property | Description | Example | Source |
|
|
179
|
+
|----------|-------------|---------|--------|
|
|
180
|
+
| `$device_type` | Device form factor | `desktop`, `phone`, `tablet` | Detected from user agent and screen size |
|
|
181
|
+
| `$device_model` | Browser name and major version | `Chrome 120.0` | Parsed from user agent |
|
|
182
|
+
| `$browser` | Browser name | `Chrome`, `Safari`, `Firefox` | Parsed from user agent |
|
|
183
|
+
| `$browser_version` | Full browser version | `120.0.6099.130` | Parsed from user agent |
|
|
184
|
+
| `$os` | Operating system name | `Mac OS X`, `Windows`, `Linux`, `iOS`, `Android` | Parsed from user agent |
|
|
185
|
+
| `$os_version` | Operating system version | `10.15.7` | Parsed from user agent |
|
|
186
|
+
| `$screen_width` | Screen width in pixels | `1920` | `window.screen.width` |
|
|
187
|
+
| `$screen_height` | Screen height in pixels | `1080` | `window.screen.height` |
|
|
188
|
+
| `$viewport_width` | Viewport width in pixels | `1440` | `window.innerWidth` |
|
|
189
|
+
| `$viewport_height` | Viewport height in pixels | `900` | `window.innerHeight` |
|
|
190
|
+
| `$user_agent` | Full user agent string | `Mozilla/5.0...` | `navigator.userAgent` |
|
|
191
|
+
|
|
192
|
+
> **Note:** Properties with the `$` prefix are reserved for system use. Do not use the `$` prefix for your own custom properties.
|
|
193
|
+
|
|
194
|
+
## Automatic Context
|
|
195
|
+
|
|
196
|
+
The SDK automatically includes these fields with every event to provide rich context:
|
|
197
|
+
|
|
198
|
+
### Identity & Session
|
|
199
|
+
|
|
200
|
+
| Field | Description | Example | Persistence |
|
|
201
|
+
|-------|-------------|---------|-------------|
|
|
202
|
+
| `user_id` | Identified user ID (set via `identify()`) or auto-generated anonymous UUID | `user_123` | Persisted in cookies + localStorage (survives page reloads) |
|
|
203
|
+
| `session_id` | UUID generated per page load | `abc123-def456` | Regenerated on each page load |
|
|
204
|
+
|
|
205
|
+
### Device & Platform
|
|
206
|
+
|
|
207
|
+
| Field | Description | Example | Source |
|
|
208
|
+
|-------|-------------|---------|--------|
|
|
209
|
+
| `platform` | Platform identifier | `web` | Hardcoded |
|
|
210
|
+
| `locale` | User's locale | `en-US` | `Intl.DateTimeFormat().resolvedOptions().locale` |
|
|
211
|
+
| `timezone` | User's timezone | `America/New_York` | `Intl.DateTimeFormat().resolvedOptions().timeZone` |
|
|
161
212
|
|
|
162
|
-
|
|
213
|
+
### App & Environment
|
|
214
|
+
|
|
215
|
+
| Field | Description | Example | Source |
|
|
216
|
+
|-------|-------------|---------|--------|
|
|
217
|
+
| `app_version` | App version (if configured) | `1.2.0` | Configuration option |
|
|
218
|
+
| `environment` | Environment name | `production`, `staging`, `development` | Configuration option (default: `production`) |
|
|
219
|
+
|
|
220
|
+
### Event Metadata
|
|
221
|
+
|
|
222
|
+
| Field | Description | Example | Purpose |
|
|
223
|
+
|-------|-------------|---------|---------|
|
|
224
|
+
| `client_event_id` | Unique UUID for each event | `550e8400-e29b-41d4-a716-446655440000` | Deduplication (prevents processing the same event twice) |
|
|
225
|
+
| `timestamp` | ISO 8601 timestamp when event was tracked | `2024-01-15T10:30:00.000Z` | Event ordering and time-based analysis |
|
|
163
226
|
|
|
164
227
|
## Event Naming
|
|
165
228
|
|
|
@@ -168,6 +231,8 @@ Event names must:
|
|
|
168
231
|
- Contain only alphanumeric characters and underscores
|
|
169
232
|
- Be 255 characters or less
|
|
170
233
|
|
|
234
|
+
> **Note:** The `$` prefix is reserved for system events (like `$app_opened`, `$app_installed`). Do not use `$` for custom event names.
|
|
235
|
+
|
|
171
236
|
```typescript
|
|
172
237
|
// Valid
|
|
173
238
|
MostlyGoodMetrics.track('button_clicked');
|
|
@@ -178,6 +243,7 @@ MostlyGoodMetrics.track('step_1_completed');
|
|
|
178
243
|
MostlyGoodMetrics.track('123_event'); // starts with number
|
|
179
244
|
MostlyGoodMetrics.track('event-name'); // contains hyphen
|
|
180
245
|
MostlyGoodMetrics.track('event name'); // contains space
|
|
246
|
+
MostlyGoodMetrics.track('$custom_event'); // $ prefix is reserved
|
|
181
247
|
```
|
|
182
248
|
|
|
183
249
|
## Properties
|
|
@@ -218,20 +284,97 @@ const count = await MostlyGoodMetrics.getPendingEventCount();
|
|
|
218
284
|
console.log(`${count} events pending`);
|
|
219
285
|
```
|
|
220
286
|
|
|
221
|
-
##
|
|
287
|
+
## A/B Testing (Experiments)
|
|
288
|
+
|
|
289
|
+
Variants are assigned server-side (`GET /v1/experiments`) so the same user always gets the same variant for the same experiment.
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// Wait for experiments to load (resolves immediately if cached).
|
|
293
|
+
// Resolves after 5 seconds at the latest so a hanging network never
|
|
294
|
+
// blocks startup - pass a custom timeout with ready(timeoutMs).
|
|
295
|
+
// ready() never rejects; if it times out, getVariant() returns fallbacks
|
|
296
|
+
// until the in-flight fetch completes (a late response is still applied).
|
|
297
|
+
await MostlyGoodMetrics.ready();
|
|
298
|
+
|
|
299
|
+
// Get the assigned variant, with an optional fallback for when the
|
|
300
|
+
// experiment is unknown or experiments haven't loaded yet (default: null)
|
|
301
|
+
const variant = MostlyGoodMetrics.getVariant('checkout-flow', 'control');
|
|
302
|
+
|
|
303
|
+
if (variant === 'treatment') {
|
|
304
|
+
// show the new checkout
|
|
305
|
+
}
|
|
306
|
+
```
|
|
222
307
|
|
|
223
|
-
|
|
308
|
+
### Caching (stale-while-revalidate, no TTL)
|
|
224
309
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
310
|
+
Assigned variants are cached locally per user and **never expire**:
|
|
311
|
+
|
|
312
|
+
- Cached variants are served synchronously as soon as they are hydrated; `ready()` resolves without waiting for the network.
|
|
313
|
+
- On a cold cache, `ready(timeoutMs = 5000)` resolves when the first experiments load settles or the timeout elapses, whichever comes first (it never rejects). The underlying request is aborted after 60 seconds so it always settles; a response arriving after a `ready()` timeout is still applied atomically.
|
|
314
|
+
- A background refetch keeps assignments up to date, throttled to at most once per hour (the last-fetch timestamp is persisted).
|
|
315
|
+
- On `identify()` with a changed user ID, the SDK keeps serving the current in-memory variants, refetches immediately (passing `anonymous_id` alongside `user_id` so the server can alias pre-identify assignments), and atomically swaps in the response. Variants are never cleared to null mid-session.
|
|
316
|
+
|
|
317
|
+
### Exposure tracking
|
|
318
|
+
|
|
319
|
+
On the first `getVariant()` hit per (user, experiment, variant), the SDK automatically tracks a `$experiment_exposure` event with properties `$experiment_name` (the raw experiment name) and `$variant`. Deduplication flags are persisted, so exposures are not re-tracked across page reloads or app restarts.
|
|
320
|
+
|
|
321
|
+
The variant is also stored as a super property (`$experiment_{name}: variant`, snake_cased) so it is attached to all subsequent events.
|
|
322
|
+
|
|
323
|
+
### Custom experiment storage (React Native)
|
|
324
|
+
|
|
325
|
+
The experiments cache and exposure flags are persisted through a small pluggable key-value interface (`IExperimentStorage`). By default the SDK uses localStorage (or in-memory storage where unavailable). React Native apps should inject AsyncStorage:
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
329
|
+
import { MostlyGoodMetrics, IExperimentStorage } from '@mostly-good-metrics/javascript';
|
|
330
|
+
|
|
331
|
+
const experimentStorage: IExperimentStorage = {
|
|
332
|
+
getItem: (key) => AsyncStorage.getItem(key),
|
|
333
|
+
setItem: (key, value) => AsyncStorage.setItem(key, value),
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
MostlyGoodMetrics.configure({
|
|
337
|
+
apiKey: 'mgm_proj_your_api_key',
|
|
338
|
+
experimentStorage,
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Async adapters are fully supported - cache hydration completes before `ready()` resolves.
|
|
343
|
+
|
|
344
|
+
## Automatic Behavior
|
|
345
|
+
|
|
346
|
+
The SDK handles many tasks automatically to provide a seamless analytics experience:
|
|
347
|
+
|
|
348
|
+
### Identity Management
|
|
349
|
+
- **Generates anonymous user ID**: Creates a persistent UUID on first visit, stored in cookies + localStorage
|
|
350
|
+
- **Persists identified user ID**: Stores user ID (from `identify()`) in cookies + localStorage, automatically restored on page reload
|
|
351
|
+
- **Generates session IDs**: Creates a new UUID on each page load to track user sessions
|
|
352
|
+
|
|
353
|
+
### Event Storage & Delivery
|
|
354
|
+
- **Persists events**: Stores events in localStorage (with in-memory fallback if unavailable)
|
|
355
|
+
- **Batches events**: Groups events together for efficient network usage (default: 100 events per batch)
|
|
356
|
+
- **Flushes on interval**: Automatically sends events every 30 seconds (configurable via `flushInterval`)
|
|
357
|
+
- **Flushes on visibility change**: Sends pending events when tab is hidden or page unloads
|
|
358
|
+
- **Compresses payloads**: Large batches (>1KB) are automatically gzip compressed
|
|
359
|
+
- **Retries on failure**: Preserves events on network errors and retries with exponential backoff
|
|
360
|
+
- **Handles rate limiting**: Automatically backs off when server rate limits are hit
|
|
361
|
+
- **Adds deduplication IDs**: Includes unique `client_event_id` with each event to prevent duplicate processing
|
|
362
|
+
- **Offline support**: Events queue locally when offline and send when connectivity returns
|
|
363
|
+
|
|
364
|
+
### Lifecycle Tracking
|
|
365
|
+
When `trackAppLifecycleEvents` is enabled, the SDK automatically:
|
|
366
|
+
- **Detects install**: Tracks `$app_installed` event on first visit (requires `appVersion` config)
|
|
367
|
+
- **Detects updates**: Tracks `$app_updated` event when app version changes (requires `appVersion` config)
|
|
368
|
+
- **Tracks page visibility**: Fires `$app_opened` when page becomes visible
|
|
369
|
+
- **Tracks page hidden**: Fires `$app_backgrounded` when page is hidden or unloads
|
|
370
|
+
|
|
371
|
+
### Platform Integration
|
|
372
|
+
- **Detects device type**: Automatically identifies desktop, phone, or tablet from user agent and screen size
|
|
373
|
+
- **Captures browser info**: Includes browser name and version with every event
|
|
374
|
+
- **Captures OS info**: Includes operating system name and version with every event
|
|
375
|
+
- **Captures screen dimensions**: Includes screen and viewport size for UI analytics
|
|
376
|
+
- **Captures locale**: Includes user's language/region setting
|
|
377
|
+
- **Captures timezone**: Includes user's timezone for accurate time-based analysis
|
|
235
378
|
|
|
236
379
|
## Debug Logging
|
|
237
380
|
|
|
@@ -252,31 +395,10 @@ Output example:
|
|
|
252
395
|
[MostlyGoodMetrics] [DEBUG] Successfully sent 5 events
|
|
253
396
|
```
|
|
254
397
|
|
|
255
|
-
## Custom Storage
|
|
256
|
-
|
|
257
|
-
You can provide a custom storage adapter for environments where localStorage isn't available:
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
import { MostlyGoodMetrics, IEventStorage, InMemoryEventStorage } from '@mostly-good-metrics/javascript';
|
|
261
|
-
|
|
262
|
-
// Use in-memory storage
|
|
263
|
-
MostlyGoodMetrics.configure({
|
|
264
|
-
apiKey: 'mgm_proj_your_api_key',
|
|
265
|
-
storage: new InMemoryEventStorage(10000),
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// Or implement your own
|
|
269
|
-
class MyCustomStorage implements IEventStorage {
|
|
270
|
-
async store(event: MGMEvent): Promise<void> { /* ... */ }
|
|
271
|
-
async fetchEvents(limit: number): Promise<MGMEvent[]> { /* ... */ }
|
|
272
|
-
async removeEvents(count: number): Promise<void> { /* ... */ }
|
|
273
|
-
async eventCount(): Promise<number> { /* ... */ }
|
|
274
|
-
async clear(): Promise<void> { /* ... */ }
|
|
275
|
-
}
|
|
276
|
-
```
|
|
277
|
-
|
|
278
398
|
## Framework Integration
|
|
279
399
|
|
|
400
|
+
The SDK supports both JavaScript and TypeScript projects. All type definitions are included automatically.
|
|
401
|
+
|
|
280
402
|
### React
|
|
281
403
|
|
|
282
404
|
```typescript
|
|
@@ -345,19 +467,27 @@ import analytics from './plugins/analytics';
|
|
|
345
467
|
app.use(analytics);
|
|
346
468
|
```
|
|
347
469
|
|
|
348
|
-
##
|
|
470
|
+
## Custom Storage
|
|
349
471
|
|
|
350
|
-
|
|
472
|
+
You can provide a custom storage adapter for environments where localStorage isn't available:
|
|
351
473
|
|
|
352
474
|
```typescript
|
|
353
|
-
import {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
475
|
+
import { MostlyGoodMetrics, IEventStorage, InMemoryEventStorage } from '@mostly-good-metrics/javascript';
|
|
476
|
+
|
|
477
|
+
// Use in-memory storage
|
|
478
|
+
MostlyGoodMetrics.configure({
|
|
479
|
+
apiKey: 'mgm_proj_your_api_key',
|
|
480
|
+
storage: new InMemoryEventStorage(10000),
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// Or implement your own
|
|
484
|
+
class MyCustomStorage implements IEventStorage {
|
|
485
|
+
async store(event: MGMEvent): Promise<void> { /* ... */ }
|
|
486
|
+
async fetchEvents(limit: number): Promise<MGMEvent[]> { /* ... */ }
|
|
487
|
+
async removeEvents(count: number): Promise<void> { /* ... */ }
|
|
488
|
+
async eventCount(): Promise<number> { /* ... */ }
|
|
489
|
+
async clear(): Promise<void> { /* ... */ }
|
|
490
|
+
}
|
|
361
491
|
```
|
|
362
492
|
|
|
363
493
|
## License
|