@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
package/dist/cjs/api.d.ts
CHANGED
|
@@ -1,60 +1,8 @@
|
|
|
1
1
|
import { MetadataType, AppConfig, EmitterCallback, EmitterMap } from './types';
|
|
2
2
|
import './types/window.types';
|
|
3
|
-
/**
|
|
4
|
-
* Initializes the tracelog app with the provided configuration.
|
|
5
|
-
* If already initialized, this function returns early without error.
|
|
6
|
-
* @param appConfig - The configuration object for the app
|
|
7
|
-
* @throws {Error} If initialization fails or environment is invalid
|
|
8
|
-
* @example
|
|
9
|
-
* await tracelog.init({ id: 'my-project-id' });
|
|
10
|
-
*/
|
|
11
3
|
export declare const init: (appConfig: AppConfig) => Promise<void>;
|
|
12
|
-
|
|
13
|
-
* Sends a custom event with the specified name and metadata.
|
|
14
|
-
* @param name - The name of the custom event.
|
|
15
|
-
* @param metadata - Optional metadata to attach to the event.
|
|
16
|
-
* @example
|
|
17
|
-
* // Send a custom event with metadata
|
|
18
|
-
* tracelog.event('user_signup', { method: 'email', plan: 'premium' });
|
|
19
|
-
* @example
|
|
20
|
-
* // Send a custom event without metadata
|
|
21
|
-
* tracelog.event('user_login');
|
|
22
|
-
* @remarks
|
|
23
|
-
* This function should be called after the app has been initialized using the `tracelog.init` function.
|
|
24
|
-
*/
|
|
25
|
-
export declare const event: (name: string, metadata?: Record<string, MetadataType>) => void;
|
|
26
|
-
/**
|
|
27
|
-
* Subscribe to events emitted by TraceLog
|
|
28
|
-
* @param event - Event name to listen to
|
|
29
|
-
* @param callback - Function to call when event is emitted
|
|
30
|
-
* @example
|
|
31
|
-
* // Listen for tracked events
|
|
32
|
-
* tracelog.on('event', (data) => {
|
|
33
|
-
* console.log('Event tracked:', data.type);
|
|
34
|
-
* });
|
|
35
|
-
*
|
|
36
|
-
* // Listen for event queues being sent
|
|
37
|
-
* tracelog.on('queue', (data) => {
|
|
38
|
-
* console.log('Events sent:', data.events.length);
|
|
39
|
-
* });
|
|
40
|
-
*/
|
|
4
|
+
export declare const event: (name: string, metadata?: Record<string, MetadataType> | Record<string, MetadataType>[]) => void;
|
|
41
5
|
export declare const on: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
42
|
-
/**
|
|
43
|
-
* Unsubscribe from events emitted by TraceLog
|
|
44
|
-
* @param event - Event name to stop listening to
|
|
45
|
-
* @param callback - The same function reference that was used in on()
|
|
46
|
-
* @example
|
|
47
|
-
* // Remove a specific listener
|
|
48
|
-
* tracelog.off('event', myCallback);
|
|
49
|
-
*/
|
|
50
6
|
export declare const off: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
51
|
-
/**
|
|
52
|
-
* Checks if the app has been initialized.
|
|
53
|
-
* @returns true if the app is initialized, false otherwise
|
|
54
|
-
*/
|
|
55
7
|
export declare const isInitialized: () => boolean;
|
|
56
|
-
/**
|
|
57
|
-
* Destroys the current app instance and cleans up resources.
|
|
58
|
-
* @throws {Error} If not initialized or already destroying
|
|
59
|
-
*/
|
|
60
8
|
export declare const destroy: () => Promise<void>;
|
package/dist/cjs/api.js
CHANGED
|
@@ -8,29 +8,17 @@ require("./types/window.types");
|
|
|
8
8
|
let app = null;
|
|
9
9
|
let isInitializing = false;
|
|
10
10
|
let isDestroying = false;
|
|
11
|
-
/**
|
|
12
|
-
* Initializes the tracelog app with the provided configuration.
|
|
13
|
-
* If already initialized, this function returns early without error.
|
|
14
|
-
* @param appConfig - The configuration object for the app
|
|
15
|
-
* @throws {Error} If initialization fails or environment is invalid
|
|
16
|
-
* @example
|
|
17
|
-
* await tracelog.init({ id: 'my-project-id' });
|
|
18
|
-
*/
|
|
19
11
|
const init = async (appConfig) => {
|
|
20
|
-
// Browser environment check
|
|
21
12
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
22
13
|
throw new Error('This library can only be used in a browser environment');
|
|
23
14
|
}
|
|
24
|
-
// Check if TraceLog is disabled
|
|
25
15
|
if (window.__traceLogDisabled) {
|
|
26
16
|
return;
|
|
27
17
|
}
|
|
28
|
-
// Already initialized - safe to return
|
|
29
18
|
if (app) {
|
|
30
19
|
utils_1.debugLog.debug('API', 'Library already initialized, skipping duplicate initialization');
|
|
31
20
|
return;
|
|
32
21
|
}
|
|
33
|
-
// Prevent concurrent initialization
|
|
34
22
|
if (isInitializing) {
|
|
35
23
|
utils_1.debugLog.warn('API', 'Initialization already in progress');
|
|
36
24
|
throw new Error('Initialization already in progress');
|
|
@@ -65,19 +53,6 @@ const init = async (appConfig) => {
|
|
|
65
53
|
}
|
|
66
54
|
};
|
|
67
55
|
exports.init = init;
|
|
68
|
-
/**
|
|
69
|
-
* Sends a custom event with the specified name and metadata.
|
|
70
|
-
* @param name - The name of the custom event.
|
|
71
|
-
* @param metadata - Optional metadata to attach to the event.
|
|
72
|
-
* @example
|
|
73
|
-
* // Send a custom event with metadata
|
|
74
|
-
* tracelog.event('user_signup', { method: 'email', plan: 'premium' });
|
|
75
|
-
* @example
|
|
76
|
-
* // Send a custom event without metadata
|
|
77
|
-
* tracelog.event('user_login');
|
|
78
|
-
* @remarks
|
|
79
|
-
* This function should be called after the app has been initialized using the `tracelog.init` function.
|
|
80
|
-
*/
|
|
81
56
|
const event = (name, metadata) => {
|
|
82
57
|
if (!app) {
|
|
83
58
|
throw new Error('TraceLog not initialized. Please call init() first.');
|
|
@@ -91,21 +66,6 @@ const event = (name, metadata) => {
|
|
|
91
66
|
}
|
|
92
67
|
};
|
|
93
68
|
exports.event = event;
|
|
94
|
-
/**
|
|
95
|
-
* Subscribe to events emitted by TraceLog
|
|
96
|
-
* @param event - Event name to listen to
|
|
97
|
-
* @param callback - Function to call when event is emitted
|
|
98
|
-
* @example
|
|
99
|
-
* // Listen for tracked events
|
|
100
|
-
* tracelog.on('event', (data) => {
|
|
101
|
-
* console.log('Event tracked:', data.type);
|
|
102
|
-
* });
|
|
103
|
-
*
|
|
104
|
-
* // Listen for event queues being sent
|
|
105
|
-
* tracelog.on('queue', (data) => {
|
|
106
|
-
* console.log('Events sent:', data.events.length);
|
|
107
|
-
* });
|
|
108
|
-
*/
|
|
109
69
|
const on = (event, callback) => {
|
|
110
70
|
if (!app) {
|
|
111
71
|
throw new Error('TraceLog not initialized. Please call init() first.');
|
|
@@ -113,14 +73,6 @@ const on = (event, callback) => {
|
|
|
113
73
|
app.on(event, callback);
|
|
114
74
|
};
|
|
115
75
|
exports.on = on;
|
|
116
|
-
/**
|
|
117
|
-
* Unsubscribe from events emitted by TraceLog
|
|
118
|
-
* @param event - Event name to stop listening to
|
|
119
|
-
* @param callback - The same function reference that was used in on()
|
|
120
|
-
* @example
|
|
121
|
-
* // Remove a specific listener
|
|
122
|
-
* tracelog.off('event', myCallback);
|
|
123
|
-
*/
|
|
124
76
|
const off = (event, callback) => {
|
|
125
77
|
if (!app) {
|
|
126
78
|
throw new Error('TraceLog not initialized. Please call init() first.');
|
|
@@ -128,24 +80,14 @@ const off = (event, callback) => {
|
|
|
128
80
|
app.off(event, callback);
|
|
129
81
|
};
|
|
130
82
|
exports.off = off;
|
|
131
|
-
/**
|
|
132
|
-
* Checks if the app has been initialized.
|
|
133
|
-
* @returns true if the app is initialized, false otherwise
|
|
134
|
-
*/
|
|
135
83
|
const isInitialized = () => {
|
|
136
84
|
return app !== null;
|
|
137
85
|
};
|
|
138
86
|
exports.isInitialized = isInitialized;
|
|
139
|
-
/**
|
|
140
|
-
* Destroys the current app instance and cleans up resources.
|
|
141
|
-
* @throws {Error} If not initialized or already destroying
|
|
142
|
-
*/
|
|
143
87
|
const destroy = async () => {
|
|
144
|
-
// Check if app was never initialized
|
|
145
88
|
if (!app) {
|
|
146
89
|
throw new Error('App not initialized');
|
|
147
90
|
}
|
|
148
|
-
// Prevent concurrent destroy operations
|
|
149
91
|
if (isDestroying) {
|
|
150
92
|
throw new Error('Destroy operation already in progress');
|
|
151
93
|
}
|
|
@@ -169,7 +111,6 @@ const destroy = async () => {
|
|
|
169
111
|
}
|
|
170
112
|
};
|
|
171
113
|
exports.destroy = destroy;
|
|
172
|
-
// Auto-inject testing bridge in development environments
|
|
173
114
|
if (process.env.NODE_ENV === 'dev' && typeof window !== 'undefined') {
|
|
174
115
|
const injectTestingBridge = () => {
|
|
175
116
|
window.__traceLogBridge = new test_bridge_1.TestBridge(isInitializing, isDestroying);
|
|
@@ -2,7 +2,7 @@ export declare const PERFORMANCE_CONFIG: {
|
|
|
2
2
|
readonly WEB_VITALS_THRESHOLDS: Record<import("./types").WebVitalType, number>;
|
|
3
3
|
};
|
|
4
4
|
export declare const DATA_PROTECTION: {
|
|
5
|
-
readonly PII_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp];
|
|
5
|
+
readonly PII_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
6
6
|
};
|
|
7
7
|
export declare const ENGAGEMENT_THRESHOLDS: {
|
|
8
8
|
readonly LOW_ACTIVITY_EVENT_COUNT: 50;
|
package/dist/cjs/app.d.ts
CHANGED
|
@@ -9,10 +9,6 @@ import { GoogleAnalyticsIntegration } from './integrations/google-analytics.inte
|
|
|
9
9
|
import { StorageManager } from './managers/storage.manager';
|
|
10
10
|
import { PerformanceHandler } from './handlers/performance.handler';
|
|
11
11
|
import { ErrorHandler } from './handlers/error.handler';
|
|
12
|
-
/**
|
|
13
|
-
* Main application class for TraceLog analytics
|
|
14
|
-
* Orchestrates event tracking, session management, and integrations
|
|
15
|
-
*/
|
|
16
12
|
export declare class App extends StateManager {
|
|
17
13
|
private isInitialized;
|
|
18
14
|
private suppressNextScrollTimer;
|
|
@@ -34,7 +30,7 @@ export declare class App extends StateManager {
|
|
|
34
30
|
};
|
|
35
31
|
get initialized(): boolean;
|
|
36
32
|
init(appConfig: AppConfig): Promise<void>;
|
|
37
|
-
sendCustomEvent(name: string, metadata?: Record<string, unknown>): void;
|
|
33
|
+
sendCustomEvent(name: string, metadata?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
38
34
|
on<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void;
|
|
39
35
|
off<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void;
|
|
40
36
|
destroy(force?: boolean): Promise<void>;
|
package/dist/cjs/app.js
CHANGED
|
@@ -17,10 +17,6 @@ const storage_manager_1 = require("./managers/storage.manager");
|
|
|
17
17
|
const config_constants_1 = require("./constants/config.constants");
|
|
18
18
|
const performance_handler_1 = require("./handlers/performance.handler");
|
|
19
19
|
const error_handler_1 = require("./handlers/error.handler");
|
|
20
|
-
/**
|
|
21
|
-
* Main application class for TraceLog analytics
|
|
22
|
-
* Orchestrates event tracking, session management, and integrations
|
|
23
|
-
*/
|
|
24
20
|
class App extends state_manager_1.StateManager {
|
|
25
21
|
constructor() {
|
|
26
22
|
super(...arguments);
|
|
@@ -103,6 +99,7 @@ class App extends state_manager_1.StateManager {
|
|
|
103
99
|
clearTimeout(this.suppressNextScrollTimer);
|
|
104
100
|
this.suppressNextScrollTimer = null;
|
|
105
101
|
}
|
|
102
|
+
this.managers.event?.flushImmediatelySync();
|
|
106
103
|
this.managers.event?.stop();
|
|
107
104
|
this.emitter.removeAllListeners();
|
|
108
105
|
this.set('hasStartSession', false);
|
|
@@ -112,20 +109,15 @@ class App extends state_manager_1.StateManager {
|
|
|
112
109
|
this.handlers = {};
|
|
113
110
|
}
|
|
114
111
|
async setupState(appConfig) {
|
|
115
|
-
// Set API URL
|
|
116
112
|
const apiUrl = (0, api_manager_1.getApiUrlForProject)(appConfig.id, appConfig.allowHttp);
|
|
117
113
|
this.set('apiUrl', apiUrl);
|
|
118
|
-
// Get remote configuration
|
|
119
114
|
const configManager = new config_manager_1.ConfigManager();
|
|
120
115
|
const config = await configManager.get(apiUrl, appConfig);
|
|
121
|
-
|
|
122
|
-
this.
|
|
123
|
-
// Set user ID
|
|
124
|
-
const userId = user_manager_1.UserManager.getId(this.managers.storage, normalizedConfig.id);
|
|
116
|
+
this.set('config', config);
|
|
117
|
+
const userId = user_manager_1.UserManager.getId(this.managers.storage, config.id);
|
|
125
118
|
this.set('userId', userId);
|
|
126
|
-
// Set device and page info
|
|
127
119
|
this.set('device', (0, utils_1.getDeviceType)());
|
|
128
|
-
const pageUrl = (0, utils_1.normalizeUrl)(window.location.href,
|
|
120
|
+
const pageUrl = (0, utils_1.normalizeUrl)(window.location.href, config.sensitiveQueryParams);
|
|
129
121
|
this.set('pageUrl', pageUrl);
|
|
130
122
|
}
|
|
131
123
|
async setupIntegrations() {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
import { ApiConfig
|
|
1
|
+
import { ApiConfig } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Default API configuration values
|
|
4
|
+
* Used as fallback when API config is not available or incomplete
|
|
5
|
+
*/
|
|
2
6
|
export declare const DEFAULT_API_CONFIG: ApiConfig;
|
|
3
|
-
export declare const DEFAULT_CONFIG: (config: Config) => Config;
|
|
@@ -1,23 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.DEFAULT_API_CONFIG = void 0;
|
|
4
4
|
const config_constants_1 = require("./config.constants");
|
|
5
|
+
/**
|
|
6
|
+
* Default API configuration values
|
|
7
|
+
* Used as fallback when API config is not available or incomplete
|
|
8
|
+
*/
|
|
5
9
|
exports.DEFAULT_API_CONFIG = {
|
|
6
10
|
samplingRate: config_constants_1.DEFAULT_SAMPLING_RATE,
|
|
7
11
|
excludedUrlPaths: [],
|
|
8
12
|
tags: [],
|
|
9
13
|
ipExcluded: false,
|
|
10
14
|
};
|
|
11
|
-
const DEFAULT_CONFIG = (config) => ({
|
|
12
|
-
...exports.DEFAULT_API_CONFIG,
|
|
13
|
-
...config,
|
|
14
|
-
allowHttp: false,
|
|
15
|
-
sessionTimeout: config_constants_1.DEFAULT_SESSION_TIMEOUT,
|
|
16
|
-
samplingRate: config.samplingRate && config.samplingRate > config_constants_1.MIN_SAMPLING_RATE && config.samplingRate <= config_constants_1.MAX_SAMPLING_RATE
|
|
17
|
-
? config.samplingRate
|
|
18
|
-
: config_constants_1.DEFAULT_SAMPLING_RATE,
|
|
19
|
-
excludedUrlPaths: config.excludedUrlPaths ?? [],
|
|
20
|
-
tags: config.tags ?? [],
|
|
21
|
-
ipExcluded: config.ipExcluded ?? false,
|
|
22
|
-
});
|
|
23
|
-
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* This file centralizes all timing, limits, browser, and initialization constants
|
|
4
4
|
*/
|
|
5
5
|
export declare const DEFAULT_SESSION_TIMEOUT: number;
|
|
6
|
-
export declare const DUPLICATE_EVENT_THRESHOLD_MS =
|
|
6
|
+
export declare const DUPLICATE_EVENT_THRESHOLD_MS = 500;
|
|
7
7
|
export declare const EVENT_SENT_INTERVAL_MS = 10000;
|
|
8
8
|
export declare const SCROLL_DEBOUNCE_TIME_MS = 250;
|
|
9
9
|
export declare const DEFAULT_VISIBILITY_TIMEOUT_MS = 2000;
|
|
10
10
|
export declare const EVENT_EXPIRY_HOURS = 24;
|
|
11
11
|
export declare const EVENT_PERSISTENCE_MAX_AGE_MS: number;
|
|
12
|
-
export declare const MAX_EVENTS_QUEUE_LENGTH =
|
|
12
|
+
export declare const MAX_EVENTS_QUEUE_LENGTH = 100;
|
|
13
13
|
export declare const MAX_RETRIES = 3;
|
|
14
14
|
export declare const RETRY_DELAY_MS = 5000;
|
|
15
15
|
export declare const REQUEST_TIMEOUT_MS = 10000;
|
|
@@ -65,7 +65,7 @@ export declare const VALIDATION_MESSAGES: {
|
|
|
65
65
|
readonly MISSING_PROJECT_ID: "Project ID is required";
|
|
66
66
|
readonly PROJECT_ID_EMPTY_AFTER_TRIM: "Project ID is required";
|
|
67
67
|
readonly INVALID_SESSION_TIMEOUT: "Session timeout must be between 30000ms (30 seconds) and 86400000ms (24 hours)";
|
|
68
|
-
readonly INVALID_SAMPLING_RATE: "Sampling rate must be
|
|
68
|
+
readonly INVALID_SAMPLING_RATE: "Sampling rate must be between 0 and 1";
|
|
69
69
|
readonly INVALID_ERROR_SAMPLING_RATE: "Error sampling must be between 0 and 1";
|
|
70
70
|
readonly INVALID_GOOGLE_ANALYTICS_ID: "Google Analytics measurement ID is required when integration is enabled";
|
|
71
71
|
readonly INVALID_SCROLL_CONTAINER_SELECTORS: "Scroll container selectors must be valid CSS selectors";
|
|
@@ -10,7 +10,7 @@ exports.XSS_PATTERNS = exports.VALIDATION_MESSAGES = exports.ALLOWED_API_CONFIG_
|
|
|
10
10
|
// SESSION & TIMING
|
|
11
11
|
// ============================================================================
|
|
12
12
|
exports.DEFAULT_SESSION_TIMEOUT = 15 * 60 * 1000; // 15 minutes
|
|
13
|
-
exports.DUPLICATE_EVENT_THRESHOLD_MS =
|
|
13
|
+
exports.DUPLICATE_EVENT_THRESHOLD_MS = 500; // 500ms
|
|
14
14
|
exports.EVENT_SENT_INTERVAL_MS = 10000; // 10 seconds
|
|
15
15
|
// Throttling and debouncing
|
|
16
16
|
exports.SCROLL_DEBOUNCE_TIME_MS = 250;
|
|
@@ -21,7 +21,7 @@ exports.EVENT_PERSISTENCE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
|
21
21
|
// ============================================================================
|
|
22
22
|
// LIMITS & RETRIES
|
|
23
23
|
// ============================================================================
|
|
24
|
-
exports.MAX_EVENTS_QUEUE_LENGTH =
|
|
24
|
+
exports.MAX_EVENTS_QUEUE_LENGTH = 100;
|
|
25
25
|
exports.MAX_RETRIES = 3;
|
|
26
26
|
exports.RETRY_DELAY_MS = 5000;
|
|
27
27
|
exports.REQUEST_TIMEOUT_MS = 10000;
|
|
@@ -153,7 +153,7 @@ exports.VALIDATION_MESSAGES = {
|
|
|
153
153
|
// Session timeout validation
|
|
154
154
|
INVALID_SESSION_TIMEOUT: `Session timeout must be between ${exports.MIN_SESSION_TIMEOUT_MS}ms (30 seconds) and ${exports.MAX_SESSION_TIMEOUT_MS}ms (24 hours)`,
|
|
155
155
|
// Sampling rate validation
|
|
156
|
-
INVALID_SAMPLING_RATE: 'Sampling rate must be
|
|
156
|
+
INVALID_SAMPLING_RATE: 'Sampling rate must be between 0 and 1',
|
|
157
157
|
INVALID_ERROR_SAMPLING_RATE: 'Error sampling must be between 0 and 1',
|
|
158
158
|
// Integration validation
|
|
159
159
|
INVALID_GOOGLE_ANALYTICS_ID: 'Google Analytics measurement ID is required when integration is enabled',
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Regular expressions for detecting and sanitizing Personally Identifiable Information (PII)
|
|
7
7
|
* These patterns are used to replace sensitive information with [REDACTED] in error messages
|
|
8
8
|
*/
|
|
9
|
-
export declare const PII_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp];
|
|
9
|
+
export declare const PII_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
10
10
|
/**
|
|
11
11
|
* Maximum length for error messages before truncation
|
|
12
12
|
* Prevents extremely long error messages from consuming excessive storage
|
|
@@ -16,12 +16,17 @@ export declare const MAX_ERROR_MESSAGE_LENGTH = 500;
|
|
|
16
16
|
* Time window for error suppression in milliseconds
|
|
17
17
|
* Prevents duplicate errors from flooding the system within this timeframe
|
|
18
18
|
*/
|
|
19
|
-
export declare const ERROR_SUPPRESSION_WINDOW_MS =
|
|
19
|
+
export declare const ERROR_SUPPRESSION_WINDOW_MS = 5000;
|
|
20
20
|
/**
|
|
21
21
|
* Maximum number of unique errors to track for suppression
|
|
22
22
|
* Prevents memory usage from growing indefinitely
|
|
23
23
|
*/
|
|
24
24
|
export declare const MAX_TRACKED_ERRORS = 50;
|
|
25
|
+
/**
|
|
26
|
+
* Hard limit for error tracking before aggressive cleanup
|
|
27
|
+
* If this limit is exceeded, the entire error map is cleared
|
|
28
|
+
*/
|
|
29
|
+
export declare const MAX_TRACKED_ERRORS_HARD_LIMIT: number;
|
|
25
30
|
/**
|
|
26
31
|
* Default error sampling rate
|
|
27
32
|
* Controls what percentage of errors are actually reported
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Centralizes patterns and limits for error tracking and data protection
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.DEFAULT_ERROR_SAMPLING_RATE = exports.MAX_TRACKED_ERRORS = exports.ERROR_SUPPRESSION_WINDOW_MS = exports.MAX_ERROR_MESSAGE_LENGTH = exports.PII_PATTERNS = void 0;
|
|
7
|
+
exports.DEFAULT_ERROR_SAMPLING_RATE = exports.MAX_TRACKED_ERRORS_HARD_LIMIT = exports.MAX_TRACKED_ERRORS = exports.ERROR_SUPPRESSION_WINDOW_MS = exports.MAX_ERROR_MESSAGE_LENGTH = exports.PII_PATTERNS = void 0;
|
|
8
8
|
// ============================================================================
|
|
9
9
|
// PII SANITIZATION PATTERNS
|
|
10
10
|
// ============================================================================
|
|
@@ -21,6 +21,12 @@ exports.PII_PATTERNS = [
|
|
|
21
21
|
/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
|
|
22
22
|
// IBAN (International Bank Account Number)
|
|
23
23
|
/\b[A-Z]{2}\d{2}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/gi,
|
|
24
|
+
// API keys/tokens (sk_test_, sk_live_, pk_test_, pk_live_, etc.)
|
|
25
|
+
/\b[sp]k_(test|live)_[a-zA-Z0-9]{10,}\b/gi,
|
|
26
|
+
// Bearer tokens (JWT-like patterns - matches complete and partial tokens)
|
|
27
|
+
/Bearer\s+[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)?(?:\.[A-Za-z0-9_-]+)?/gi,
|
|
28
|
+
// Passwords in connection strings (protocol://user:password@host)
|
|
29
|
+
/:\/\/[^:/]+:([^@]+)@/gi,
|
|
24
30
|
];
|
|
25
31
|
// ============================================================================
|
|
26
32
|
// ERROR TRACKING LIMITS
|
|
@@ -34,12 +40,17 @@ exports.MAX_ERROR_MESSAGE_LENGTH = 500;
|
|
|
34
40
|
* Time window for error suppression in milliseconds
|
|
35
41
|
* Prevents duplicate errors from flooding the system within this timeframe
|
|
36
42
|
*/
|
|
37
|
-
exports.ERROR_SUPPRESSION_WINDOW_MS =
|
|
43
|
+
exports.ERROR_SUPPRESSION_WINDOW_MS = 5000; // 5 seconds
|
|
38
44
|
/**
|
|
39
45
|
* Maximum number of unique errors to track for suppression
|
|
40
46
|
* Prevents memory usage from growing indefinitely
|
|
41
47
|
*/
|
|
42
48
|
exports.MAX_TRACKED_ERRORS = 50;
|
|
49
|
+
/**
|
|
50
|
+
* Hard limit for error tracking before aggressive cleanup
|
|
51
|
+
* If this limit is exceeded, the entire error map is cleared
|
|
52
|
+
*/
|
|
53
|
+
exports.MAX_TRACKED_ERRORS_HARD_LIMIT = exports.MAX_TRACKED_ERRORS * 2;
|
|
43
54
|
// ============================================================================
|
|
44
55
|
// ERROR SAMPLING
|
|
45
56
|
// ============================================================================
|
|
@@ -66,11 +66,9 @@ class ClickHandler extends state_manager_1.StateManager {
|
|
|
66
66
|
getRelevantClickElement(element) {
|
|
67
67
|
for (const selector of constants_1.INTERACTIVE_SELECTORS) {
|
|
68
68
|
try {
|
|
69
|
-
// First check if the element itself matches
|
|
70
69
|
if (element.matches(selector)) {
|
|
71
70
|
return element;
|
|
72
71
|
}
|
|
73
|
-
// If not, search for matching ancestors
|
|
74
72
|
const parent = element.closest(selector);
|
|
75
73
|
if (parent) {
|
|
76
74
|
return parent;
|
|
@@ -133,19 +131,15 @@ class ClickHandler extends state_manager_1.StateManager {
|
|
|
133
131
|
getRelevantText(clickedElement, relevantElement) {
|
|
134
132
|
const clickedText = clickedElement.textContent?.trim() ?? '';
|
|
135
133
|
const relevantText = relevantElement.textContent?.trim() ?? '';
|
|
136
|
-
// No text available
|
|
137
134
|
if (!clickedText && !relevantText) {
|
|
138
135
|
return '';
|
|
139
136
|
}
|
|
140
|
-
// Prefer clicked element text if it's reasonable length
|
|
141
137
|
if (clickedText && clickedText.length <= constants_1.MAX_TEXT_LENGTH) {
|
|
142
138
|
return clickedText;
|
|
143
139
|
}
|
|
144
|
-
// Use relevant element text if it fits
|
|
145
140
|
if (relevantText.length <= constants_1.MAX_TEXT_LENGTH) {
|
|
146
141
|
return relevantText;
|
|
147
142
|
}
|
|
148
|
-
// Truncate relevant text if too long
|
|
149
143
|
return relevantText.slice(0, constants_1.MAX_TEXT_LENGTH - 3) + '...';
|
|
150
144
|
}
|
|
151
145
|
extractElementAttributes(element) {
|
|
@@ -109,6 +109,15 @@ class ErrorHandler extends state_manager_1.StateManager {
|
|
|
109
109
|
return true;
|
|
110
110
|
}
|
|
111
111
|
this.recentErrors.set(key, now);
|
|
112
|
+
if (this.recentErrors.size > error_constants_1.MAX_TRACKED_ERRORS_HARD_LIMIT) {
|
|
113
|
+
logging_1.debugLog.warn('ErrorHandler', 'Hard limit exceeded, clearing all tracked errors', {
|
|
114
|
+
size: this.recentErrors.size,
|
|
115
|
+
limit: error_constants_1.MAX_TRACKED_ERRORS_HARD_LIMIT,
|
|
116
|
+
});
|
|
117
|
+
this.recentErrors.clear();
|
|
118
|
+
this.recentErrors.set(key, now);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
112
121
|
if (this.recentErrors.size > error_constants_1.MAX_TRACKED_ERRORS) {
|
|
113
122
|
this.pruneOldErrors();
|
|
114
123
|
}
|
|
@@ -64,7 +64,6 @@ class ScrollHandler extends state_manager_1.StateManager {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
setupScrollContainer(element) {
|
|
67
|
-
// Skip setup for non-scrollable elements
|
|
68
67
|
if (element !== window && !this.isElementScrollable(element)) {
|
|
69
68
|
return;
|
|
70
69
|
}
|
|
@@ -176,12 +175,10 @@ class ScrollHandler extends state_manager_1.StateManager {
|
|
|
176
175
|
calculateScrollData(container) {
|
|
177
176
|
const { element, lastScrollPos } = container;
|
|
178
177
|
const scrollTop = this.getScrollTop(element);
|
|
179
|
-
// Early return: check significant movement first (cheapest check)
|
|
180
178
|
const positionDelta = Math.abs(scrollTop - lastScrollPos);
|
|
181
179
|
if (positionDelta < constants_1.SIGNIFICANT_SCROLL_DELTA) {
|
|
182
180
|
return null;
|
|
183
181
|
}
|
|
184
|
-
// Early return: check if window is scrollable
|
|
185
182
|
if (element === window && !this.isWindowScrollable()) {
|
|
186
183
|
return null;
|
|
187
184
|
}
|
|
@@ -209,7 +206,6 @@ class ScrollHandler extends state_manager_1.StateManager {
|
|
|
209
206
|
style.overflowX === 'scroll' ||
|
|
210
207
|
style.overflow === 'auto' ||
|
|
211
208
|
style.overflow === 'scroll';
|
|
212
|
-
// Element must have scrollable overflow AND content that exceeds the container
|
|
213
209
|
const hasOverflowContent = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
|
|
214
210
|
return hasScrollableOverflow && hasOverflowContent;
|
|
215
211
|
}
|
|
@@ -218,7 +214,6 @@ class ScrollHandler extends state_manager_1.StateManager {
|
|
|
218
214
|
return document.querySelector(selector);
|
|
219
215
|
}
|
|
220
216
|
catch (error) {
|
|
221
|
-
// Invalid CSS selector - log warning and continue
|
|
222
217
|
logging_1.debugLog.clientWarn('ScrollHandler', 'Invalid CSS selector', {
|
|
223
218
|
selector,
|
|
224
219
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -20,12 +20,15 @@ class SessionHandler extends state_manager_1.StateManager {
|
|
|
20
20
|
logging_1.debugLog.warn('SessionHandler', 'Cannot start tracking on destroyed handler');
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
|
+
const projectId = this.get('config')?.id;
|
|
24
|
+
if (!projectId) {
|
|
25
|
+
throw new Error('Cannot start session tracking: config not available');
|
|
26
|
+
}
|
|
23
27
|
try {
|
|
24
|
-
this.sessionManager = new session_manager_1.SessionManager(this.storageManager, this.eventManager);
|
|
28
|
+
this.sessionManager = new session_manager_1.SessionManager(this.storageManager, this.eventManager, projectId);
|
|
25
29
|
await this.sessionManager.startTracking();
|
|
26
30
|
}
|
|
27
31
|
catch (error) {
|
|
28
|
-
// Cleanup on failure
|
|
29
32
|
if (this.sessionManager) {
|
|
30
33
|
try {
|
|
31
34
|
this.sessionManager.destroy();
|
|
@@ -9,7 +9,7 @@ declare global {
|
|
|
9
9
|
export declare class GoogleAnalyticsIntegration extends StateManager {
|
|
10
10
|
private isInitialized;
|
|
11
11
|
initialize(): Promise<void>;
|
|
12
|
-
trackEvent(eventName: string, metadata: Record<string, MetadataType>): void;
|
|
12
|
+
trackEvent(eventName: string, metadata: Record<string, MetadataType> | Record<string, MetadataType>[]): void;
|
|
13
13
|
cleanup(): void;
|
|
14
14
|
private isScriptAlreadyLoaded;
|
|
15
15
|
private loadScript;
|
|
@@ -37,7 +37,8 @@ class GoogleAnalyticsIntegration extends state_manager_1.StateManager {
|
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
try {
|
|
40
|
-
|
|
40
|
+
const normalizedMetadata = Array.isArray(metadata) ? { items: metadata } : metadata;
|
|
41
|
+
window.gtag('event', eventName, normalizedMetadata);
|
|
41
42
|
}
|
|
42
43
|
catch (error) {
|
|
43
44
|
utils_1.debugLog.error('GoogleAnalyticsIntegration', 'Event tracking failed', {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Generates API URL for TraceLog service based on project ID
|
|
3
3
|
*
|
|
4
4
|
* Handles two special cases:
|
|
5
|
-
* - 'localhost:
|
|
5
|
+
* - 'localhost:8080' or 'localhost:9999' - for local development (generates http://localhost:PORT)
|
|
6
6
|
* - Regular project IDs - generates subdomain URLs via getApiUrl utility
|
|
7
7
|
*
|
|
8
8
|
* @param id Project ID or localhost address
|
|
@@ -8,7 +8,7 @@ const logging_1 = require("../utils/logging");
|
|
|
8
8
|
* Generates API URL for TraceLog service based on project ID
|
|
9
9
|
*
|
|
10
10
|
* Handles two special cases:
|
|
11
|
-
* - 'localhost:
|
|
11
|
+
* - 'localhost:8080' or 'localhost:9999' - for local development (generates http://localhost:PORT)
|
|
12
12
|
* - Regular project IDs - generates subdomain URLs via getApiUrl utility
|
|
13
13
|
*
|
|
14
14
|
* @param id Project ID or localhost address
|
|
@@ -18,8 +18,8 @@ const logging_1 = require("../utils/logging");
|
|
|
18
18
|
*/
|
|
19
19
|
function getApiUrlForProject(id, allowHttp = false) {
|
|
20
20
|
try {
|
|
21
|
-
// Handle localhost development case
|
|
22
|
-
if (id.
|
|
21
|
+
// Handle localhost development case (localhost:8080 or localhost:9999)
|
|
22
|
+
if (id === types_1.SpecialProjectId.Localhost || id === types_1.SpecialProjectId.Fail) {
|
|
23
23
|
const url = `http://${id}`;
|
|
24
24
|
if (!(0, utils_1.isValidUrl)(url, true)) {
|
|
25
25
|
throw new Error(`Invalid localhost URL format: ${id}`);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AppConfig, ApiConfig, Config } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Centralized configuration builder
|
|
4
|
+
* Single source of truth for merging and building final configuration
|
|
5
|
+
*/
|
|
6
|
+
export declare class ConfigBuilder {
|
|
7
|
+
/**
|
|
8
|
+
* Builds final configuration from app config and API config
|
|
9
|
+
* Applies clear precedence: API overrides client, with defaults as fallback
|
|
10
|
+
*/
|
|
11
|
+
static build(appConfig: AppConfig, apiConfig?: ApiConfig): Config;
|
|
12
|
+
/**
|
|
13
|
+
* Resolves session timeout with validation
|
|
14
|
+
* Returns default if undefined or out of valid range
|
|
15
|
+
*/
|
|
16
|
+
private static resolveSessionTimeout;
|
|
17
|
+
/**
|
|
18
|
+
* Resolves sampling rate with validation
|
|
19
|
+
* Priority: API config > app config > default
|
|
20
|
+
*/
|
|
21
|
+
private static resolveSamplingRate;
|
|
22
|
+
/**
|
|
23
|
+
* Resolves error sampling rate based on mode
|
|
24
|
+
* In debug/qa modes: uses provided value or defaults to full sampling (1.0)
|
|
25
|
+
* In production: uses provided value or defaults to 10% sampling (0.1)
|
|
26
|
+
*/
|
|
27
|
+
private static resolveErrorSampling;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves mode with special project ID handling
|
|
30
|
+
* Priority: Special project ID > API mode > app mode
|
|
31
|
+
*/
|
|
32
|
+
private static resolveMode;
|
|
33
|
+
}
|