@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
|
@@ -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";
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// SESSION & TIMING
|
|
7
7
|
// ============================================================================
|
|
8
8
|
export const DEFAULT_SESSION_TIMEOUT = 15 * 60 * 1000; // 15 minutes
|
|
9
|
-
export const DUPLICATE_EVENT_THRESHOLD_MS =
|
|
9
|
+
export const DUPLICATE_EVENT_THRESHOLD_MS = 500; // 500ms
|
|
10
10
|
export const EVENT_SENT_INTERVAL_MS = 10000; // 10 seconds
|
|
11
11
|
// Throttling and debouncing
|
|
12
12
|
export const SCROLL_DEBOUNCE_TIME_MS = 250;
|
|
@@ -17,7 +17,7 @@ export const EVENT_PERSISTENCE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
|
17
17
|
// ============================================================================
|
|
18
18
|
// LIMITS & RETRIES
|
|
19
19
|
// ============================================================================
|
|
20
|
-
export const MAX_EVENTS_QUEUE_LENGTH =
|
|
20
|
+
export const MAX_EVENTS_QUEUE_LENGTH = 100;
|
|
21
21
|
export const MAX_RETRIES = 3;
|
|
22
22
|
export const RETRY_DELAY_MS = 5000;
|
|
23
23
|
export const REQUEST_TIMEOUT_MS = 10000;
|
|
@@ -149,7 +149,7 @@ export const VALIDATION_MESSAGES = {
|
|
|
149
149
|
// Session timeout validation
|
|
150
150
|
INVALID_SESSION_TIMEOUT: `Session timeout must be between ${MIN_SESSION_TIMEOUT_MS}ms (30 seconds) and ${MAX_SESSION_TIMEOUT_MS}ms (24 hours)`,
|
|
151
151
|
// Sampling rate validation
|
|
152
|
-
INVALID_SAMPLING_RATE: 'Sampling rate must be
|
|
152
|
+
INVALID_SAMPLING_RATE: 'Sampling rate must be between 0 and 1',
|
|
153
153
|
INVALID_ERROR_SAMPLING_RATE: 'Error sampling must be between 0 and 1',
|
|
154
154
|
// Integration validation
|
|
155
155
|
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
|
|
@@ -18,6 +18,12 @@ export const PII_PATTERNS = [
|
|
|
18
18
|
/\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/g,
|
|
19
19
|
// IBAN (International Bank Account Number)
|
|
20
20
|
/\b[A-Z]{2}\d{2}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b/gi,
|
|
21
|
+
// API keys/tokens (sk_test_, sk_live_, pk_test_, pk_live_, etc.)
|
|
22
|
+
/\b[sp]k_(test|live)_[a-zA-Z0-9]{10,}\b/gi,
|
|
23
|
+
// Bearer tokens (JWT-like patterns - matches complete and partial tokens)
|
|
24
|
+
/Bearer\s+[A-Za-z0-9_-]+(?:\.[A-Za-z0-9_-]+)?(?:\.[A-Za-z0-9_-]+)?/gi,
|
|
25
|
+
// Passwords in connection strings (protocol://user:password@host)
|
|
26
|
+
/:\/\/[^:/]+:([^@]+)@/gi,
|
|
21
27
|
];
|
|
22
28
|
// ============================================================================
|
|
23
29
|
// ERROR TRACKING LIMITS
|
|
@@ -31,12 +37,17 @@ export const MAX_ERROR_MESSAGE_LENGTH = 500;
|
|
|
31
37
|
* Time window for error suppression in milliseconds
|
|
32
38
|
* Prevents duplicate errors from flooding the system within this timeframe
|
|
33
39
|
*/
|
|
34
|
-
export const ERROR_SUPPRESSION_WINDOW_MS =
|
|
40
|
+
export const ERROR_SUPPRESSION_WINDOW_MS = 5000; // 5 seconds
|
|
35
41
|
/**
|
|
36
42
|
* Maximum number of unique errors to track for suppression
|
|
37
43
|
* Prevents memory usage from growing indefinitely
|
|
38
44
|
*/
|
|
39
45
|
export const MAX_TRACKED_ERRORS = 50;
|
|
46
|
+
/**
|
|
47
|
+
* Hard limit for error tracking before aggressive cleanup
|
|
48
|
+
* If this limit is exceeded, the entire error map is cleared
|
|
49
|
+
*/
|
|
50
|
+
export const MAX_TRACKED_ERRORS_HARD_LIMIT = MAX_TRACKED_ERRORS * 2;
|
|
40
51
|
// ============================================================================
|
|
41
52
|
// ERROR SAMPLING
|
|
42
53
|
// ============================================================================
|
|
@@ -63,11 +63,9 @@ export class ClickHandler extends StateManager {
|
|
|
63
63
|
getRelevantClickElement(element) {
|
|
64
64
|
for (const selector of INTERACTIVE_SELECTORS) {
|
|
65
65
|
try {
|
|
66
|
-
// First check if the element itself matches
|
|
67
66
|
if (element.matches(selector)) {
|
|
68
67
|
return element;
|
|
69
68
|
}
|
|
70
|
-
// If not, search for matching ancestors
|
|
71
69
|
const parent = element.closest(selector);
|
|
72
70
|
if (parent) {
|
|
73
71
|
return parent;
|
|
@@ -130,19 +128,15 @@ export class ClickHandler extends StateManager {
|
|
|
130
128
|
getRelevantText(clickedElement, relevantElement) {
|
|
131
129
|
const clickedText = clickedElement.textContent?.trim() ?? '';
|
|
132
130
|
const relevantText = relevantElement.textContent?.trim() ?? '';
|
|
133
|
-
// No text available
|
|
134
131
|
if (!clickedText && !relevantText) {
|
|
135
132
|
return '';
|
|
136
133
|
}
|
|
137
|
-
// Prefer clicked element text if it's reasonable length
|
|
138
134
|
if (clickedText && clickedText.length <= MAX_TEXT_LENGTH) {
|
|
139
135
|
return clickedText;
|
|
140
136
|
}
|
|
141
|
-
// Use relevant element text if it fits
|
|
142
137
|
if (relevantText.length <= MAX_TEXT_LENGTH) {
|
|
143
138
|
return relevantText;
|
|
144
139
|
}
|
|
145
|
-
// Truncate relevant text if too long
|
|
146
140
|
return relevantText.slice(0, MAX_TEXT_LENGTH - 3) + '...';
|
|
147
141
|
}
|
|
148
142
|
extractElementAttributes(element) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { StateManager } from '../managers/state.manager';
|
|
2
2
|
import { ErrorType, EventType } from '../types';
|
|
3
|
-
import { PII_PATTERNS, MAX_ERROR_MESSAGE_LENGTH, ERROR_SUPPRESSION_WINDOW_MS, MAX_TRACKED_ERRORS, } from '../constants/error.constants';
|
|
3
|
+
import { PII_PATTERNS, MAX_ERROR_MESSAGE_LENGTH, ERROR_SUPPRESSION_WINDOW_MS, MAX_TRACKED_ERRORS, MAX_TRACKED_ERRORS_HARD_LIMIT, } from '../constants/error.constants';
|
|
4
4
|
import { debugLog } from '../utils/logging';
|
|
5
5
|
/**
|
|
6
6
|
* Simplified error handler for tracking JavaScript errors and unhandled promise rejections
|
|
@@ -106,6 +106,15 @@ export class ErrorHandler extends StateManager {
|
|
|
106
106
|
return true;
|
|
107
107
|
}
|
|
108
108
|
this.recentErrors.set(key, now);
|
|
109
|
+
if (this.recentErrors.size > MAX_TRACKED_ERRORS_HARD_LIMIT) {
|
|
110
|
+
debugLog.warn('ErrorHandler', 'Hard limit exceeded, clearing all tracked errors', {
|
|
111
|
+
size: this.recentErrors.size,
|
|
112
|
+
limit: MAX_TRACKED_ERRORS_HARD_LIMIT,
|
|
113
|
+
});
|
|
114
|
+
this.recentErrors.clear();
|
|
115
|
+
this.recentErrors.set(key, now);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
109
118
|
if (this.recentErrors.size > MAX_TRACKED_ERRORS) {
|
|
110
119
|
this.pruneOldErrors();
|
|
111
120
|
}
|
|
@@ -61,7 +61,6 @@ export class ScrollHandler extends StateManager {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
setupScrollContainer(element) {
|
|
64
|
-
// Skip setup for non-scrollable elements
|
|
65
64
|
if (element !== window && !this.isElementScrollable(element)) {
|
|
66
65
|
return;
|
|
67
66
|
}
|
|
@@ -173,12 +172,10 @@ export class ScrollHandler extends StateManager {
|
|
|
173
172
|
calculateScrollData(container) {
|
|
174
173
|
const { element, lastScrollPos } = container;
|
|
175
174
|
const scrollTop = this.getScrollTop(element);
|
|
176
|
-
// Early return: check significant movement first (cheapest check)
|
|
177
175
|
const positionDelta = Math.abs(scrollTop - lastScrollPos);
|
|
178
176
|
if (positionDelta < SIGNIFICANT_SCROLL_DELTA) {
|
|
179
177
|
return null;
|
|
180
178
|
}
|
|
181
|
-
// Early return: check if window is scrollable
|
|
182
179
|
if (element === window && !this.isWindowScrollable()) {
|
|
183
180
|
return null;
|
|
184
181
|
}
|
|
@@ -206,7 +203,6 @@ export class ScrollHandler extends StateManager {
|
|
|
206
203
|
style.overflowX === 'scroll' ||
|
|
207
204
|
style.overflow === 'auto' ||
|
|
208
205
|
style.overflow === 'scroll';
|
|
209
|
-
// Element must have scrollable overflow AND content that exceeds the container
|
|
210
206
|
const hasOverflowContent = element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
|
|
211
207
|
return hasScrollableOverflow && hasOverflowContent;
|
|
212
208
|
}
|
|
@@ -215,7 +211,6 @@ export class ScrollHandler extends StateManager {
|
|
|
215
211
|
return document.querySelector(selector);
|
|
216
212
|
}
|
|
217
213
|
catch (error) {
|
|
218
|
-
// Invalid CSS selector - log warning and continue
|
|
219
214
|
debugLog.clientWarn('ScrollHandler', 'Invalid CSS selector', {
|
|
220
215
|
selector,
|
|
221
216
|
error: error instanceof Error ? error.message : 'Unknown error',
|
|
@@ -17,12 +17,15 @@ export class SessionHandler extends StateManager {
|
|
|
17
17
|
debugLog.warn('SessionHandler', 'Cannot start tracking on destroyed handler');
|
|
18
18
|
return;
|
|
19
19
|
}
|
|
20
|
+
const projectId = this.get('config')?.id;
|
|
21
|
+
if (!projectId) {
|
|
22
|
+
throw new Error('Cannot start session tracking: config not available');
|
|
23
|
+
}
|
|
20
24
|
try {
|
|
21
|
-
this.sessionManager = new SessionManager(this.storageManager, this.eventManager);
|
|
25
|
+
this.sessionManager = new SessionManager(this.storageManager, this.eventManager, projectId);
|
|
22
26
|
await this.sessionManager.startTracking();
|
|
23
27
|
}
|
|
24
28
|
catch (error) {
|
|
25
|
-
// Cleanup on failure
|
|
26
29
|
if (this.sessionManager) {
|
|
27
30
|
try {
|
|
28
31
|
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;
|
|
@@ -34,7 +34,8 @@ export class GoogleAnalyticsIntegration extends StateManager {
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
try {
|
|
37
|
-
|
|
37
|
+
const normalizedMetadata = Array.isArray(metadata) ? { items: metadata } : metadata;
|
|
38
|
+
window.gtag('event', eventName, normalizedMetadata);
|
|
38
39
|
}
|
|
39
40
|
catch (error) {
|
|
40
41
|
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
|
|
@@ -5,7 +5,7 @@ import { debugLog } from '../utils/logging';
|
|
|
5
5
|
* Generates API URL for TraceLog service based on project ID
|
|
6
6
|
*
|
|
7
7
|
* Handles two special cases:
|
|
8
|
-
* - 'localhost:
|
|
8
|
+
* - 'localhost:8080' or 'localhost:9999' - for local development (generates http://localhost:PORT)
|
|
9
9
|
* - Regular project IDs - generates subdomain URLs via getApiUrl utility
|
|
10
10
|
*
|
|
11
11
|
* @param id Project ID or localhost address
|
|
@@ -15,8 +15,8 @@ import { debugLog } from '../utils/logging';
|
|
|
15
15
|
*/
|
|
16
16
|
export function getApiUrlForProject(id, allowHttp = false) {
|
|
17
17
|
try {
|
|
18
|
-
// Handle localhost development case
|
|
19
|
-
if (id.
|
|
18
|
+
// Handle localhost development case (localhost:8080 or localhost:9999)
|
|
19
|
+
if (id === SpecialProjectId.Localhost || id === SpecialProjectId.Fail) {
|
|
20
20
|
const url = `http://${id}`;
|
|
21
21
|
if (!isValidUrl(url, true)) {
|
|
22
22
|
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
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { DEFAULT_SESSION_TIMEOUT, DEFAULT_SAMPLING_RATE, MIN_SAMPLING_RATE, MAX_SAMPLING_RATE, MIN_SESSION_TIMEOUT_MS, MAX_SESSION_TIMEOUT_MS, } from '../constants/config.constants';
|
|
2
|
+
import { Mode, SpecialProjectId } from '../types';
|
|
3
|
+
import { debugLog } from '../utils/logging';
|
|
4
|
+
/**
|
|
5
|
+
* Centralized configuration builder
|
|
6
|
+
* Single source of truth for merging and building final configuration
|
|
7
|
+
*/
|
|
8
|
+
export class ConfigBuilder {
|
|
9
|
+
/**
|
|
10
|
+
* Builds final configuration from app config and API config
|
|
11
|
+
* Applies clear precedence: API overrides client, with defaults as fallback
|
|
12
|
+
*/
|
|
13
|
+
static build(appConfig, apiConfig = {}) {
|
|
14
|
+
// Resolve mode first as it affects other settings (like errorSampling)
|
|
15
|
+
const finalMode = this.resolveMode(appConfig, apiConfig.mode);
|
|
16
|
+
const config = {
|
|
17
|
+
// Core identifiers
|
|
18
|
+
id: appConfig.id,
|
|
19
|
+
// Session configuration
|
|
20
|
+
sessionTimeout: this.resolveSessionTimeout(appConfig.sessionTimeout),
|
|
21
|
+
// Mode configuration (resolved first)
|
|
22
|
+
mode: finalMode,
|
|
23
|
+
// Sampling configuration (depends on mode)
|
|
24
|
+
samplingRate: this.resolveSamplingRate(apiConfig.samplingRate, appConfig.samplingRate),
|
|
25
|
+
errorSampling: this.resolveErrorSampling(appConfig.errorSampling, finalMode),
|
|
26
|
+
// Filtering configuration
|
|
27
|
+
excludedUrlPaths: apiConfig.excludedUrlPaths ?? appConfig.excludedUrlPaths ?? [],
|
|
28
|
+
tags: apiConfig.tags ?? [],
|
|
29
|
+
ipExcluded: apiConfig.ipExcluded ?? false,
|
|
30
|
+
// Client-only configuration
|
|
31
|
+
globalMetadata: appConfig.globalMetadata ?? {},
|
|
32
|
+
scrollContainerSelectors: appConfig.scrollContainerSelectors,
|
|
33
|
+
sensitiveQueryParams: appConfig.sensitiveQueryParams ?? [],
|
|
34
|
+
integrations: appConfig.integrations,
|
|
35
|
+
// Security configuration
|
|
36
|
+
allowHttp: appConfig.allowHttp ?? false,
|
|
37
|
+
};
|
|
38
|
+
debugLog.debug('ConfigBuilder', 'Configuration built', {
|
|
39
|
+
projectId: config.id,
|
|
40
|
+
mode: config.mode,
|
|
41
|
+
samplingRate: config.samplingRate,
|
|
42
|
+
errorSampling: config.errorSampling,
|
|
43
|
+
hasTags: !!config.tags?.length,
|
|
44
|
+
hasExclusions: !!config.excludedUrlPaths?.length,
|
|
45
|
+
});
|
|
46
|
+
return config;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Resolves session timeout with validation
|
|
50
|
+
* Returns default if undefined or out of valid range
|
|
51
|
+
*/
|
|
52
|
+
static resolveSessionTimeout(timeout) {
|
|
53
|
+
if (timeout === undefined) {
|
|
54
|
+
return DEFAULT_SESSION_TIMEOUT;
|
|
55
|
+
}
|
|
56
|
+
if (timeout < MIN_SESSION_TIMEOUT_MS || timeout > MAX_SESSION_TIMEOUT_MS) {
|
|
57
|
+
debugLog.warn('ConfigBuilder', 'Invalid session timeout, using default', {
|
|
58
|
+
provided: timeout,
|
|
59
|
+
min: MIN_SESSION_TIMEOUT_MS,
|
|
60
|
+
max: MAX_SESSION_TIMEOUT_MS,
|
|
61
|
+
default: DEFAULT_SESSION_TIMEOUT,
|
|
62
|
+
});
|
|
63
|
+
return DEFAULT_SESSION_TIMEOUT;
|
|
64
|
+
}
|
|
65
|
+
return timeout;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolves sampling rate with validation
|
|
69
|
+
* Priority: API config > app config > default
|
|
70
|
+
*/
|
|
71
|
+
static resolveSamplingRate(apiRate, appRate) {
|
|
72
|
+
const rate = apiRate ?? appRate;
|
|
73
|
+
if (rate === undefined) {
|
|
74
|
+
return DEFAULT_SAMPLING_RATE;
|
|
75
|
+
}
|
|
76
|
+
if (rate < MIN_SAMPLING_RATE || rate > MAX_SAMPLING_RATE) {
|
|
77
|
+
debugLog.warn('ConfigBuilder', 'Invalid sampling rate, using default', {
|
|
78
|
+
provided: rate,
|
|
79
|
+
default: DEFAULT_SAMPLING_RATE,
|
|
80
|
+
});
|
|
81
|
+
return DEFAULT_SAMPLING_RATE;
|
|
82
|
+
}
|
|
83
|
+
return rate;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Resolves error sampling rate based on mode
|
|
87
|
+
* In debug/qa modes: uses provided value or defaults to full sampling (1.0)
|
|
88
|
+
* In production: uses provided value or defaults to 10% sampling (0.1)
|
|
89
|
+
*/
|
|
90
|
+
static resolveErrorSampling(appErrorSampling, apiMode) {
|
|
91
|
+
const isDebugMode = apiMode === Mode.DEBUG || apiMode === Mode.QA;
|
|
92
|
+
if (isDebugMode) {
|
|
93
|
+
// In debug mode, respect explicit value or default to full sampling
|
|
94
|
+
return appErrorSampling ?? 1;
|
|
95
|
+
}
|
|
96
|
+
return appErrorSampling ?? 0.1; // Default to 10% sampling in production
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolves mode with special project ID handling
|
|
100
|
+
* Priority: Special project ID > API mode > app mode
|
|
101
|
+
*/
|
|
102
|
+
static resolveMode(appConfig, apiMode) {
|
|
103
|
+
// Force DEBUG mode for special project IDs
|
|
104
|
+
if (appConfig.id === SpecialProjectId.Skip ||
|
|
105
|
+
appConfig.id === SpecialProjectId.Fail ||
|
|
106
|
+
appConfig.id.toLowerCase().startsWith('skip-')) {
|
|
107
|
+
return Mode.DEBUG;
|
|
108
|
+
}
|
|
109
|
+
// API mode takes precedence over app mode
|
|
110
|
+
return apiMode ?? appConfig.mode;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -2,20 +2,20 @@ import { AppConfig, Config } from '../types';
|
|
|
2
2
|
/**
|
|
3
3
|
* Configuration manager responsible for loading and merging application configuration.
|
|
4
4
|
*
|
|
5
|
-
* Handles
|
|
6
|
-
* 1.
|
|
7
|
-
* 2.
|
|
8
|
-
*
|
|
5
|
+
* Handles configuration from two sources:
|
|
6
|
+
* 1. API configuration (server-side settings)
|
|
7
|
+
* 2. App configuration (client initialization settings)
|
|
8
|
+
*
|
|
9
|
+
* Uses ConfigBuilder for centralized merge logic.
|
|
9
10
|
*
|
|
10
11
|
* Supports special project IDs for development and testing:
|
|
11
12
|
* - 'skip': Bypasses all network calls, uses defaults
|
|
12
|
-
* - 'localhost:
|
|
13
|
+
* - 'localhost:8080': Loads config from local development server
|
|
13
14
|
*/
|
|
14
15
|
export declare class ConfigManager {
|
|
15
|
-
private static readonly LOCALHOST_PATTERN;
|
|
16
16
|
private static readonly PRODUCTION_DOMAINS;
|
|
17
17
|
/**
|
|
18
|
-
* Gets complete configuration by
|
|
18
|
+
* Gets complete configuration by loading API config and building final config.
|
|
19
19
|
*
|
|
20
20
|
* @param apiUrl - Base URL for the configuration API
|
|
21
21
|
* @param appConfig - Client-side configuration from init()
|
|
@@ -23,7 +23,8 @@ export declare class ConfigManager {
|
|
|
23
23
|
*/
|
|
24
24
|
get(apiUrl: string, appConfig: AppConfig): Promise<Config>;
|
|
25
25
|
/**
|
|
26
|
-
* Loads configuration from API and
|
|
26
|
+
* Loads configuration from API and returns sanitized API config.
|
|
27
|
+
* Only returns values explicitly provided by the API.
|
|
27
28
|
*/
|
|
28
29
|
private loadFromApi;
|
|
29
30
|
/**
|
|
@@ -32,26 +33,24 @@ export declare class ConfigManager {
|
|
|
32
33
|
private buildConfigUrl;
|
|
33
34
|
/**
|
|
34
35
|
* Builds request headers based on project configuration.
|
|
36
|
+
* Always includes X-TraceLog-Project header for consistent identification.
|
|
35
37
|
*/
|
|
36
38
|
private buildHeaders;
|
|
37
39
|
/**
|
|
38
40
|
* Parses and validates JSON response from config API.
|
|
39
41
|
*/
|
|
40
42
|
private parseJsonResponse;
|
|
41
|
-
/**
|
|
42
|
-
* Validates localhost project ID format and port range.
|
|
43
|
-
*/
|
|
44
|
-
private validateLocalhostProjectId;
|
|
45
43
|
/**
|
|
46
44
|
* Checks if QA mode is enabled via URL parameter.
|
|
47
45
|
*/
|
|
48
46
|
private isQaModeEnabled;
|
|
49
47
|
/**
|
|
50
|
-
*
|
|
48
|
+
* Applies QA mode to API config if enabled via URL parameter.
|
|
51
49
|
*/
|
|
52
|
-
private
|
|
50
|
+
private applyQaModeIfEnabled;
|
|
53
51
|
/**
|
|
54
52
|
* Creates default configuration for skip mode and fallback scenarios.
|
|
53
|
+
* Only uses API defaults for fields not provided by the app config.
|
|
55
54
|
*/
|
|
56
55
|
private createDefaultConfig;
|
|
57
56
|
}
|