@mostly-good-metrics/javascript 0.4.3 → 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 +34 -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/network.js +1 -1
- package/dist/cjs/network.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 +30 -0
- package/dist/cjs/utils.js.map +1 -1
- package/dist/esm/client.js +35 -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/network.js +1 -1
- package/dist/esm/network.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 +29 -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 +42 -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 +153 -1
- package/src/client.ts +45 -16
- package/src/index.ts +1 -0
- package/src/network.test.ts +3 -3
- package/src/network.ts +1 -1
- package/src/storage.test.ts +126 -0
- package/src/storage.ts +153 -1
- package/src/types.ts +52 -15
- package/src/utils.test.ts +21 -0
- package/src/utils.ts +30 -0
package/dist/types/index.d.ts
CHANGED
|
@@ -29,6 +29,6 @@ export { MGMError } from './types';
|
|
|
29
29
|
export { SystemEvents, SystemProperties, DefaultConfiguration, Constraints, EVENT_NAME_REGEX, } from './types';
|
|
30
30
|
export { InMemoryEventStorage, LocalStorageEventStorage, createDefaultStorage } from './storage';
|
|
31
31
|
export { FetchNetworkClient, createDefaultNetworkClient } from './network';
|
|
32
|
-
export { generateUUID, getISOTimestamp, isValidEventName, validateEventName, sanitizeProperties, detectPlatform, detectDeviceType, getOSVersion, getDeviceModel, } from './utils';
|
|
32
|
+
export { generateAnonymousId, generateUUID, getISOTimestamp, isValidEventName, validateEventName, sanitizeProperties, detectPlatform, detectDeviceType, getOSVersion, getDeviceModel, } from './utils';
|
|
33
33
|
export { logger } from './logger';
|
|
34
34
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,GAAG,EACH,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGjG,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAG3E,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAG7C,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,gBAAgB,EAChB,QAAQ,EACR,GAAG,EACH,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,GACjB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,oBAAoB,EAAE,wBAAwB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAGjG,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,WAAW,CAAC;AAG3E,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,YAAY,EACZ,cAAc,GACf,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/types/storage.d.ts
CHANGED
|
@@ -44,12 +44,25 @@ export declare class LocalStorageEventStorage implements IEventStorage {
|
|
|
44
44
|
export declare function createDefaultStorage(maxEvents: number): IEventStorage;
|
|
45
45
|
/**
|
|
46
46
|
* Persistence helpers for user ID and app version.
|
|
47
|
-
*
|
|
47
|
+
* Uses cookies first (for cross-subdomain support), then localStorage as fallback.
|
|
48
48
|
*/
|
|
49
49
|
declare class PersistenceManager {
|
|
50
50
|
private inMemoryUserId;
|
|
51
|
+
private inMemoryAnonymousId;
|
|
51
52
|
private inMemoryAppVersion;
|
|
52
53
|
private inMemorySuperProperties;
|
|
54
|
+
private cookieDomain;
|
|
55
|
+
private disableCookies;
|
|
56
|
+
/**
|
|
57
|
+
* Configure cookie settings.
|
|
58
|
+
* @param cookieDomain Domain for cross-subdomain cookies (e.g., '.example.com')
|
|
59
|
+
* @param disableCookies If true, only use localStorage (no cookies)
|
|
60
|
+
*/
|
|
61
|
+
configureCookies(cookieDomain?: string, disableCookies?: boolean): void;
|
|
62
|
+
/**
|
|
63
|
+
* Check if cookies should be used.
|
|
64
|
+
*/
|
|
65
|
+
private shouldUseCookies;
|
|
53
66
|
/**
|
|
54
67
|
* Get the persisted user ID.
|
|
55
68
|
*/
|
|
@@ -58,6 +71,28 @@ declare class PersistenceManager {
|
|
|
58
71
|
* Set the user ID (persists across sessions).
|
|
59
72
|
*/
|
|
60
73
|
setUserId(userId: string | null): void;
|
|
74
|
+
/**
|
|
75
|
+
* Get the anonymous ID (auto-generated UUID).
|
|
76
|
+
* Checks cookies first, then localStorage, then in-memory.
|
|
77
|
+
*/
|
|
78
|
+
getAnonymousId(): string | null;
|
|
79
|
+
/**
|
|
80
|
+
* Set the anonymous ID (persists across sessions).
|
|
81
|
+
* Saves to both cookies and localStorage for redundancy.
|
|
82
|
+
*/
|
|
83
|
+
setAnonymousId(anonymousId: string): void;
|
|
84
|
+
/**
|
|
85
|
+
* Initialize the anonymous ID. If an override is provided, use it.
|
|
86
|
+
* Otherwise, use existing persisted ID or generate a new UUID.
|
|
87
|
+
* @param overrideId Optional ID from wrapper SDK (e.g., React Native device ID)
|
|
88
|
+
* @param generateUUID Function to generate a UUID
|
|
89
|
+
*/
|
|
90
|
+
initializeAnonymousId(overrideId: string | undefined, generateUUID: () => string): string;
|
|
91
|
+
/**
|
|
92
|
+
* Reset the anonymous ID to a new UUID.
|
|
93
|
+
* @param generateUUID Function to generate a UUID
|
|
94
|
+
*/
|
|
95
|
+
resetAnonymousId(generateUUID: () => string): string;
|
|
61
96
|
/**
|
|
62
97
|
* Get the persisted app version (for detecting updates).
|
|
63
98
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/storage.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,eAAe,EAAE,aAAa,EAAY,QAAQ,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/storage.ts"],"names":[],"mappings":"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"}
|
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.
|
|
@@ -86,13 +105,20 @@ export interface MGMConfiguration {
|
|
|
86
105
|
* Custom network client. If not provided, uses fetch-based client.
|
|
87
106
|
*/
|
|
88
107
|
networkClient?: INetworkClient;
|
|
108
|
+
/**
|
|
109
|
+
* Callback invoked when a network error occurs.
|
|
110
|
+
* Useful for reporting errors to external services like Sentry.
|
|
111
|
+
* @param error The error that occurred
|
|
112
|
+
*/
|
|
113
|
+
onError?: (error: MGMError) => void;
|
|
89
114
|
}
|
|
90
115
|
/**
|
|
91
116
|
* Internal resolved configuration with all defaults applied.
|
|
92
117
|
*/
|
|
93
|
-
export interface ResolvedConfiguration extends Required<Omit<MGMConfiguration, 'storage' | 'networkClient'>> {
|
|
118
|
+
export interface ResolvedConfiguration extends Required<Omit<MGMConfiguration, 'storage' | 'networkClient' | 'onError' | 'anonymousId' | 'cookieDomain' | 'disableCookies'>> {
|
|
94
119
|
storage?: IEventStorage;
|
|
95
120
|
networkClient?: INetworkClient;
|
|
121
|
+
onError?: (error: MGMError) => void;
|
|
96
122
|
}
|
|
97
123
|
/**
|
|
98
124
|
* Properties that can be attached to an event.
|
|
@@ -124,12 +150,13 @@ export interface MGMEvent {
|
|
|
124
150
|
timestamp: string;
|
|
125
151
|
/**
|
|
126
152
|
* The user ID associated with this event.
|
|
153
|
+
* Uses identified user if set, otherwise falls back to anonymous UUID.
|
|
127
154
|
*/
|
|
128
|
-
|
|
155
|
+
user_id: string;
|
|
129
156
|
/**
|
|
130
157
|
* The session ID associated with this event.
|
|
131
158
|
*/
|
|
132
|
-
|
|
159
|
+
session_id?: string;
|
|
133
160
|
/**
|
|
134
161
|
* The platform this event was generated from.
|
|
135
162
|
*/
|
|
@@ -137,15 +164,15 @@ export interface MGMEvent {
|
|
|
137
164
|
/**
|
|
138
165
|
* The app version string.
|
|
139
166
|
*/
|
|
140
|
-
|
|
167
|
+
app_version?: string;
|
|
141
168
|
/**
|
|
142
169
|
* The app build number (separate from version).
|
|
143
170
|
*/
|
|
144
|
-
|
|
171
|
+
app_build_number?: string;
|
|
145
172
|
/**
|
|
146
173
|
* The OS version string.
|
|
147
174
|
*/
|
|
148
|
-
|
|
175
|
+
os_version?: string;
|
|
149
176
|
/**
|
|
150
177
|
* The environment name.
|
|
151
178
|
*/
|
|
@@ -153,7 +180,7 @@ export interface MGMEvent {
|
|
|
153
180
|
/**
|
|
154
181
|
* The device manufacturer (e.g., "Apple", "Samsung").
|
|
155
182
|
*/
|
|
156
|
-
|
|
183
|
+
device_manufacturer?: string;
|
|
157
184
|
/**
|
|
158
185
|
* The user's locale (e.g., "en-US").
|
|
159
186
|
*/
|
|
@@ -173,13 +200,13 @@ export interface MGMEvent {
|
|
|
173
200
|
*/
|
|
174
201
|
export interface MGMEventContext {
|
|
175
202
|
platform: Platform;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
203
|
+
app_version?: string;
|
|
204
|
+
app_build_number?: string;
|
|
205
|
+
os_version?: string;
|
|
206
|
+
user_id: string;
|
|
207
|
+
session_id?: string;
|
|
181
208
|
environment: string;
|
|
182
|
-
|
|
209
|
+
device_manufacturer?: string;
|
|
183
210
|
locale?: string;
|
|
184
211
|
timezone?: string;
|
|
185
212
|
}
|
|
@@ -299,7 +326,7 @@ export declare const DefaultConfiguration: {
|
|
|
299
326
|
readonly flushInterval: 30;
|
|
300
327
|
readonly maxStoredEvents: 10000;
|
|
301
328
|
readonly enableDebugLogging: false;
|
|
302
|
-
readonly trackAppLifecycleEvents:
|
|
329
|
+
readonly trackAppLifecycleEvents: false;
|
|
303
330
|
};
|
|
304
331
|
/**
|
|
305
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;
|
|
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,
|
|
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', () => {
|
|
@@ -355,6 +435,78 @@ describe('MostlyGoodMetrics', () => {
|
|
|
355
435
|
const count = await storage.eventCount();
|
|
356
436
|
expect(count).toBe(0);
|
|
357
437
|
});
|
|
438
|
+
|
|
439
|
+
it('should call onError callback when network error occurs', async () => {
|
|
440
|
+
const onError = jest.fn();
|
|
441
|
+
MostlyGoodMetrics.reset();
|
|
442
|
+
MostlyGoodMetrics.configure({
|
|
443
|
+
apiKey: 'test-key',
|
|
444
|
+
storage,
|
|
445
|
+
networkClient,
|
|
446
|
+
trackAppLifecycleEvents: false,
|
|
447
|
+
onError,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
networkClient.sendResult = {
|
|
451
|
+
success: false,
|
|
452
|
+
error: { name: 'MGMError', message: 'Server error', type: 'SERVER_ERROR' } as never,
|
|
453
|
+
shouldRetry: true,
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
MostlyGoodMetrics.track('test_event');
|
|
457
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
458
|
+
await MostlyGoodMetrics.flush();
|
|
459
|
+
|
|
460
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
461
|
+
expect(onError).toHaveBeenCalledWith(expect.objectContaining({ type: 'SERVER_ERROR' }));
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it('should not call onError callback on success', async () => {
|
|
465
|
+
const onError = jest.fn();
|
|
466
|
+
MostlyGoodMetrics.reset();
|
|
467
|
+
MostlyGoodMetrics.configure({
|
|
468
|
+
apiKey: 'test-key',
|
|
469
|
+
storage,
|
|
470
|
+
networkClient,
|
|
471
|
+
trackAppLifecycleEvents: false,
|
|
472
|
+
onError,
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
networkClient.sendResult = { success: true };
|
|
476
|
+
|
|
477
|
+
MostlyGoodMetrics.track('test_event');
|
|
478
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
479
|
+
await MostlyGoodMetrics.flush();
|
|
480
|
+
|
|
481
|
+
expect(onError).not.toHaveBeenCalled();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('should catch exceptions thrown by onError callback', async () => {
|
|
485
|
+
const onError = jest.fn().mockImplementation(() => {
|
|
486
|
+
throw new Error('Callback error');
|
|
487
|
+
});
|
|
488
|
+
MostlyGoodMetrics.reset();
|
|
489
|
+
MostlyGoodMetrics.configure({
|
|
490
|
+
apiKey: 'test-key',
|
|
491
|
+
storage,
|
|
492
|
+
networkClient,
|
|
493
|
+
trackAppLifecycleEvents: false,
|
|
494
|
+
onError,
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
networkClient.sendResult = {
|
|
498
|
+
success: false,
|
|
499
|
+
error: { name: 'MGMError', message: 'Server error', type: 'SERVER_ERROR' } as never,
|
|
500
|
+
shouldRetry: true,
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
MostlyGoodMetrics.track('test_event');
|
|
504
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
505
|
+
|
|
506
|
+
// Should not throw
|
|
507
|
+
await expect(MostlyGoodMetrics.flush()).resolves.not.toThrow();
|
|
508
|
+
expect(onError).toHaveBeenCalled();
|
|
509
|
+
});
|
|
358
510
|
});
|
|
359
511
|
|
|
360
512
|
describe('startNewSession', () => {
|
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();
|
|
@@ -439,6 +457,15 @@ export class MostlyGoodMetrics {
|
|
|
439
457
|
} else {
|
|
440
458
|
logger.warn(`Failed to send events: ${result.error.message}`);
|
|
441
459
|
|
|
460
|
+
// Call onError callback if configured
|
|
461
|
+
if (this.config.onError) {
|
|
462
|
+
try {
|
|
463
|
+
this.config.onError(result.error);
|
|
464
|
+
} catch (e) {
|
|
465
|
+
logger.error('Error in onError callback', e);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
442
469
|
if (!result.shouldRetry) {
|
|
443
470
|
// Drop events on non-retryable errors (4xx)
|
|
444
471
|
logger.warn('Dropping events due to non-retryable error');
|
|
@@ -457,10 +484,12 @@ export class MostlyGoodMetrics {
|
|
|
457
484
|
private buildPayload(events: MGMEvent[]): MGMEventsPayload {
|
|
458
485
|
const context: MGMEventContext = {
|
|
459
486
|
platform: this.config.platform,
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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,
|
|
464
493
|
environment: this.config.environment,
|
|
465
494
|
locale: getLocale(),
|
|
466
495
|
timezone: getTimezone(),
|
package/src/index.ts
CHANGED
package/src/network.test.ts
CHANGED
|
@@ -93,11 +93,11 @@ describe('FetchNetworkClient', () => {
|
|
|
93
93
|
expect(capturedHeaders['X-MGM-Platform-Version']).toBe('17.0');
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
it('should include
|
|
96
|
+
it('should include Authorization Bearer header with API key', async () => {
|
|
97
97
|
const config = createMockConfig({ apiKey: 'my-secret-key' });
|
|
98
98
|
await networkClient.sendEvents(createMockPayload(), config);
|
|
99
99
|
|
|
100
|
-
expect(capturedHeaders['
|
|
100
|
+
expect(capturedHeaders['Authorization']).toBe('Bearer my-secret-key');
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
it('should include X-MGM-Bundle-Id when bundleId is configured', async () => {
|
|
@@ -124,7 +124,7 @@ describe('FetchNetworkClient', () => {
|
|
|
124
124
|
});
|
|
125
125
|
await networkClient.sendEvents(createMockPayload(), config);
|
|
126
126
|
|
|
127
|
-
expect(capturedHeaders['
|
|
127
|
+
expect(capturedHeaders['Authorization']).toBe('Bearer test-key');
|
|
128
128
|
expect(capturedHeaders['X-MGM-SDK']).toBe('javascript');
|
|
129
129
|
expect(capturedHeaders['X-MGM-SDK-Version']).toBeDefined();
|
|
130
130
|
expect(capturedHeaders['X-MGM-Platform']).toBe('web');
|
package/src/network.ts
CHANGED
|
@@ -78,7 +78,7 @@ export class FetchNetworkClient implements INetworkClient {
|
|
|
78
78
|
const sdkVersion = config.sdkVersion || SDK_VERSION;
|
|
79
79
|
const headers: Record<string, string> = {
|
|
80
80
|
'Content-Type': 'application/json',
|
|
81
|
-
|
|
81
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
82
82
|
'X-MGM-SDK': config.sdk,
|
|
83
83
|
'X-MGM-SDK-Version': sdkVersion,
|
|
84
84
|
'X-MGM-Platform': config.platform,
|