@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.
@@ -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 true
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
- userId?: string;
155
+ user_id: string;
136
156
  /**
137
157
  * The session ID associated with this event.
138
158
  */
139
- sessionId?: string;
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
- appVersion?: string;
167
+ app_version?: string;
148
168
  /**
149
169
  * The app build number (separate from version).
150
170
  */
151
- appBuildNumber?: string;
171
+ app_build_number?: string;
152
172
  /**
153
173
  * The OS version string.
154
174
  */
155
- osVersion?: string;
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
- deviceManufacturer?: string;
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
- appVersion?: string;
184
- appBuildNumber?: string;
185
- osVersion?: string;
186
- userId?: string;
187
- sessionId?: string;
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
- deviceManufacturer?: string;
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: true;
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":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -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.4.4",
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",
@@ -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].userId).toBe('user_456');
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
- userId: this.userId ?? undefined,
269
- sessionId: this.sessionIdValue,
284
+
285
+ user_id: this.userId ?? this.anonymousIdValue,
286
+
287
+ session_id: this.sessionIdValue,
270
288
  platform: this.config.platform,
271
- appVersion: this.config.appVersion || undefined,
272
- osVersion: this.config.osVersion || getOSVersion() || undefined,
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
- appVersion: this.config.appVersion || undefined,
470
- osVersion: this.config.osVersion || getOSVersion() || undefined,
471
- userId: this.userId ?? undefined,
472
- sessionId: this.sessionIdValue,
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
@@ -65,6 +65,7 @@ export { FetchNetworkClient, createDefaultNetworkClient } from './network';
65
65
 
66
66
  // Utilities (for advanced usage)
67
67
  export {
68
+ generateAnonymousId,
68
69
  generateUUID,
69
70
  getISOTimestamp,
70
71
  isValidEventName,
@@ -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
+ });