@mostly-good-metrics/javascript 0.4.4 → 0.5.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/README.md +50 -4
- package/dist/cjs/client.js +25 -16
- package/dist/cjs/client.js.map +1 -1
- package/dist/cjs/index.js +2 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/storage.js +139 -1
- package/dist/cjs/storage.js.map +1 -1
- package/dist/cjs/types.js +1 -1
- package/dist/cjs/types.js.map +1 -1
- package/dist/cjs/utils.js +29 -0
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/client.js +26 -17
- 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 +139 -1
- package/dist/esm/storage.js.map +1 -1
- package/dist/esm/types.js +1 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/utils.js +28 -0
- package/dist/esm/utils.js.map +1 -1
- package/dist/types/client.d.ts +5 -0
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/storage.d.ts +36 -1
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/types/types.d.ts +35 -15
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils.d.ts +5 -0
- package/dist/types/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/client.test.ts +81 -1
- package/src/client.ts +36 -16
- package/src/index.ts +1 -0
- package/src/storage.test.ts +126 -0
- package/src/storage.ts +153 -1
- package/src/types.ts +44 -15
- package/src/utils.test.ts +21 -0
- package/src/utils.ts +29 -0
package/dist/types/types.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export interface MGMConfiguration {
|
|
|
43
43
|
/**
|
|
44
44
|
* Whether to automatically track app lifecycle events
|
|
45
45
|
* ($app_opened, $app_backgrounded, $app_installed, $app_updated).
|
|
46
|
-
* @default
|
|
46
|
+
* @default false
|
|
47
47
|
*/
|
|
48
48
|
trackAppLifecycleEvents?: boolean;
|
|
49
49
|
/**
|
|
@@ -77,6 +77,25 @@ export interface MGMConfiguration {
|
|
|
77
77
|
* If not provided, uses the JS SDK's version.
|
|
78
78
|
*/
|
|
79
79
|
sdkVersion?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Override the auto-generated anonymous user ID.
|
|
82
|
+
* Wrapper SDKs (e.g., React Native) can pass a device-specific ID here.
|
|
83
|
+
* If not provided, a UUID will be auto-generated and persisted.
|
|
84
|
+
*/
|
|
85
|
+
anonymousId?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Cookie domain for cross-subdomain tracking.
|
|
88
|
+
* Set to '.yourdomain.com' to share anonymous ID across subdomains.
|
|
89
|
+
* Example: '.example.com' allows sharing between app.example.com and www.example.com
|
|
90
|
+
*/
|
|
91
|
+
cookieDomain?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Disable cookie storage entirely.
|
|
94
|
+
* When true, only localStorage will be used (no cross-subdomain tracking).
|
|
95
|
+
* Useful for GDPR compliance or privacy-focused applications.
|
|
96
|
+
* @default false
|
|
97
|
+
*/
|
|
98
|
+
disableCookies?: boolean;
|
|
80
99
|
/**
|
|
81
100
|
* Custom storage adapter. If not provided, uses localStorage in browsers
|
|
82
101
|
* or in-memory storage in non-browser environments.
|
|
@@ -96,7 +115,7 @@ export interface MGMConfiguration {
|
|
|
96
115
|
/**
|
|
97
116
|
* Internal resolved configuration with all defaults applied.
|
|
98
117
|
*/
|
|
99
|
-
export interface ResolvedConfiguration extends Required<Omit<MGMConfiguration, 'storage' | 'networkClient' | 'onError'>> {
|
|
118
|
+
export interface ResolvedConfiguration extends Required<Omit<MGMConfiguration, 'storage' | 'networkClient' | 'onError' | 'anonymousId' | 'cookieDomain' | 'disableCookies'>> {
|
|
100
119
|
storage?: IEventStorage;
|
|
101
120
|
networkClient?: INetworkClient;
|
|
102
121
|
onError?: (error: MGMError) => void;
|
|
@@ -131,12 +150,13 @@ export interface MGMEvent {
|
|
|
131
150
|
timestamp: string;
|
|
132
151
|
/**
|
|
133
152
|
* The user ID associated with this event.
|
|
153
|
+
* Uses identified user if set, otherwise falls back to anonymous UUID.
|
|
134
154
|
*/
|
|
135
|
-
|
|
155
|
+
user_id: string;
|
|
136
156
|
/**
|
|
137
157
|
* The session ID associated with this event.
|
|
138
158
|
*/
|
|
139
|
-
|
|
159
|
+
session_id?: string;
|
|
140
160
|
/**
|
|
141
161
|
* The platform this event was generated from.
|
|
142
162
|
*/
|
|
@@ -144,15 +164,15 @@ export interface MGMEvent {
|
|
|
144
164
|
/**
|
|
145
165
|
* The app version string.
|
|
146
166
|
*/
|
|
147
|
-
|
|
167
|
+
app_version?: string;
|
|
148
168
|
/**
|
|
149
169
|
* The app build number (separate from version).
|
|
150
170
|
*/
|
|
151
|
-
|
|
171
|
+
app_build_number?: string;
|
|
152
172
|
/**
|
|
153
173
|
* The OS version string.
|
|
154
174
|
*/
|
|
155
|
-
|
|
175
|
+
os_version?: string;
|
|
156
176
|
/**
|
|
157
177
|
* The environment name.
|
|
158
178
|
*/
|
|
@@ -160,7 +180,7 @@ export interface MGMEvent {
|
|
|
160
180
|
/**
|
|
161
181
|
* The device manufacturer (e.g., "Apple", "Samsung").
|
|
162
182
|
*/
|
|
163
|
-
|
|
183
|
+
device_manufacturer?: string;
|
|
164
184
|
/**
|
|
165
185
|
* The user's locale (e.g., "en-US").
|
|
166
186
|
*/
|
|
@@ -180,13 +200,13 @@ export interface MGMEvent {
|
|
|
180
200
|
*/
|
|
181
201
|
export interface MGMEventContext {
|
|
182
202
|
platform: Platform;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
203
|
+
app_version?: string;
|
|
204
|
+
app_build_number?: string;
|
|
205
|
+
os_version?: string;
|
|
206
|
+
user_id: string;
|
|
207
|
+
session_id?: string;
|
|
188
208
|
environment: string;
|
|
189
|
-
|
|
209
|
+
device_manufacturer?: string;
|
|
190
210
|
locale?: string;
|
|
191
211
|
timezone?: string;
|
|
192
212
|
}
|
|
@@ -306,7 +326,7 @@ export declare const DefaultConfiguration: {
|
|
|
306
326
|
readonly flushInterval: 30;
|
|
307
327
|
readonly maxStoredEvents: 10000;
|
|
308
328
|
readonly enableDebugLogging: false;
|
|
309
|
-
readonly trackAppLifecycleEvents:
|
|
329
|
+
readonly trackAppLifecycleEvents: false;
|
|
310
330
|
};
|
|
311
331
|
/**
|
|
312
332
|
* Validation constraints.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;OAGG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC;IAEV;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;OAEG;IACH,aAAa,CAAC,EAAE,cAAc,CAAC;IAE/B;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;CACrC;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,QAAQ,CACrD,IAAI,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types/utils.d.ts
CHANGED
|
@@ -3,6 +3,11 @@ import { DeviceType, EventProperties, MGMConfiguration, Platform, ResolvedConfig
|
|
|
3
3
|
* Generate a UUID v4 string.
|
|
4
4
|
*/
|
|
5
5
|
export declare function generateUUID(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Generate an anonymous user ID with $anon_ prefix.
|
|
8
|
+
* Format: $anon_xxxxxxxxxxxx (12 random chars)
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateAnonymousId(): string;
|
|
6
11
|
/**
|
|
7
12
|
* Get the current timestamp in ISO8601 format.
|
|
8
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,UAAU,EAEV,eAAe,EAEf,gBAAgB,EAEhB,QAAQ,EACR,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAYrC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkBpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,eAAe,GAAG,SAAS,EACvC,QAAQ,GAAE,MAAuC,GAChD,eAAe,GAAG,SAAS,CAW7B;AA0DD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,qBAAqB,CAoCpF;AAeD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAQzC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CA0B7C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CA0BrC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAyBvC;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAKlC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,UAAU,EAEV,eAAe,EAEf,gBAAgB,EAEhB,QAAQ,EACR,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAYrC;AAuBD;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkBpD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,eAAe,GAAG,SAAS,EACvC,QAAQ,GAAE,MAAuC,GAChD,eAAe,GAAG,SAAS,CAW7B;AA0DD;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,qBAAqB,CAoCpF;AAeD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,QAAQ,CAQzC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CA0B7C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CA0BrC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAyBvC;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAKlC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAMpC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mostly-good-metrics/javascript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "JavaScript/TypeScript SDK for MostlyGoodMetrics - a lightweight analytics library for web applications",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
package/src/client.test.ts
CHANGED
|
@@ -258,7 +258,7 @@ describe('MostlyGoodMetrics', () => {
|
|
|
258
258
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
259
259
|
|
|
260
260
|
const events = await storage.fetchEvents(1);
|
|
261
|
-
expect(events[0].
|
|
261
|
+
expect(events[0].user_id).toBe('user_456');
|
|
262
262
|
});
|
|
263
263
|
|
|
264
264
|
it('should not set empty userId', () => {
|
|
@@ -283,6 +283,86 @@ describe('MostlyGoodMetrics', () => {
|
|
|
283
283
|
MostlyGoodMetrics.resetIdentity();
|
|
284
284
|
expect(MostlyGoodMetrics.shared?.userId).toBeNull();
|
|
285
285
|
});
|
|
286
|
+
|
|
287
|
+
it('should keep anonymousId unchanged', () => {
|
|
288
|
+
const originalAnonymousId = MostlyGoodMetrics.shared?.anonymousId;
|
|
289
|
+
MostlyGoodMetrics.resetIdentity();
|
|
290
|
+
const newAnonymousId = MostlyGoodMetrics.shared?.anonymousId;
|
|
291
|
+
expect(newAnonymousId).toBe(originalAnonymousId);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('anonymousId', () => {
|
|
296
|
+
it('should auto-generate anonymousId on init', () => {
|
|
297
|
+
MostlyGoodMetrics.configure({
|
|
298
|
+
apiKey: 'test-key',
|
|
299
|
+
storage,
|
|
300
|
+
networkClient,
|
|
301
|
+
trackAppLifecycleEvents: false,
|
|
302
|
+
});
|
|
303
|
+
expect(MostlyGoodMetrics.shared?.anonymousId).toBeDefined();
|
|
304
|
+
expect(MostlyGoodMetrics.shared?.anonymousId.length).toBeGreaterThan(0);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it('should include anonymousId as user_id in events when not identified', async () => {
|
|
308
|
+
MostlyGoodMetrics.configure({
|
|
309
|
+
apiKey: 'test-key',
|
|
310
|
+
storage,
|
|
311
|
+
networkClient,
|
|
312
|
+
trackAppLifecycleEvents: false,
|
|
313
|
+
});
|
|
314
|
+
MostlyGoodMetrics.track('test_event');
|
|
315
|
+
|
|
316
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
317
|
+
|
|
318
|
+
const events = await storage.fetchEvents(1);
|
|
319
|
+
expect(events[0].user_id).toBe(MostlyGoodMetrics.shared?.anonymousId);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('should allow wrapper SDK to override anonymousId', () => {
|
|
323
|
+
const customAnonymousId = 'react-native-device-id-12345';
|
|
324
|
+
MostlyGoodMetrics.configure({
|
|
325
|
+
apiKey: 'test-key',
|
|
326
|
+
storage,
|
|
327
|
+
networkClient,
|
|
328
|
+
trackAppLifecycleEvents: false,
|
|
329
|
+
anonymousId: customAnonymousId,
|
|
330
|
+
});
|
|
331
|
+
expect(MostlyGoodMetrics.shared?.anonymousId).toBe(customAnonymousId);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('should include custom anonymousId as user_id in events', async () => {
|
|
335
|
+
const customAnonymousId = 'react-native-device-id-12345';
|
|
336
|
+
MostlyGoodMetrics.configure({
|
|
337
|
+
apiKey: 'test-key',
|
|
338
|
+
storage,
|
|
339
|
+
networkClient,
|
|
340
|
+
trackAppLifecycleEvents: false,
|
|
341
|
+
anonymousId: customAnonymousId,
|
|
342
|
+
});
|
|
343
|
+
MostlyGoodMetrics.track('test_event');
|
|
344
|
+
|
|
345
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
346
|
+
|
|
347
|
+
const events = await storage.fetchEvents(1);
|
|
348
|
+
expect(events[0].user_id).toBe(customAnonymousId);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should use identified userId over anonymousId', async () => {
|
|
352
|
+
MostlyGoodMetrics.configure({
|
|
353
|
+
apiKey: 'test-key',
|
|
354
|
+
storage,
|
|
355
|
+
networkClient,
|
|
356
|
+
trackAppLifecycleEvents: false,
|
|
357
|
+
});
|
|
358
|
+
MostlyGoodMetrics.identify('identified_user_123');
|
|
359
|
+
MostlyGoodMetrics.track('test_event');
|
|
360
|
+
|
|
361
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
362
|
+
|
|
363
|
+
const events = await storage.fetchEvents(1);
|
|
364
|
+
expect(events[0].user_id).toBe('identified_user_123');
|
|
365
|
+
});
|
|
286
366
|
});
|
|
287
367
|
|
|
288
368
|
describe('flush', () => {
|
package/src/client.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
delay,
|
|
18
18
|
detectDeviceType,
|
|
19
|
+
generateAnonymousId,
|
|
19
20
|
generateUUID,
|
|
20
21
|
getDeviceModel,
|
|
21
22
|
getISOTimestamp,
|
|
@@ -42,6 +43,7 @@ export class MostlyGoodMetrics {
|
|
|
42
43
|
private flushTimer: ReturnType<typeof setInterval> | null = null;
|
|
43
44
|
private isFlushingInternal = false;
|
|
44
45
|
private sessionIdValue: string;
|
|
46
|
+
private anonymousIdValue: string;
|
|
45
47
|
private lifecycleSetup = false;
|
|
46
48
|
|
|
47
49
|
/**
|
|
@@ -51,6 +53,13 @@ export class MostlyGoodMetrics {
|
|
|
51
53
|
this.config = resolveConfiguration(config);
|
|
52
54
|
this.sessionIdValue = generateUUID();
|
|
53
55
|
|
|
56
|
+
// Configure cookie settings before initializing anonymous ID
|
|
57
|
+
persistence.configureCookies(config.cookieDomain, config.disableCookies);
|
|
58
|
+
this.anonymousIdValue = persistence.initializeAnonymousId(
|
|
59
|
+
config.anonymousId,
|
|
60
|
+
generateAnonymousId
|
|
61
|
+
);
|
|
62
|
+
|
|
54
63
|
// Set up logging
|
|
55
64
|
setDebugLogging(this.config.enableDebugLogging);
|
|
56
65
|
|
|
@@ -113,9 +122,9 @@ export class MostlyGoodMetrics {
|
|
|
113
122
|
}
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
//
|
|
125
|
+
// =====================================================
|
|
117
126
|
// Static convenience methods (delegate to shared instance)
|
|
118
|
-
//
|
|
127
|
+
// =====================================================
|
|
119
128
|
|
|
120
129
|
/**
|
|
121
130
|
* Track an event with the given name and optional properties.
|
|
@@ -201,9 +210,9 @@ export class MostlyGoodMetrics {
|
|
|
201
210
|
return MostlyGoodMetrics.instance?.getSuperProperties() ?? {};
|
|
202
211
|
}
|
|
203
212
|
|
|
204
|
-
//
|
|
213
|
+
// =====================================================
|
|
205
214
|
// Instance properties
|
|
206
|
-
//
|
|
215
|
+
// =====================================================
|
|
207
216
|
|
|
208
217
|
/**
|
|
209
218
|
* Get the current user ID.
|
|
@@ -219,6 +228,13 @@ export class MostlyGoodMetrics {
|
|
|
219
228
|
return this.sessionIdValue;
|
|
220
229
|
}
|
|
221
230
|
|
|
231
|
+
/**
|
|
232
|
+
* Get the anonymous ID (auto-generated UUID, persisted across sessions).
|
|
233
|
+
*/
|
|
234
|
+
get anonymousId(): string {
|
|
235
|
+
return this.anonymousIdValue;
|
|
236
|
+
}
|
|
237
|
+
|
|
222
238
|
/**
|
|
223
239
|
* Check if a flush operation is in progress.
|
|
224
240
|
*/
|
|
@@ -233,9 +249,9 @@ export class MostlyGoodMetrics {
|
|
|
233
249
|
return { ...this.config };
|
|
234
250
|
}
|
|
235
251
|
|
|
236
|
-
//
|
|
252
|
+
// =====================================================
|
|
237
253
|
// Instance methods
|
|
238
|
-
//
|
|
254
|
+
// =====================================================
|
|
239
255
|
|
|
240
256
|
/**
|
|
241
257
|
* Track an event with the given name and optional properties.
|
|
@@ -265,11 +281,13 @@ export class MostlyGoodMetrics {
|
|
|
265
281
|
name,
|
|
266
282
|
client_event_id: generateUUID(),
|
|
267
283
|
timestamp: getISOTimestamp(),
|
|
268
|
-
|
|
269
|
-
|
|
284
|
+
|
|
285
|
+
user_id: this.userId ?? this.anonymousIdValue,
|
|
286
|
+
|
|
287
|
+
session_id: this.sessionIdValue,
|
|
270
288
|
platform: this.config.platform,
|
|
271
|
-
|
|
272
|
-
|
|
289
|
+
app_version: this.config.appVersion || undefined,
|
|
290
|
+
os_version: this.config.osVersion || getOSVersion() || undefined,
|
|
273
291
|
environment: this.config.environment,
|
|
274
292
|
locale: getLocale(),
|
|
275
293
|
timezone: getTimezone(),
|
|
@@ -398,9 +416,9 @@ export class MostlyGoodMetrics {
|
|
|
398
416
|
logger.debug('MostlyGoodMetrics instance destroyed');
|
|
399
417
|
}
|
|
400
418
|
|
|
401
|
-
//
|
|
419
|
+
// =====================================================
|
|
402
420
|
// Private methods
|
|
403
|
-
//
|
|
421
|
+
// =====================================================
|
|
404
422
|
|
|
405
423
|
private async checkBatchSize(): Promise<void> {
|
|
406
424
|
const count = await this.storage.eventCount();
|
|
@@ -466,10 +484,12 @@ export class MostlyGoodMetrics {
|
|
|
466
484
|
private buildPayload(events: MGMEvent[]): MGMEventsPayload {
|
|
467
485
|
const context: MGMEventContext = {
|
|
468
486
|
platform: this.config.platform,
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
487
|
+
app_version: this.config.appVersion || undefined,
|
|
488
|
+
os_version: this.config.osVersion || getOSVersion() || undefined,
|
|
489
|
+
|
|
490
|
+
user_id: this.userId ?? this.anonymousIdValue,
|
|
491
|
+
|
|
492
|
+
session_id: this.sessionIdValue,
|
|
473
493
|
environment: this.config.environment,
|
|
474
494
|
locale: getLocale(),
|
|
475
495
|
timezone: getTimezone(),
|
package/src/index.ts
CHANGED
package/src/storage.test.ts
CHANGED
|
@@ -322,3 +322,129 @@ describe('PersistenceManager super properties', () => {
|
|
|
322
322
|
expect(props).toEqual({});
|
|
323
323
|
});
|
|
324
324
|
});
|
|
325
|
+
|
|
326
|
+
describe('PersistenceManager anonymous ID (localStorage)', () => {
|
|
327
|
+
beforeEach(() => {
|
|
328
|
+
jest.clearAllMocks();
|
|
329
|
+
(localStorage.getItem as jest.Mock).mockReturnValue(null);
|
|
330
|
+
// Disable cookies for these tests to test localStorage behavior
|
|
331
|
+
persistence.configureCookies(undefined, true);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const generateMockUUID = () => 'mock-uuid-12345';
|
|
335
|
+
|
|
336
|
+
it('should generate anonymous ID if none exists', () => {
|
|
337
|
+
const id = persistence.initializeAnonymousId(undefined, generateMockUUID);
|
|
338
|
+
expect(id).toBe('mock-uuid-12345');
|
|
339
|
+
expect(localStorage.setItem).toHaveBeenCalledWith(
|
|
340
|
+
'mostlygoodmetrics_anonymous_id',
|
|
341
|
+
'mock-uuid-12345'
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should return existing anonymous ID from localStorage', () => {
|
|
346
|
+
(localStorage.getItem as jest.Mock).mockReturnValueOnce('existing-anonymous-id');
|
|
347
|
+
const id = persistence.initializeAnonymousId(undefined, generateMockUUID);
|
|
348
|
+
expect(id).toBe('existing-anonymous-id');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should use override ID from wrapper SDK', () => {
|
|
352
|
+
const overrideId = 'react-native-device-id';
|
|
353
|
+
const id = persistence.initializeAnonymousId(overrideId, generateMockUUID);
|
|
354
|
+
expect(id).toBe(overrideId);
|
|
355
|
+
expect(localStorage.setItem).toHaveBeenCalledWith('mostlygoodmetrics_anonymous_id', overrideId);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('should prefer override ID over existing persisted ID', () => {
|
|
359
|
+
(localStorage.getItem as jest.Mock).mockReturnValueOnce('existing-anonymous-id');
|
|
360
|
+
const overrideId = 'react-native-device-id';
|
|
361
|
+
const id = persistence.initializeAnonymousId(overrideId, generateMockUUID);
|
|
362
|
+
expect(id).toBe(overrideId);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('should reset anonymous ID with new UUID', () => {
|
|
366
|
+
let callCount = 0;
|
|
367
|
+
const generateNewUUID = () => {
|
|
368
|
+
callCount++;
|
|
369
|
+
return `new-uuid-${callCount}`;
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
const id1 = persistence.resetAnonymousId(generateNewUUID);
|
|
373
|
+
expect(id1).toBe('new-uuid-1');
|
|
374
|
+
|
|
375
|
+
const id2 = persistence.resetAnonymousId(generateNewUUID);
|
|
376
|
+
expect(id2).toBe('new-uuid-2');
|
|
377
|
+
expect(id2).not.toBe(id1);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should persist anonymous ID to localStorage', () => {
|
|
381
|
+
persistence.setAnonymousId('test-anonymous-id');
|
|
382
|
+
expect(localStorage.setItem).toHaveBeenCalledWith(
|
|
383
|
+
'mostlygoodmetrics_anonymous_id',
|
|
384
|
+
'test-anonymous-id'
|
|
385
|
+
);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('should get anonymous ID from localStorage', () => {
|
|
389
|
+
(localStorage.getItem as jest.Mock).mockReturnValueOnce('stored-anonymous-id');
|
|
390
|
+
const id = persistence.getAnonymousId();
|
|
391
|
+
expect(id).toBe('stored-anonymous-id');
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
describe('PersistenceManager anonymous ID (cookies)', () => {
|
|
396
|
+
beforeEach(() => {
|
|
397
|
+
jest.clearAllMocks();
|
|
398
|
+
(localStorage.getItem as jest.Mock).mockReturnValue(null);
|
|
399
|
+
// Clear document.cookie
|
|
400
|
+
document.cookie = 'mostlygoodmetrics_anonymous_id=; path=/; max-age=0';
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
const generateMockUUID = () => 'mock-uuid-12345';
|
|
404
|
+
|
|
405
|
+
it('should use cookies when enabled', () => {
|
|
406
|
+
persistence.configureCookies(undefined, false);
|
|
407
|
+
persistence.setAnonymousId('cookie-test-id');
|
|
408
|
+
expect(document.cookie).toContain('mostlygoodmetrics_anonymous_id=cookie-test-id');
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('should read from cookies before localStorage', () => {
|
|
412
|
+
persistence.configureCookies(undefined, false);
|
|
413
|
+
document.cookie = 'mostlygoodmetrics_anonymous_id=cookie-id; path=/';
|
|
414
|
+
(localStorage.getItem as jest.Mock).mockReturnValue('localStorage-id');
|
|
415
|
+
|
|
416
|
+
const id = persistence.getAnonymousId();
|
|
417
|
+
expect(id).toBe('cookie-id');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should fall back to localStorage when cookie is not set', () => {
|
|
421
|
+
persistence.configureCookies(undefined, false);
|
|
422
|
+
(localStorage.getItem as jest.Mock).mockReturnValue('localStorage-id');
|
|
423
|
+
|
|
424
|
+
const id = persistence.getAnonymousId();
|
|
425
|
+
expect(id).toBe('localStorage-id');
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should set cookie with custom domain for cross-subdomain support', () => {
|
|
429
|
+
persistence.configureCookies('.example.com', false);
|
|
430
|
+
persistence.setAnonymousId('cross-domain-id');
|
|
431
|
+
// Note: jsdom rejects cookies for non-matching domains, so we verify localStorage fallback
|
|
432
|
+
// In real browsers, the cookie would be set with domain=.example.com
|
|
433
|
+
expect(localStorage.setItem).toHaveBeenCalledWith(
|
|
434
|
+
'mostlygoodmetrics_anonymous_id',
|
|
435
|
+
'cross-domain-id'
|
|
436
|
+
);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('should not use cookies when disabled', () => {
|
|
440
|
+
persistence.configureCookies(undefined, true);
|
|
441
|
+
persistence.setAnonymousId('no-cookie-id');
|
|
442
|
+
// Cookie should not contain our ID (cleared in beforeEach)
|
|
443
|
+
expect(document.cookie).not.toContain('mostlygoodmetrics_anonymous_id=no-cookie-id');
|
|
444
|
+
// But localStorage should have it
|
|
445
|
+
expect(localStorage.setItem).toHaveBeenCalledWith(
|
|
446
|
+
'mostlygoodmetrics_anonymous_id',
|
|
447
|
+
'no-cookie-id'
|
|
448
|
+
);
|
|
449
|
+
});
|
|
450
|
+
});
|