@tracelog/lib 0.4.0 → 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/dist/browser/tracelog.js +620 -658
- package/dist/cjs/api.d.ts +1 -53
- package/dist/cjs/api.js +0 -59
- package/dist/cjs/app.constants.d.ts +1 -1
- package/dist/cjs/app.d.ts +1 -5
- package/dist/cjs/app.js +4 -12
- package/dist/cjs/constants/api.constants.d.ts +5 -2
- package/dist/cjs/constants/api.constants.js +5 -14
- package/dist/cjs/constants/config.constants.d.ts +3 -3
- package/dist/cjs/constants/config.constants.js +3 -3
- package/dist/cjs/constants/error.constants.d.ts +7 -2
- package/dist/cjs/constants/error.constants.js +13 -2
- package/dist/cjs/handlers/click.handler.js +0 -6
- package/dist/cjs/handlers/error.handler.js +9 -0
- package/dist/cjs/handlers/scroll.handler.js +0 -5
- package/dist/cjs/handlers/session.handler.js +5 -2
- package/dist/cjs/integrations/google-analytics.integration.d.ts +1 -1
- package/dist/cjs/integrations/google-analytics.integration.js +2 -1
- package/dist/cjs/managers/api.manager.d.ts +1 -1
- package/dist/cjs/managers/api.manager.js +3 -3
- package/dist/cjs/managers/config.builder.d.ts +33 -0
- package/dist/cjs/managers/config.builder.js +116 -0
- package/dist/cjs/managers/config.manager.d.ts +13 -14
- package/dist/cjs/managers/config.manager.js +52 -58
- package/dist/cjs/managers/event.manager.d.ts +1 -46
- package/dist/cjs/managers/event.manager.js +15 -70
- package/dist/cjs/managers/sender.manager.d.ts +1 -28
- package/dist/cjs/managers/sender.manager.js +43 -73
- package/dist/cjs/managers/session.manager.d.ts +2 -49
- package/dist/cjs/managers/session.manager.js +42 -83
- package/dist/cjs/managers/state.manager.d.ts +1 -28
- package/dist/cjs/managers/state.manager.js +5 -33
- package/dist/cjs/managers/storage.manager.d.ts +6 -0
- package/dist/cjs/managers/storage.manager.js +18 -1
- package/dist/cjs/public-api.d.ts +1 -1
- package/dist/cjs/test-bridge.d.ts +3 -2
- package/dist/cjs/test-bridge.js +34 -7
- package/dist/cjs/types/api.types.d.ts +24 -8
- package/dist/cjs/types/api.types.js +24 -8
- package/dist/cjs/types/event.types.d.ts +2 -4
- package/dist/cjs/types/event.types.js +0 -1
- package/dist/cjs/types/test-bridge.types.d.ts +2 -1
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +1 -2
- package/dist/cjs/utils/logging/debug-logger.utils.js +2 -3
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +1 -26
- package/dist/cjs/utils/validations/config-validations.utils.js +5 -117
- package/dist/cjs/utils/validations/event-validations.utils.d.ts +2 -2
- package/dist/cjs/utils/validations/metadata-validations.utils.d.ts +3 -3
- package/dist/cjs/utils/validations/metadata-validations.utils.js +41 -3
- package/dist/esm/api.d.ts +1 -53
- package/dist/esm/api.js +0 -59
- package/dist/esm/app.constants.d.ts +1 -1
- package/dist/esm/app.d.ts +1 -5
- package/dist/esm/app.js +5 -13
- package/dist/esm/constants/api.constants.d.ts +5 -2
- package/dist/esm/constants/api.constants.js +5 -13
- package/dist/esm/constants/config.constants.d.ts +3 -3
- package/dist/esm/constants/config.constants.js +3 -3
- package/dist/esm/constants/error.constants.d.ts +7 -2
- package/dist/esm/constants/error.constants.js +12 -1
- package/dist/esm/handlers/click.handler.js +0 -6
- package/dist/esm/handlers/error.handler.js +10 -1
- package/dist/esm/handlers/scroll.handler.js +0 -5
- package/dist/esm/handlers/session.handler.js +5 -2
- package/dist/esm/integrations/google-analytics.integration.d.ts +1 -1
- package/dist/esm/integrations/google-analytics.integration.js +2 -1
- package/dist/esm/managers/api.manager.d.ts +1 -1
- package/dist/esm/managers/api.manager.js +3 -3
- package/dist/esm/managers/config.builder.d.ts +33 -0
- package/dist/esm/managers/config.builder.js +112 -0
- package/dist/esm/managers/config.manager.d.ts +13 -14
- package/dist/esm/managers/config.manager.js +54 -60
- package/dist/esm/managers/event.manager.d.ts +1 -46
- package/dist/esm/managers/event.manager.js +15 -70
- package/dist/esm/managers/sender.manager.d.ts +1 -28
- package/dist/esm/managers/sender.manager.js +44 -74
- package/dist/esm/managers/session.manager.d.ts +2 -49
- package/dist/esm/managers/session.manager.js +42 -83
- package/dist/esm/managers/state.manager.d.ts +1 -28
- package/dist/esm/managers/state.manager.js +4 -33
- package/dist/esm/managers/storage.manager.d.ts +6 -0
- package/dist/esm/managers/storage.manager.js +18 -1
- package/dist/esm/public-api.d.ts +1 -1
- package/dist/esm/test-bridge.d.ts +3 -2
- package/dist/esm/test-bridge.js +34 -7
- package/dist/esm/types/api.types.d.ts +24 -8
- package/dist/esm/types/api.types.js +24 -8
- package/dist/esm/types/event.types.d.ts +2 -4
- package/dist/esm/types/event.types.js +0 -1
- package/dist/esm/types/test-bridge.types.d.ts +2 -1
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +1 -2
- package/dist/esm/utils/logging/debug-logger.utils.js +3 -4
- package/dist/esm/utils/validations/config-validations.utils.d.ts +1 -26
- package/dist/esm/utils/validations/config-validations.utils.js +5 -114
- package/dist/esm/utils/validations/event-validations.utils.d.ts +2 -2
- package/dist/esm/utils/validations/metadata-validations.utils.d.ts +3 -3
- package/dist/esm/utils/validations/metadata-validations.utils.js +41 -3
- package/package.json +1 -1
|
@@ -1,41 +1,25 @@
|
|
|
1
1
|
import { debugLog } from '../utils/logging';
|
|
2
2
|
import { DEFAULT_SAMPLING_RATE } from '../constants';
|
|
3
|
-
/**
|
|
4
|
-
* Global state store shared across all TraceLog components
|
|
5
|
-
*/
|
|
6
3
|
const globalState = {};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
4
|
+
export function getGlobalState() {
|
|
5
|
+
return globalState;
|
|
6
|
+
}
|
|
11
7
|
export function resetGlobalState() {
|
|
12
8
|
Object.keys(globalState).forEach((key) => {
|
|
13
9
|
delete globalState[key];
|
|
14
10
|
});
|
|
15
11
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Abstract base class providing state management capabilities to TraceLog components.
|
|
18
|
-
*
|
|
19
|
-
* All managers and handlers extend this class to access and modify the shared global state.
|
|
20
|
-
* State operations are synchronous and thread-safe within the single-threaded browser environment.
|
|
21
|
-
*/
|
|
22
12
|
export class StateManager {
|
|
23
|
-
/**
|
|
24
|
-
* Gets a value from the global state
|
|
25
|
-
*/
|
|
26
13
|
get(key) {
|
|
27
14
|
return globalState[key];
|
|
28
15
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Sets a value in the global state
|
|
31
|
-
*/
|
|
32
16
|
set(key, value) {
|
|
33
17
|
const oldValue = globalState[key];
|
|
34
18
|
if (key === 'config' && value) {
|
|
35
19
|
const configValue = value;
|
|
36
20
|
if (configValue) {
|
|
37
21
|
const samplingRate = configValue.samplingRate ?? DEFAULT_SAMPLING_RATE;
|
|
38
|
-
const normalizedSamplingRate = samplingRate
|
|
22
|
+
const normalizedSamplingRate = samplingRate < 0 || samplingRate > 1 ? DEFAULT_SAMPLING_RATE : samplingRate;
|
|
39
23
|
const hasNormalizedSampling = normalizedSamplingRate !== samplingRate;
|
|
40
24
|
if (hasNormalizedSampling) {
|
|
41
25
|
const normalizedConfig = { ...configValue, samplingRate: normalizedSamplingRate };
|
|
@@ -52,7 +36,6 @@ export class StateManager {
|
|
|
52
36
|
else {
|
|
53
37
|
globalState[key] = value;
|
|
54
38
|
}
|
|
55
|
-
// Log critical state changes for debugging
|
|
56
39
|
if (this.isCriticalStateKey(key) && this.shouldLog(oldValue, globalState[key])) {
|
|
57
40
|
debugLog.debug('StateManager', 'State updated', {
|
|
58
41
|
key,
|
|
@@ -61,27 +44,15 @@ export class StateManager {
|
|
|
61
44
|
});
|
|
62
45
|
}
|
|
63
46
|
}
|
|
64
|
-
/**
|
|
65
|
-
* Gets the entire state object (for debugging purposes)
|
|
66
|
-
*/
|
|
67
47
|
getState() {
|
|
68
48
|
return { ...globalState };
|
|
69
49
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Checks if a state key is considered critical for logging
|
|
72
|
-
*/
|
|
73
50
|
isCriticalStateKey(key) {
|
|
74
51
|
return key === 'sessionId' || key === 'config' || key === 'hasStartSession';
|
|
75
52
|
}
|
|
76
|
-
/**
|
|
77
|
-
* Determines if a state change should be logged
|
|
78
|
-
*/
|
|
79
53
|
shouldLog(oldValue, newValue) {
|
|
80
54
|
return oldValue !== newValue;
|
|
81
55
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Formats values for logging (avoiding large object dumps)
|
|
84
|
-
*/
|
|
85
56
|
formatLogValue(key, value) {
|
|
86
57
|
if (key === 'config') {
|
|
87
58
|
return value ? '(configured)' : '(not configured)';
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
export declare class StorageManager {
|
|
7
7
|
private readonly storage;
|
|
8
8
|
private readonly fallbackStorage;
|
|
9
|
+
private hasQuotaExceededError;
|
|
9
10
|
constructor();
|
|
10
11
|
/**
|
|
11
12
|
* Retrieves an item from storage
|
|
@@ -27,6 +28,11 @@ export declare class StorageManager {
|
|
|
27
28
|
* Checks if storage is available
|
|
28
29
|
*/
|
|
29
30
|
isAvailable(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Checks if a QuotaExceededError has occurred
|
|
33
|
+
* This indicates localStorage is full and data may not persist
|
|
34
|
+
*/
|
|
35
|
+
hasQuotaError(): boolean;
|
|
30
36
|
/**
|
|
31
37
|
* Initialize localStorage with feature detection
|
|
32
38
|
*/
|
|
@@ -7,6 +7,7 @@ import { debugLog } from '../utils/logging/debug-logger.utils';
|
|
|
7
7
|
export class StorageManager {
|
|
8
8
|
constructor() {
|
|
9
9
|
this.fallbackStorage = new Map();
|
|
10
|
+
this.hasQuotaExceededError = false;
|
|
10
11
|
this.storage = this.initializeStorage();
|
|
11
12
|
if (!this.storage) {
|
|
12
13
|
debugLog.warn('StorageManager', 'localStorage not available, using memory fallback');
|
|
@@ -38,7 +39,16 @@ export class StorageManager {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
catch (error) {
|
|
41
|
-
|
|
42
|
+
if (error instanceof DOMException && error.name === 'QuotaExceededError') {
|
|
43
|
+
this.hasQuotaExceededError = true;
|
|
44
|
+
debugLog.error('StorageManager', 'localStorage quota exceeded - data will not persist after reload', {
|
|
45
|
+
key,
|
|
46
|
+
valueSize: value.length,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
debugLog.warn('StorageManager', 'Failed to set item, using fallback', { key, error });
|
|
51
|
+
}
|
|
42
52
|
}
|
|
43
53
|
// Always update fallback for consistency
|
|
44
54
|
this.fallbackStorage.set(key, value);
|
|
@@ -89,6 +99,13 @@ export class StorageManager {
|
|
|
89
99
|
isAvailable() {
|
|
90
100
|
return this.storage !== null;
|
|
91
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Checks if a QuotaExceededError has occurred
|
|
104
|
+
* This indicates localStorage is full and data may not persist
|
|
105
|
+
*/
|
|
106
|
+
hasQuotaError() {
|
|
107
|
+
return this.hasQuotaExceededError;
|
|
108
|
+
}
|
|
92
109
|
/**
|
|
93
110
|
* Initialize localStorage with feature detection
|
|
94
111
|
*/
|
package/dist/esm/public-api.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export * from './app.constants';
|
|
|
2
2
|
export * from './app.types';
|
|
3
3
|
export declare const tracelog: {
|
|
4
4
|
init: (appConfig: import("./app.types").AppConfig) => Promise<void>;
|
|
5
|
-
event: (name: string, metadata?: Record<string, import("./app.types").MetadataType>) => void;
|
|
5
|
+
event: (name: string, metadata?: Record<string, import("./app.types").MetadataType> | Record<string, import("./app.types").MetadataType>[]) => void;
|
|
6
6
|
on: <K extends keyof import("./types").EmitterMap>(event: K, callback: import("./types").EmitterCallback<import("./types").EmitterMap[K]>) => void;
|
|
7
7
|
off: <K extends keyof import("./types").EmitterMap>(event: K, callback: import("./types").EmitterCallback<import("./types").EmitterMap[K]>) => void;
|
|
8
8
|
isInitialized: () => boolean;
|
|
@@ -17,11 +17,12 @@ export declare class TestBridge extends App implements TraceLogTestBridge {
|
|
|
17
17
|
private _isDestroying;
|
|
18
18
|
constructor(isInitializing: boolean, isDestroying: boolean);
|
|
19
19
|
isInitializing(): boolean;
|
|
20
|
-
sendCustomEvent(name: string, data?: Record<string, unknown>): void;
|
|
20
|
+
sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
21
21
|
getSessionData(): Record<string, unknown> | null;
|
|
22
22
|
setSessionTimeout(timeout: number): void;
|
|
23
23
|
getQueueLength(): number;
|
|
24
24
|
forceInitLock(enabled?: boolean): void;
|
|
25
|
+
simulatePersistedEvents(events: any[]): void;
|
|
25
26
|
get<T extends keyof State>(key: T): State[T];
|
|
26
27
|
getStorageManager(): StorageManager | null;
|
|
27
28
|
getEventManager(): EventManager | null;
|
|
@@ -32,7 +33,7 @@ export declare class TestBridge extends App implements TraceLogTestBridge {
|
|
|
32
33
|
getPerformanceHandler(): PerformanceHandler | null;
|
|
33
34
|
getErrorHandler(): ErrorHandler | null;
|
|
34
35
|
getGoogleAnalytics(): GoogleAnalyticsIntegration | null;
|
|
35
|
-
destroy(): Promise<void>;
|
|
36
|
+
destroy(force?: boolean): Promise<void>;
|
|
36
37
|
/**
|
|
37
38
|
* Helper to safely access managers/handlers and convert undefined to null
|
|
38
39
|
*/
|
package/dist/esm/test-bridge.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { App } from './app';
|
|
2
|
-
import { normalizeConfig } from './utils';
|
|
3
2
|
/**
|
|
4
3
|
* Test bridge for E2E testing
|
|
5
4
|
*/
|
|
@@ -14,7 +13,10 @@ export class TestBridge extends App {
|
|
|
14
13
|
return this._isInitializing;
|
|
15
14
|
}
|
|
16
15
|
sendCustomEvent(name, data) {
|
|
17
|
-
|
|
16
|
+
// Silently ignore events after destroy instead of throwing error
|
|
17
|
+
if (!this.initialized) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
18
20
|
super.sendCustomEvent(name, data);
|
|
19
21
|
}
|
|
20
22
|
getSessionData() {
|
|
@@ -27,8 +29,7 @@ export class TestBridge extends App {
|
|
|
27
29
|
setSessionTimeout(timeout) {
|
|
28
30
|
const config = this.get('config');
|
|
29
31
|
if (config) {
|
|
30
|
-
|
|
31
|
-
this.set('config', normalizedConfig);
|
|
32
|
+
this.set('config', { ...config, sessionTimeout: timeout });
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
getQueueLength() {
|
|
@@ -37,6 +38,29 @@ export class TestBridge extends App {
|
|
|
37
38
|
forceInitLock(enabled = true) {
|
|
38
39
|
this._isInitializing = enabled;
|
|
39
40
|
}
|
|
41
|
+
simulatePersistedEvents(events) {
|
|
42
|
+
const storageManager = this.managers?.storage;
|
|
43
|
+
if (!storageManager) {
|
|
44
|
+
throw new Error('Storage manager not available');
|
|
45
|
+
}
|
|
46
|
+
const projectId = this.get('config')?.id;
|
|
47
|
+
const userId = this.get('userId');
|
|
48
|
+
const sessionId = this.get('sessionId');
|
|
49
|
+
if (!projectId || !userId) {
|
|
50
|
+
throw new Error('Project ID or User ID not available. Initialize TraceLog first.');
|
|
51
|
+
}
|
|
52
|
+
// Build the persisted data structure matching what SenderManager expects
|
|
53
|
+
const persistedData = {
|
|
54
|
+
userId,
|
|
55
|
+
sessionId: sessionId || `test-session-${Date.now()}`,
|
|
56
|
+
device: 'desktop',
|
|
57
|
+
events,
|
|
58
|
+
timestamp: Date.now(),
|
|
59
|
+
};
|
|
60
|
+
// Store in the same format as SenderManager.persistEvents()
|
|
61
|
+
const storageKey = `tl:${projectId}:queue:${userId}`;
|
|
62
|
+
storageManager.setItem(storageKey, JSON.stringify(persistedData));
|
|
63
|
+
}
|
|
40
64
|
get(key) {
|
|
41
65
|
return super.get(key);
|
|
42
66
|
}
|
|
@@ -70,12 +94,15 @@ export class TestBridge extends App {
|
|
|
70
94
|
getGoogleAnalytics() {
|
|
71
95
|
return this.safeAccess(this.integrations?.googleAnalytics);
|
|
72
96
|
}
|
|
73
|
-
async destroy() {
|
|
74
|
-
|
|
97
|
+
async destroy(force = false) {
|
|
98
|
+
// If not initialized and not forcing, silently return (no-op)
|
|
99
|
+
if (!this.initialized && !force) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
75
102
|
this.ensureNotDestroying();
|
|
76
103
|
this._isDestroying = true;
|
|
77
104
|
try {
|
|
78
|
-
await super.destroy();
|
|
105
|
+
await super.destroy(force);
|
|
79
106
|
}
|
|
80
107
|
finally {
|
|
81
108
|
this._isDestroying = false;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Special project IDs for testing and development
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* All automatically force mode: 'debug' but differ in HTTP behavior:
|
|
5
5
|
* - Skip: NO network calls (pure offline testing)
|
|
6
6
|
* - Localhost: Makes network calls to local server (integration testing)
|
|
7
|
+
* - Fail: Makes network calls that intentionally fail (persistence testing)
|
|
7
8
|
*/
|
|
8
9
|
export declare enum SpecialProjectId {
|
|
9
10
|
/**
|
|
@@ -20,17 +21,32 @@ export declare enum SpecialProjectId {
|
|
|
20
21
|
*/
|
|
21
22
|
Skip = "skip",
|
|
22
23
|
/**
|
|
23
|
-
* Value: 'localhost:'
|
|
24
|
+
* Value: 'localhost:8080'
|
|
24
25
|
*
|
|
25
|
-
* Makes HTTP calls to local development server
|
|
26
|
-
*
|
|
27
|
-
* Converts to http://localhost:PORT/config for requests
|
|
26
|
+
* Makes HTTP calls to local development server on port 8080
|
|
27
|
+
* Converts to http://localhost:8080/config for requests
|
|
28
28
|
* Requires origin to be in ALLOWED_ORIGINS list, forces debug mode
|
|
29
29
|
* Perfect for local development with running middleware
|
|
30
30
|
*
|
|
31
31
|
* @example
|
|
32
|
-
* await TraceLog.init({ id:
|
|
33
|
-
* //
|
|
32
|
+
* await TraceLog.init({ id: SpecialProjectId.Localhost });
|
|
33
|
+
* // or
|
|
34
|
+
* await TraceLog.init({ id: 'localhost:8080' });
|
|
35
|
+
* // Makes requests to: http://localhost:8080/config
|
|
36
|
+
*/
|
|
37
|
+
Localhost = "localhost:8080",
|
|
38
|
+
/**
|
|
39
|
+
* Value: 'localhost:9999'
|
|
40
|
+
*
|
|
41
|
+
* Makes HTTP calls to non-existent server (port 9999)
|
|
42
|
+
* All HTTP requests will fail naturally, triggering persistence
|
|
43
|
+
* Forces debug mode, perfect for testing event persistence & recovery
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* await TraceLog.init({ id: SpecialProjectId.Fail });
|
|
47
|
+
* // or
|
|
48
|
+
* await TraceLog.init({ id: 'localhost:9999' });
|
|
49
|
+
* // Makes requests to: http://localhost:9999 (will fail)
|
|
34
50
|
*/
|
|
35
|
-
|
|
51
|
+
Fail = "localhost:9999"
|
|
36
52
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Special project IDs for testing and development
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* All automatically force mode: 'debug' but differ in HTTP behavior:
|
|
5
5
|
* - Skip: NO network calls (pure offline testing)
|
|
6
6
|
* - Localhost: Makes network calls to local server (integration testing)
|
|
7
|
+
* - Fail: Makes network calls that intentionally fail (persistence testing)
|
|
7
8
|
*/
|
|
8
9
|
export var SpecialProjectId;
|
|
9
10
|
(function (SpecialProjectId) {
|
|
@@ -21,17 +22,32 @@ export var SpecialProjectId;
|
|
|
21
22
|
*/
|
|
22
23
|
SpecialProjectId["Skip"] = "skip";
|
|
23
24
|
/**
|
|
24
|
-
* Value: 'localhost:'
|
|
25
|
+
* Value: 'localhost:8080'
|
|
25
26
|
*
|
|
26
|
-
* Makes HTTP calls to local development server
|
|
27
|
-
*
|
|
28
|
-
* Converts to http://localhost:PORT/config for requests
|
|
27
|
+
* Makes HTTP calls to local development server on port 8080
|
|
28
|
+
* Converts to http://localhost:8080/config for requests
|
|
29
29
|
* Requires origin to be in ALLOWED_ORIGINS list, forces debug mode
|
|
30
30
|
* Perfect for local development with running middleware
|
|
31
31
|
*
|
|
32
32
|
* @example
|
|
33
|
-
* await TraceLog.init({ id:
|
|
34
|
-
* //
|
|
33
|
+
* await TraceLog.init({ id: SpecialProjectId.Localhost });
|
|
34
|
+
* // or
|
|
35
|
+
* await TraceLog.init({ id: 'localhost:8080' });
|
|
36
|
+
* // Makes requests to: http://localhost:8080/config
|
|
37
|
+
*/
|
|
38
|
+
SpecialProjectId["Localhost"] = "localhost:8080";
|
|
39
|
+
/**
|
|
40
|
+
* Value: 'localhost:9999'
|
|
41
|
+
*
|
|
42
|
+
* Makes HTTP calls to non-existent server (port 9999)
|
|
43
|
+
* All HTTP requests will fail naturally, triggering persistence
|
|
44
|
+
* Forces debug mode, perfect for testing event persistence & recovery
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* await TraceLog.init({ id: SpecialProjectId.Fail });
|
|
48
|
+
* // or
|
|
49
|
+
* await TraceLog.init({ id: 'localhost:9999' });
|
|
50
|
+
* // Makes requests to: http://localhost:9999 (will fail)
|
|
35
51
|
*/
|
|
36
|
-
SpecialProjectId["
|
|
52
|
+
SpecialProjectId["Fail"] = "localhost:9999";
|
|
37
53
|
})(SpecialProjectId || (SpecialProjectId = {}));
|
|
@@ -17,8 +17,7 @@ export declare enum ScrollDirection {
|
|
|
17
17
|
}
|
|
18
18
|
export declare enum ErrorType {
|
|
19
19
|
JS_ERROR = "js_error",
|
|
20
|
-
PROMISE_REJECTION = "promise_rejection"
|
|
21
|
-
NETWORK_ERROR = "network_error"
|
|
20
|
+
PROMISE_REJECTION = "promise_rejection"
|
|
22
21
|
}
|
|
23
22
|
export interface ScrollData {
|
|
24
23
|
depth: number;
|
|
@@ -48,7 +47,7 @@ export interface ClickTrackingElementData {
|
|
|
48
47
|
}
|
|
49
48
|
export interface CustomEventData {
|
|
50
49
|
name: string;
|
|
51
|
-
metadata?: Record<string, MetadataType
|
|
50
|
+
metadata?: Record<string, MetadataType> | Record<string, MetadataType>[];
|
|
52
51
|
}
|
|
53
52
|
export interface WebVitalsData {
|
|
54
53
|
type: WebVitalType;
|
|
@@ -94,7 +93,6 @@ export interface EventData {
|
|
|
94
93
|
custom_event?: CustomEventData;
|
|
95
94
|
web_vitals?: WebVitalsData;
|
|
96
95
|
page_view?: PageViewData;
|
|
97
|
-
session_start_recovered?: boolean;
|
|
98
96
|
session_end_reason?: SessionEndReason;
|
|
99
97
|
error_data?: ErrorData;
|
|
100
98
|
utm?: UTM;
|
|
@@ -18,13 +18,14 @@ export interface TraceLogTestBridge {
|
|
|
18
18
|
init(config: AppConfig): Promise<void>;
|
|
19
19
|
destroy(): Promise<void>;
|
|
20
20
|
isInitializing(): boolean;
|
|
21
|
-
sendCustomEvent(name: string, data?: Record<string, unknown>): void;
|
|
21
|
+
sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
22
22
|
on(event: string, callback: (data: any) => void): void;
|
|
23
23
|
off(event: string, callback: (data: any) => void): void;
|
|
24
24
|
getSessionData(): Record<string, unknown> | null;
|
|
25
25
|
setSessionTimeout(timeout: number): void;
|
|
26
26
|
getQueueLength(): number;
|
|
27
27
|
forceInitLock(enabled?: boolean): void;
|
|
28
|
+
simulatePersistedEvents(events: any[]): void;
|
|
28
29
|
get<T extends keyof State>(key: T): State[T];
|
|
29
30
|
getStorageManager(): TraceLogStorageManager | null;
|
|
30
31
|
getEventManager(): EventManager | null;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
declare class DebugLogger extends StateManager {
|
|
1
|
+
declare class DebugLogger {
|
|
3
2
|
clientError: (ns: string, msg: string, data?: unknown) => void;
|
|
4
3
|
clientWarn: (ns: string, msg: string, data?: unknown) => void;
|
|
5
4
|
info: (ns: string, msg: string, data?: unknown) => void;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
class DebugLogger
|
|
1
|
+
import { getGlobalState } from '../../managers/state.manager';
|
|
2
|
+
class DebugLogger {
|
|
3
3
|
constructor() {
|
|
4
|
-
super(...arguments);
|
|
5
4
|
this.clientError = (ns, msg, data) => this.log('CLIENT_ERROR', ns, msg, data);
|
|
6
5
|
this.clientWarn = (ns, msg, data) => this.log('CLIENT_WARN', ns, msg, data);
|
|
7
6
|
this.info = (ns, msg, data) => this.log('INFO', ns, msg, data);
|
|
@@ -11,7 +10,7 @@ class DebugLogger extends StateManager {
|
|
|
11
10
|
this.verbose = (ns, msg, data) => this.log('VERBOSE', ns, msg, data);
|
|
12
11
|
}
|
|
13
12
|
log(level, ns, msg, data) {
|
|
14
|
-
const mode =
|
|
13
|
+
const mode = getGlobalState()?.config?.mode;
|
|
15
14
|
if (!this.shouldShow(level, mode))
|
|
16
15
|
return;
|
|
17
16
|
const formattedMsg = `[TraceLog:${ns}] ${msg}`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AppConfig,
|
|
1
|
+
import { AppConfig, ApiConfig } from '../../types';
|
|
2
2
|
/**
|
|
3
3
|
* Validates the app configuration object (before normalization)
|
|
4
4
|
* This validates the structure and basic types but allows for normalization afterward
|
|
@@ -16,31 +16,6 @@ export declare const validateAppConfig: (config: AppConfig) => void;
|
|
|
16
16
|
* @throws {AppConfigValidationError} If other configuration validation fails
|
|
17
17
|
*/
|
|
18
18
|
export declare const validateAndNormalizeConfig: (config: AppConfig) => AppConfig;
|
|
19
|
-
/**
|
|
20
|
-
* Validates a complete configuration object
|
|
21
|
-
* @param config - The configuration to validate
|
|
22
|
-
* @returns Validation result with errors and warnings
|
|
23
|
-
*/
|
|
24
|
-
export declare const validateConfig: (config: Config) => {
|
|
25
|
-
errors: string[];
|
|
26
|
-
warnings: string[];
|
|
27
|
-
samplingRate: number;
|
|
28
|
-
};
|
|
29
|
-
export declare const normalizeConfig: (config: Config) => {
|
|
30
|
-
config: Config;
|
|
31
|
-
errors: string[];
|
|
32
|
-
warnings: string[];
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Validates the final configuration
|
|
36
|
-
* @param config - The configuration to validate
|
|
37
|
-
* @returns Validation result with errors and warnings
|
|
38
|
-
*/
|
|
39
|
-
export declare const validateFinalConfig: (config: Config) => {
|
|
40
|
-
errors: string[];
|
|
41
|
-
warnings: string[];
|
|
42
|
-
samplingRate: number;
|
|
43
|
-
};
|
|
44
19
|
/**
|
|
45
20
|
* Type guard to check if a JSON response is a valid API config
|
|
46
21
|
* @param json - The JSON to validate
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MAX_SESSION_TIMEOUT_MS, MIN_SESSION_TIMEOUT_MS, VALIDATION_MESSAGES } from '../../constants';
|
|
2
2
|
import { Mode } from '../../types';
|
|
3
3
|
import { ProjectIdValidationError, AppConfigValidationError, SessionTimeoutValidationError, SamplingRateValidationError, IntegrationValidationError, } from '../../types/validation-error.types';
|
|
4
4
|
import { debugLog } from '../logging';
|
|
@@ -204,118 +204,6 @@ export const validateAndNormalizeConfig = (config) => {
|
|
|
204
204
|
}
|
|
205
205
|
return normalizedConfig;
|
|
206
206
|
};
|
|
207
|
-
/**
|
|
208
|
-
* Validates sampling rate
|
|
209
|
-
* @param samplingRate - The sampling rate to validate
|
|
210
|
-
* @param errors - Array to push errors to
|
|
211
|
-
*/
|
|
212
|
-
const validateSamplingRate = (samplingRate, errors) => {
|
|
213
|
-
if (samplingRate === undefined) {
|
|
214
|
-
return undefined;
|
|
215
|
-
}
|
|
216
|
-
if (typeof samplingRate !== 'number') {
|
|
217
|
-
errors.push('samplingRate must be a number');
|
|
218
|
-
return DEFAULT_SAMPLING_RATE;
|
|
219
|
-
}
|
|
220
|
-
if (Number.isNaN(samplingRate) || samplingRate <= 0 || samplingRate > 1) {
|
|
221
|
-
errors.push(VALIDATION_MESSAGES.INVALID_SAMPLING_RATE);
|
|
222
|
-
return DEFAULT_SAMPLING_RATE;
|
|
223
|
-
}
|
|
224
|
-
return samplingRate;
|
|
225
|
-
};
|
|
226
|
-
/**
|
|
227
|
-
* Validates excluded URL paths
|
|
228
|
-
* @param excludedUrlPaths - The excluded URL paths to validate
|
|
229
|
-
* @param errors - Array to push errors to
|
|
230
|
-
* @param prefix - Optional prefix for error messages
|
|
231
|
-
*/
|
|
232
|
-
const validateExcludedUrlPaths = (excludedUrlPaths, errors, prefix = '') => {
|
|
233
|
-
if (excludedUrlPaths !== undefined) {
|
|
234
|
-
if (Array.isArray(excludedUrlPaths)) {
|
|
235
|
-
for (const [index, path] of excludedUrlPaths.entries()) {
|
|
236
|
-
if (typeof path === 'string') {
|
|
237
|
-
try {
|
|
238
|
-
new RegExp(path);
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
errors.push(`${prefix}excludedUrlPaths[${index}] is not a valid regex pattern`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
errors.push(`${prefix}excludedUrlPaths[${index}] must be a string`);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
errors.push(`${prefix}excludedUrlPaths must be an array`);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
/**
|
|
255
|
-
* Validates a complete configuration object
|
|
256
|
-
* @param config - The configuration to validate
|
|
257
|
-
* @returns Validation result with errors and warnings
|
|
258
|
-
*/
|
|
259
|
-
export const validateConfig = (config) => {
|
|
260
|
-
const errors = [];
|
|
261
|
-
const warnings = [];
|
|
262
|
-
if (config.sessionTimeout !== undefined) {
|
|
263
|
-
if (typeof config.sessionTimeout !== 'number') {
|
|
264
|
-
errors.push('sessionTimeout must be a number');
|
|
265
|
-
}
|
|
266
|
-
else if (config.sessionTimeout < MIN_SESSION_TIMEOUT_MS) {
|
|
267
|
-
errors.push('sessionTimeout must be at least 30 seconds (30000ms)');
|
|
268
|
-
}
|
|
269
|
-
else if (config.sessionTimeout > MAX_SESSION_TIMEOUT_MS) {
|
|
270
|
-
warnings.push('sessionTimeout is very long (>24 hours), consider reducing it');
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (config.globalMetadata !== undefined) {
|
|
274
|
-
if (typeof config.globalMetadata !== 'object' || config.globalMetadata === null) {
|
|
275
|
-
errors.push('globalMetadata must be an object');
|
|
276
|
-
}
|
|
277
|
-
else {
|
|
278
|
-
const metadataSize = JSON.stringify(config.globalMetadata).length;
|
|
279
|
-
if (metadataSize > 10240) {
|
|
280
|
-
errors.push('globalMetadata is too large (max 10KB)');
|
|
281
|
-
}
|
|
282
|
-
if (Object.keys(config.globalMetadata).length > 12) {
|
|
283
|
-
errors.push('globalMetadata has too many keys (max 12)');
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
// No custom API endpoints supported
|
|
288
|
-
const validatedSamplingRate = validateSamplingRate(config.samplingRate, errors) ?? DEFAULT_SAMPLING_RATE;
|
|
289
|
-
if (config.tags !== undefined && !Array.isArray(config.tags)) {
|
|
290
|
-
errors.push('tags must be an array');
|
|
291
|
-
}
|
|
292
|
-
validateExcludedUrlPaths(config.excludedUrlPaths, errors);
|
|
293
|
-
return { errors, warnings, samplingRate: validatedSamplingRate };
|
|
294
|
-
};
|
|
295
|
-
export const normalizeConfig = (config) => {
|
|
296
|
-
const { errors, warnings, samplingRate } = validateConfig(config);
|
|
297
|
-
return {
|
|
298
|
-
config: {
|
|
299
|
-
...config,
|
|
300
|
-
samplingRate,
|
|
301
|
-
},
|
|
302
|
-
errors,
|
|
303
|
-
warnings,
|
|
304
|
-
};
|
|
305
|
-
};
|
|
306
|
-
/**
|
|
307
|
-
* Validates the final configuration
|
|
308
|
-
* @param config - The configuration to validate
|
|
309
|
-
* @returns Validation result with errors and warnings
|
|
310
|
-
*/
|
|
311
|
-
export const validateFinalConfig = (config) => {
|
|
312
|
-
const errors = [];
|
|
313
|
-
const warnings = [];
|
|
314
|
-
const validatedSamplingRate = validateSamplingRate(config.samplingRate, errors) ?? DEFAULT_SAMPLING_RATE;
|
|
315
|
-
validateExcludedUrlPaths(config.excludedUrlPaths, errors);
|
|
316
|
-
// No custom API endpoints supported
|
|
317
|
-
return { errors, warnings, samplingRate: validatedSamplingRate };
|
|
318
|
-
};
|
|
319
207
|
/**
|
|
320
208
|
* Type guard to check if a JSON response is a valid API config
|
|
321
209
|
* @param json - The JSON to validate
|
|
@@ -329,8 +217,11 @@ export const isValidConfigApiResponse = (json) => {
|
|
|
329
217
|
const response = json;
|
|
330
218
|
const result = {
|
|
331
219
|
mode: response['mode'] === undefined || [Mode.QA, Mode.DEBUG].includes(response['mode']),
|
|
220
|
+
// Zero is valid for samplingRate (means "sample nothing")
|
|
332
221
|
samplingRate: response['samplingRate'] === undefined ||
|
|
333
|
-
(typeof response['samplingRate'] === 'number' &&
|
|
222
|
+
(typeof response['samplingRate'] === 'number' &&
|
|
223
|
+
response['samplingRate'] >= 0 &&
|
|
224
|
+
response['samplingRate'] <= 1),
|
|
334
225
|
tags: response['tags'] === undefined || Array.isArray(response['tags']),
|
|
335
226
|
excludedUrlPaths: response['excludedUrlPaths'] === undefined || Array.isArray(response['excludedUrlPaths']),
|
|
336
227
|
ipExcluded: response['ipExcluded'] === undefined || typeof response['ipExcluded'] === 'boolean',
|
|
@@ -5,8 +5,8 @@ import { MetadataType } from '../../types';
|
|
|
5
5
|
* @param metadata - Optional metadata to validate
|
|
6
6
|
* @returns Validation result with sanitized metadata if valid
|
|
7
7
|
*/
|
|
8
|
-
export declare const isEventValid: (eventName: string, metadata?: Record<string, unknown>) => {
|
|
8
|
+
export declare const isEventValid: (eventName: string, metadata?: Record<string, unknown> | Record<string, unknown>[]) => {
|
|
9
9
|
valid: boolean;
|
|
10
10
|
error?: string;
|
|
11
|
-
sanitizedMetadata?: Record<string, MetadataType
|
|
11
|
+
sanitizedMetadata?: Record<string, MetadataType> | Record<string, MetadataType>[];
|
|
12
12
|
};
|
|
@@ -9,14 +9,14 @@ export declare const isValidEventName: (eventName: string) => {
|
|
|
9
9
|
error?: string;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
12
|
-
* Validates metadata for events
|
|
12
|
+
* Validates metadata for events (supports both objects and arrays of objects)
|
|
13
13
|
* @param eventName - The event name (for error messages)
|
|
14
14
|
* @param metadata - The metadata to validate
|
|
15
15
|
* @param type - Type of metadata (globalMetadata or customEvent)
|
|
16
16
|
* @returns Validation result with sanitized metadata if valid
|
|
17
17
|
*/
|
|
18
|
-
export declare const isValidMetadata: (eventName: string, metadata: Record<string, unknown
|
|
18
|
+
export declare const isValidMetadata: (eventName: string, metadata: Record<string, unknown> | Record<string, unknown>[], type?: "globalMetadata" | "customEvent") => {
|
|
19
19
|
valid: boolean;
|
|
20
20
|
error?: string;
|
|
21
|
-
sanitizedMetadata?: Record<string, MetadataType
|
|
21
|
+
sanitizedMetadata?: Record<string, MetadataType> | Record<string, MetadataType>[];
|
|
22
22
|
};
|