@tracelog/lib 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -24
- package/dist/browser/tracelog.js +1928 -3297
- package/dist/cjs/api.d.ts +33 -19
- package/dist/cjs/api.js +111 -156
- package/dist/cjs/app.constants.d.ts +74 -1
- package/dist/cjs/app.constants.js +77 -3
- package/dist/cjs/app.d.ts +29 -44
- package/dist/cjs/app.js +114 -212
- package/dist/cjs/app.types.d.ts +2 -7
- package/dist/cjs/app.types.js +10 -21
- package/dist/cjs/constants/api.constants.js +11 -5
- package/dist/cjs/constants/config.constants.d.ts +75 -0
- package/dist/cjs/constants/config.constants.js +178 -0
- package/dist/cjs/constants/error.constants.d.ts +29 -0
- package/dist/cjs/constants/error.constants.js +50 -0
- package/dist/cjs/constants/index.d.ts +3 -6
- package/dist/cjs/constants/index.js +3 -6
- package/dist/cjs/constants/performance.constants.d.ts +28 -0
- package/dist/cjs/constants/performance.constants.js +43 -0
- package/dist/cjs/handlers/click.handler.d.ts +1 -0
- package/dist/cjs/handlers/click.handler.js +30 -49
- package/dist/cjs/handlers/error.handler.d.ts +11 -6
- package/dist/cjs/handlers/error.handler.js +91 -51
- package/dist/cjs/handlers/page-view.handler.js +38 -29
- package/dist/cjs/handlers/performance.handler.d.ts +3 -0
- package/dist/cjs/handlers/performance.handler.js +76 -37
- package/dist/cjs/handlers/scroll.handler.d.ts +16 -10
- package/dist/cjs/handlers/scroll.handler.js +119 -185
- package/dist/cjs/handlers/session.handler.d.ts +6 -20
- package/dist/cjs/handlers/session.handler.js +38 -326
- package/dist/cjs/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/cjs/integrations/google-analytics.integration.js +27 -98
- package/dist/cjs/listeners/input-listener-managers.d.ts +18 -9
- package/dist/cjs/listeners/input-listener-managers.js +24 -33
- package/dist/cjs/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/cjs/listeners/touch-listener-manager.js +1 -23
- package/dist/cjs/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/cjs/listeners/visibility-listener-manager.js +6 -42
- package/dist/cjs/managers/api.manager.d.ts +13 -3
- package/dist/cjs/managers/api.manager.js +35 -5
- package/dist/cjs/managers/config.manager.d.ts +53 -3
- package/dist/cjs/managers/config.manager.js +131 -62
- package/dist/cjs/managers/event.manager.d.ts +57 -36
- package/dist/cjs/managers/event.manager.js +266 -417
- package/dist/cjs/managers/sender.manager.d.ts +40 -22
- package/dist/cjs/managers/sender.manager.js +200 -198
- package/dist/cjs/managers/session.manager.d.ts +80 -66
- package/dist/cjs/managers/session.manager.js +267 -522
- package/dist/cjs/managers/state.manager.d.ts +33 -0
- package/dist/cjs/managers/state.manager.js +79 -6
- package/dist/cjs/managers/storage.manager.d.ts +26 -2
- package/dist/cjs/managers/storage.manager.js +67 -34
- package/dist/cjs/managers/tags.manager.d.ts +31 -7
- package/dist/cjs/managers/tags.manager.js +123 -241
- package/dist/cjs/managers/user.manager.d.ts +14 -5
- package/dist/cjs/managers/user.manager.js +17 -9
- package/dist/cjs/public-api.d.ts +10 -1
- package/dist/cjs/public-api.js +18 -24
- package/dist/cjs/test-bridge.d.ts +48 -0
- package/dist/cjs/test-bridge.js +110 -0
- package/dist/cjs/types/api.types.d.ts +21 -6
- package/dist/cjs/types/api.types.js +21 -6
- package/dist/cjs/types/config.types.d.ts +22 -84
- package/dist/cjs/types/emitter.types.d.ts +11 -0
- package/dist/cjs/types/emitter.types.js +8 -0
- package/dist/cjs/types/event.types.d.ts +8 -11
- package/dist/cjs/types/index.d.ts +3 -1
- package/dist/cjs/types/index.js +3 -1
- package/dist/cjs/types/queue.types.d.ts +1 -0
- package/dist/cjs/types/session.types.d.ts +0 -64
- package/dist/cjs/types/state.types.d.ts +1 -0
- package/dist/cjs/types/test-bridge.types.d.ts +38 -0
- package/dist/cjs/types/validation-error.types.d.ts +7 -0
- package/dist/cjs/types/validation-error.types.js +11 -1
- package/dist/cjs/types/window.types.d.ts +1 -8
- package/dist/cjs/utils/data/uuid.utils.d.ts +1 -1
- package/dist/cjs/utils/data/uuid.utils.js +7 -5
- package/dist/cjs/utils/emitter.utils.d.ts +8 -0
- package/dist/cjs/utils/emitter.utils.js +33 -0
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/cjs/utils/logging/debug-logger.utils.js +36 -127
- package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/cjs/utils/network/fetch-with-timeout.utils.js +25 -0
- package/dist/cjs/utils/network/index.d.ts +1 -0
- package/dist/cjs/utils/network/index.js +1 -0
- package/dist/cjs/utils/network/url.utils.js +2 -42
- package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/cjs/utils/security/sanitize.utils.js +7 -41
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/cjs/utils/validations/config-validations.utils.js +77 -22
- package/dist/esm/api.d.ts +33 -19
- package/dist/esm/api.js +105 -118
- package/dist/esm/app.constants.d.ts +74 -1
- package/dist/esm/app.constants.js +76 -1
- package/dist/esm/app.d.ts +29 -44
- package/dist/esm/app.js +115 -213
- package/dist/esm/app.types.d.ts +2 -7
- package/dist/esm/app.types.js +1 -7
- package/dist/esm/constants/api.constants.js +10 -4
- package/dist/esm/constants/config.constants.d.ts +75 -0
- package/dist/esm/constants/config.constants.js +174 -0
- package/dist/esm/constants/error.constants.d.ts +29 -0
- package/dist/esm/constants/error.constants.js +47 -0
- package/dist/esm/constants/index.d.ts +3 -6
- package/dist/esm/constants/index.js +3 -6
- package/dist/esm/constants/performance.constants.d.ts +28 -0
- package/dist/esm/constants/performance.constants.js +40 -0
- package/dist/esm/handlers/click.handler.d.ts +1 -0
- package/dist/esm/handlers/click.handler.js +30 -49
- package/dist/esm/handlers/error.handler.d.ts +11 -6
- package/dist/esm/handlers/error.handler.js +91 -51
- package/dist/esm/handlers/page-view.handler.js +38 -29
- package/dist/esm/handlers/performance.handler.d.ts +3 -0
- package/dist/esm/handlers/performance.handler.js +71 -32
- package/dist/esm/handlers/scroll.handler.d.ts +16 -10
- package/dist/esm/handlers/scroll.handler.js +120 -186
- package/dist/esm/handlers/session.handler.d.ts +6 -20
- package/dist/esm/handlers/session.handler.js +38 -326
- package/dist/esm/integrations/google-analytics.integration.d.ts +0 -1
- package/dist/esm/integrations/google-analytics.integration.js +27 -98
- package/dist/esm/listeners/input-listener-managers.d.ts +18 -9
- package/dist/esm/listeners/input-listener-managers.js +23 -32
- package/dist/esm/listeners/touch-listener-manager.d.ts +1 -3
- package/dist/esm/listeners/touch-listener-manager.js +1 -23
- package/dist/esm/listeners/visibility-listener-manager.d.ts +1 -4
- package/dist/esm/listeners/visibility-listener-manager.js +6 -42
- package/dist/esm/managers/api.manager.d.ts +13 -3
- package/dist/esm/managers/api.manager.js +34 -3
- package/dist/esm/managers/config.manager.d.ts +53 -3
- package/dist/esm/managers/config.manager.js +133 -64
- package/dist/esm/managers/event.manager.d.ts +57 -36
- package/dist/esm/managers/event.manager.js +268 -419
- package/dist/esm/managers/sender.manager.d.ts +40 -22
- package/dist/esm/managers/sender.manager.js +201 -199
- package/dist/esm/managers/session.manager.d.ts +80 -66
- package/dist/esm/managers/session.manager.js +269 -524
- package/dist/esm/managers/state.manager.d.ts +33 -0
- package/dist/esm/managers/state.manager.js +78 -6
- package/dist/esm/managers/storage.manager.d.ts +26 -2
- package/dist/esm/managers/storage.manager.js +66 -33
- package/dist/esm/managers/tags.manager.d.ts +31 -7
- package/dist/esm/managers/tags.manager.js +124 -242
- package/dist/esm/managers/user.manager.d.ts +14 -5
- package/dist/esm/managers/user.manager.js +17 -9
- package/dist/esm/public-api.d.ts +10 -1
- package/dist/esm/public-api.js +14 -1
- package/dist/esm/test-bridge.d.ts +48 -0
- package/dist/esm/test-bridge.js +106 -0
- package/dist/esm/types/api.types.d.ts +21 -6
- package/dist/esm/types/api.types.js +21 -6
- package/dist/esm/types/config.types.d.ts +22 -84
- package/dist/esm/types/emitter.types.d.ts +11 -0
- package/dist/esm/types/emitter.types.js +5 -0
- package/dist/esm/types/event.types.d.ts +8 -11
- package/dist/esm/types/index.d.ts +3 -1
- package/dist/esm/types/index.js +3 -1
- package/dist/esm/types/queue.types.d.ts +1 -0
- package/dist/esm/types/session.types.d.ts +0 -64
- package/dist/esm/types/state.types.d.ts +1 -0
- package/dist/esm/types/test-bridge.types.d.ts +38 -0
- package/dist/esm/types/validation-error.types.d.ts +7 -0
- package/dist/esm/types/validation-error.types.js +9 -0
- package/dist/esm/types/window.types.d.ts +1 -8
- package/dist/esm/utils/data/uuid.utils.d.ts +1 -1
- package/dist/esm/utils/data/uuid.utils.js +7 -5
- package/dist/esm/utils/emitter.utils.d.ts +8 -0
- package/dist/esm/utils/emitter.utils.js +29 -0
- package/dist/esm/utils/index.d.ts +1 -0
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +10 -51
- package/dist/esm/utils/logging/debug-logger.utils.js +36 -127
- package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +4 -0
- package/dist/esm/utils/network/fetch-with-timeout.utils.js +22 -0
- package/dist/esm/utils/network/index.d.ts +1 -0
- package/dist/esm/utils/network/index.js +1 -0
- package/dist/esm/utils/network/url.utils.js +2 -42
- package/dist/esm/utils/security/sanitize.utils.d.ts +1 -8
- package/dist/esm/utils/security/sanitize.utils.js +6 -39
- package/dist/esm/utils/validations/config-validations.utils.d.ts +7 -0
- package/dist/esm/utils/validations/config-validations.utils.js +76 -22
- package/package.json +23 -16
- package/dist/browser/web-vitals-CCnqwnC8.mjs +0 -198
- package/dist/cjs/constants/browser.constants.d.ts +0 -3
- package/dist/cjs/constants/browser.constants.js +0 -41
- package/dist/cjs/constants/initialization.constants.d.ts +0 -40
- package/dist/cjs/constants/initialization.constants.js +0 -48
- package/dist/cjs/constants/limits.constants.d.ts +0 -27
- package/dist/cjs/constants/limits.constants.js +0 -43
- package/dist/cjs/constants/security.constants.d.ts +0 -1
- package/dist/cjs/constants/security.constants.js +0 -12
- package/dist/cjs/constants/timing.constants.d.ts +0 -22
- package/dist/cjs/constants/timing.constants.js +0 -34
- package/dist/cjs/constants/validation.constants.d.ts +0 -13
- package/dist/cjs/constants/validation.constants.js +0 -31
- package/dist/cjs/handlers/network.handler.d.ts +0 -16
- package/dist/cjs/handlers/network.handler.js +0 -136
- package/dist/cjs/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/cjs/managers/cross-tab-session.manager.js +0 -730
- package/dist/cjs/managers/sampling.manager.d.ts +0 -8
- package/dist/cjs/managers/sampling.manager.js +0 -53
- package/dist/cjs/managers/session-recovery.manager.d.ts +0 -65
- package/dist/cjs/managers/session-recovery.manager.js +0 -237
- package/dist/cjs/types/web-vitals.types.d.ts +0 -6
- package/dist/esm/constants/browser.constants.d.ts +0 -3
- package/dist/esm/constants/browser.constants.js +0 -38
- package/dist/esm/constants/initialization.constants.d.ts +0 -40
- package/dist/esm/constants/initialization.constants.js +0 -45
- package/dist/esm/constants/limits.constants.d.ts +0 -27
- package/dist/esm/constants/limits.constants.js +0 -40
- package/dist/esm/constants/security.constants.d.ts +0 -1
- package/dist/esm/constants/security.constants.js +0 -9
- package/dist/esm/constants/timing.constants.d.ts +0 -22
- package/dist/esm/constants/timing.constants.js +0 -31
- package/dist/esm/constants/validation.constants.d.ts +0 -13
- package/dist/esm/constants/validation.constants.js +0 -28
- package/dist/esm/handlers/network.handler.d.ts +0 -16
- package/dist/esm/handlers/network.handler.js +0 -132
- package/dist/esm/managers/cross-tab-session.manager.d.ts +0 -170
- package/dist/esm/managers/cross-tab-session.manager.js +0 -726
- package/dist/esm/managers/sampling.manager.d.ts +0 -8
- package/dist/esm/managers/sampling.manager.js +0 -49
- package/dist/esm/managers/session-recovery.manager.d.ts +0 -65
- package/dist/esm/managers/session-recovery.manager.js +0 -233
- package/dist/esm/types/web-vitals.types.d.ts +0 -6
- /package/dist/cjs/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
- /package/dist/esm/types/{web-vitals.types.js → test-bridge.types.js} +0 -0
|
@@ -1,53 +1,44 @@
|
|
|
1
1
|
import { debugLog } from '../utils/logging';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Base class for input listener managers to reduce code duplication
|
|
4
|
+
*/
|
|
5
|
+
class BaseInputListenerManager {
|
|
3
6
|
constructor(onActivity) {
|
|
4
7
|
this.options = { passive: true };
|
|
5
8
|
this.onActivity = onActivity;
|
|
6
9
|
}
|
|
7
10
|
setup() {
|
|
8
11
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
this.events.forEach((event) => {
|
|
13
|
+
window.addEventListener(event, this.onActivity, this.options);
|
|
14
|
+
});
|
|
12
15
|
}
|
|
13
16
|
catch (error) {
|
|
14
|
-
debugLog.error(
|
|
15
|
-
throw error;
|
|
17
|
+
debugLog.error(this.logPrefix, `Failed to setup ${this.logPrefix.toLowerCase()} listeners`, { error });
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
cleanup() {
|
|
19
21
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
this.events.forEach((event) => {
|
|
23
|
+
window.removeEventListener(event, this.onActivity);
|
|
24
|
+
});
|
|
23
25
|
}
|
|
24
26
|
catch (error) {
|
|
25
|
-
debugLog.warn(
|
|
27
|
+
debugLog.warn(this.logPrefix, `Error during ${this.logPrefix.toLowerCase()} listeners cleanup`, { error });
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
|
-
export class
|
|
30
|
-
constructor(
|
|
31
|
-
|
|
32
|
-
this.
|
|
31
|
+
export class MouseListenerManager extends BaseInputListenerManager {
|
|
32
|
+
constructor() {
|
|
33
|
+
super(...arguments);
|
|
34
|
+
this.events = ['mousemove', 'mousedown', 'wheel'];
|
|
35
|
+
this.logPrefix = 'MouseListenerManager';
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
debugLog.error('KeyboardListenerManager', 'Failed to setup keyboard listeners', { error });
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
cleanup() {
|
|
45
|
-
try {
|
|
46
|
-
window.removeEventListener('keydown', this.onActivity);
|
|
47
|
-
window.removeEventListener('keypress', this.onActivity);
|
|
48
|
-
}
|
|
49
|
-
catch (error) {
|
|
50
|
-
debugLog.warn('KeyboardListenerManager', 'Error during keyboard listeners cleanup', { error });
|
|
51
|
-
}
|
|
37
|
+
}
|
|
38
|
+
export class KeyboardListenerManager extends BaseInputListenerManager {
|
|
39
|
+
constructor() {
|
|
40
|
+
super(...arguments);
|
|
41
|
+
this.events = ['keydown'];
|
|
42
|
+
this.logPrefix = 'KeyboardListenerManager';
|
|
52
43
|
}
|
|
53
44
|
}
|
|
@@ -2,9 +2,7 @@ import { EventListenerManager } from './listeners.types';
|
|
|
2
2
|
export declare class TouchListenerManager implements EventListenerManager {
|
|
3
3
|
private readonly onActivity;
|
|
4
4
|
private readonly options;
|
|
5
|
-
|
|
6
|
-
constructor(onActivity: () => void, motionThreshold: number);
|
|
5
|
+
constructor(onActivity: () => void);
|
|
7
6
|
setup(): void;
|
|
8
7
|
cleanup(): void;
|
|
9
|
-
private readonly handleDeviceMotion;
|
|
10
8
|
}
|
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
import { debugLog } from '../utils/logging';
|
|
2
2
|
export class TouchListenerManager {
|
|
3
|
-
constructor(onActivity
|
|
3
|
+
constructor(onActivity) {
|
|
4
4
|
this.options = { passive: true };
|
|
5
|
-
this.handleDeviceMotion = (event) => {
|
|
6
|
-
try {
|
|
7
|
-
const acceleration = event.acceleration;
|
|
8
|
-
if (acceleration) {
|
|
9
|
-
const totalAcceleration = Math.abs(acceleration.x ?? 0) + Math.abs(acceleration.y ?? 0) + Math.abs(acceleration.z ?? 0);
|
|
10
|
-
if (totalAcceleration > this.motionThreshold) {
|
|
11
|
-
this.onActivity();
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
debugLog.warn('TouchListenerManager', 'Error handling device motion event', { error });
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
5
|
this.onActivity = onActivity;
|
|
20
|
-
this.motionThreshold = motionThreshold;
|
|
21
6
|
}
|
|
22
7
|
setup() {
|
|
23
8
|
try {
|
|
@@ -25,10 +10,6 @@ export class TouchListenerManager {
|
|
|
25
10
|
window.addEventListener('touchmove', this.onActivity, this.options);
|
|
26
11
|
window.addEventListener('touchend', this.onActivity, this.options);
|
|
27
12
|
window.addEventListener('orientationchange', this.onActivity, this.options);
|
|
28
|
-
const hasDeviceMotion = 'DeviceMotionEvent' in window;
|
|
29
|
-
if (hasDeviceMotion) {
|
|
30
|
-
window.addEventListener('devicemotion', this.handleDeviceMotion, this.options);
|
|
31
|
-
}
|
|
32
13
|
}
|
|
33
14
|
catch (error) {
|
|
34
15
|
debugLog.error('TouchListenerManager', 'Failed to setup touch listeners', { error });
|
|
@@ -41,9 +22,6 @@ export class TouchListenerManager {
|
|
|
41
22
|
window.removeEventListener('touchmove', this.onActivity);
|
|
42
23
|
window.removeEventListener('touchend', this.onActivity);
|
|
43
24
|
window.removeEventListener('orientationchange', this.onActivity);
|
|
44
|
-
if ('DeviceMotionEvent' in window) {
|
|
45
|
-
window.removeEventListener('devicemotion', this.handleDeviceMotion);
|
|
46
|
-
}
|
|
47
25
|
}
|
|
48
26
|
catch (error) {
|
|
49
27
|
debugLog.warn('TouchListenerManager', 'Error during touch listeners cleanup', { error });
|
|
@@ -2,11 +2,8 @@ import { EventListenerManager } from './listeners.types';
|
|
|
2
2
|
export declare class VisibilityListenerManager implements EventListenerManager {
|
|
3
3
|
private readonly onActivity;
|
|
4
4
|
private readonly onVisibilityChange;
|
|
5
|
-
private readonly isMobile;
|
|
6
5
|
private readonly options;
|
|
7
|
-
constructor(onActivity: () => void, onVisibilityChange: () => void
|
|
6
|
+
constructor(onActivity: () => void, onVisibilityChange: () => void);
|
|
8
7
|
setup(): void;
|
|
9
8
|
cleanup(): void;
|
|
10
|
-
private setupMobileEvents;
|
|
11
|
-
private cleanupMobileEvents;
|
|
12
9
|
}
|
|
@@ -1,31 +1,27 @@
|
|
|
1
1
|
import { debugLog } from '../utils/logging';
|
|
2
2
|
export class VisibilityListenerManager {
|
|
3
|
-
constructor(onActivity, onVisibilityChange
|
|
3
|
+
constructor(onActivity, onVisibilityChange) {
|
|
4
4
|
this.options = { passive: true };
|
|
5
5
|
this.onActivity = onActivity;
|
|
6
6
|
this.onVisibilityChange = onVisibilityChange;
|
|
7
|
-
this.isMobile = isMobile;
|
|
8
7
|
}
|
|
9
8
|
setup() {
|
|
10
9
|
try {
|
|
11
|
-
|
|
12
|
-
if (
|
|
10
|
+
// Core visibility API support
|
|
11
|
+
if ('visibilityState' in document) {
|
|
13
12
|
document.addEventListener('visibilitychange', this.onVisibilityChange, this.options);
|
|
14
13
|
}
|
|
14
|
+
// Window focus/blur events
|
|
15
15
|
window.addEventListener('blur', this.onVisibilityChange, this.options);
|
|
16
16
|
window.addEventListener('focus', this.onActivity, this.options);
|
|
17
|
-
|
|
18
|
-
if (
|
|
17
|
+
// Basic network status detection
|
|
18
|
+
if ('onLine' in navigator) {
|
|
19
19
|
window.addEventListener('online', this.onActivity, this.options);
|
|
20
20
|
window.addEventListener('offline', this.onVisibilityChange, this.options);
|
|
21
21
|
}
|
|
22
|
-
if (this.isMobile) {
|
|
23
|
-
this.setupMobileEvents();
|
|
24
|
-
}
|
|
25
22
|
}
|
|
26
23
|
catch (error) {
|
|
27
24
|
debugLog.error('VisibilityListenerManager', 'Failed to setup visibility listeners', { error });
|
|
28
|
-
throw error;
|
|
29
25
|
}
|
|
30
26
|
}
|
|
31
27
|
cleanup() {
|
|
@@ -39,41 +35,9 @@ export class VisibilityListenerManager {
|
|
|
39
35
|
window.removeEventListener('online', this.onActivity);
|
|
40
36
|
window.removeEventListener('offline', this.onVisibilityChange);
|
|
41
37
|
}
|
|
42
|
-
if (this.isMobile) {
|
|
43
|
-
this.cleanupMobileEvents();
|
|
44
|
-
}
|
|
45
38
|
}
|
|
46
39
|
catch (error) {
|
|
47
40
|
debugLog.warn('VisibilityListenerManager', 'Error during visibility listeners cleanup', { error });
|
|
48
41
|
}
|
|
49
42
|
}
|
|
50
|
-
setupMobileEvents() {
|
|
51
|
-
try {
|
|
52
|
-
document.addEventListener('pause', this.onVisibilityChange, this.options);
|
|
53
|
-
document.addEventListener('resume', this.onActivity, this.options);
|
|
54
|
-
const hasOrientationAPI = 'orientation' in screen;
|
|
55
|
-
if (hasOrientationAPI) {
|
|
56
|
-
screen.orientation.addEventListener('change', this.onActivity, this.options);
|
|
57
|
-
}
|
|
58
|
-
window.addEventListener('pageshow', this.onActivity, this.options);
|
|
59
|
-
window.addEventListener('pagehide', this.onActivity, this.options);
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
debugLog.warn('VisibilityListenerManager', 'Failed to setup mobile listeners', { error });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
cleanupMobileEvents() {
|
|
66
|
-
try {
|
|
67
|
-
document.removeEventListener('pause', this.onVisibilityChange);
|
|
68
|
-
document.removeEventListener('resume', this.onActivity);
|
|
69
|
-
if ('orientation' in screen) {
|
|
70
|
-
screen.orientation.removeEventListener('change', this.onActivity);
|
|
71
|
-
}
|
|
72
|
-
window.removeEventListener('pageshow', this.onActivity);
|
|
73
|
-
window.removeEventListener('pagehide', this.onActivity);
|
|
74
|
-
}
|
|
75
|
-
catch (error) {
|
|
76
|
-
debugLog.warn('VisibilityListenerManager', 'Error during mobile listeners cleanup', { error });
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
43
|
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Generates API URL for TraceLog service based on project ID
|
|
3
|
+
*
|
|
4
|
+
* Handles two special cases:
|
|
5
|
+
* - 'localhost:PORT' - for local development (generates http://localhost:PORT)
|
|
6
|
+
* - Regular project IDs - generates subdomain URLs via getApiUrl utility
|
|
7
|
+
*
|
|
8
|
+
* @param id Project ID or localhost address
|
|
9
|
+
* @param allowHttp Whether to allow HTTP protocol (default: false)
|
|
10
|
+
* @returns Generated API URL
|
|
11
|
+
* @throws Error if URL generation or validation fails
|
|
12
|
+
*/
|
|
13
|
+
export declare function getApiUrlForProject(id: string, allowHttp?: boolean): string;
|
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
import { getApiUrl, isValidUrl } from '../utils';
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
import { SpecialProjectId } from '../types';
|
|
3
|
+
import { debugLog } from '../utils/logging';
|
|
4
|
+
/**
|
|
5
|
+
* Generates API URL for TraceLog service based on project ID
|
|
6
|
+
*
|
|
7
|
+
* Handles two special cases:
|
|
8
|
+
* - 'localhost:PORT' - for local development (generates http://localhost:PORT)
|
|
9
|
+
* - Regular project IDs - generates subdomain URLs via getApiUrl utility
|
|
10
|
+
*
|
|
11
|
+
* @param id Project ID or localhost address
|
|
12
|
+
* @param allowHttp Whether to allow HTTP protocol (default: false)
|
|
13
|
+
* @returns Generated API URL
|
|
14
|
+
* @throws Error if URL generation or validation fails
|
|
15
|
+
*/
|
|
16
|
+
export function getApiUrlForProject(id, allowHttp = false) {
|
|
17
|
+
try {
|
|
18
|
+
// Handle localhost development case
|
|
19
|
+
if (id.startsWith(SpecialProjectId.Localhost)) {
|
|
20
|
+
const url = `http://${id}`;
|
|
21
|
+
if (!isValidUrl(url, true)) {
|
|
22
|
+
throw new Error(`Invalid localhost URL format: ${id}`);
|
|
23
|
+
}
|
|
24
|
+
return url;
|
|
25
|
+
}
|
|
26
|
+
// Handle regular project ID case
|
|
4
27
|
const url = getApiUrl(id, allowHttp);
|
|
5
28
|
if (!isValidUrl(url, allowHttp)) {
|
|
6
|
-
throw new Error(
|
|
29
|
+
throw new Error(`Generated API URL failed validation: ${url}`);
|
|
7
30
|
}
|
|
8
31
|
return url;
|
|
9
32
|
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
debugLog.error('ApiManager', 'API URL generation failed', {
|
|
35
|
+
projectId: id,
|
|
36
|
+
allowHttp,
|
|
37
|
+
error: error instanceof Error ? error.message : error,
|
|
38
|
+
});
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
10
41
|
}
|
|
@@ -1,7 +1,57 @@
|
|
|
1
1
|
import { AppConfig, Config } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration manager responsible for loading and merging application configuration.
|
|
4
|
+
*
|
|
5
|
+
* Handles three configuration sources:
|
|
6
|
+
* 1. Default configuration (fallback values)
|
|
7
|
+
* 2. API configuration (server-side settings)
|
|
8
|
+
* 3. App configuration (client initialization settings)
|
|
9
|
+
*
|
|
10
|
+
* Supports special project IDs for development and testing:
|
|
11
|
+
* - 'skip': Bypasses all network calls, uses defaults
|
|
12
|
+
* - 'localhost:PORT': Loads config from local development server
|
|
13
|
+
*/
|
|
2
14
|
export declare class ConfigManager {
|
|
15
|
+
private static readonly LOCALHOST_PATTERN;
|
|
16
|
+
private static readonly PRODUCTION_DOMAINS;
|
|
17
|
+
/**
|
|
18
|
+
* Gets complete configuration by merging default, API, and app configurations.
|
|
19
|
+
*
|
|
20
|
+
* @param apiUrl - Base URL for the configuration API
|
|
21
|
+
* @param appConfig - Client-side configuration from init()
|
|
22
|
+
* @returns Promise<Config> - Merged configuration object
|
|
23
|
+
*/
|
|
3
24
|
get(apiUrl: string, appConfig: AppConfig): Promise<Config>;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Loads configuration from API and merges with app config.
|
|
27
|
+
*/
|
|
28
|
+
private loadFromApi;
|
|
29
|
+
/**
|
|
30
|
+
* Builds the configuration URL based on project type and QA mode.
|
|
31
|
+
*/
|
|
32
|
+
private buildConfigUrl;
|
|
33
|
+
/**
|
|
34
|
+
* Builds request headers based on project configuration.
|
|
35
|
+
*/
|
|
36
|
+
private buildHeaders;
|
|
37
|
+
/**
|
|
38
|
+
* Parses and validates JSON response from config API.
|
|
39
|
+
*/
|
|
40
|
+
private parseJsonResponse;
|
|
41
|
+
/**
|
|
42
|
+
* Validates localhost project ID format and port range.
|
|
43
|
+
*/
|
|
44
|
+
private validateLocalhostProjectId;
|
|
45
|
+
/**
|
|
46
|
+
* Checks if QA mode is enabled via URL parameter.
|
|
47
|
+
*/
|
|
48
|
+
private isQaModeEnabled;
|
|
49
|
+
/**
|
|
50
|
+
* Merges API configuration with app configuration and applies mode-specific settings.
|
|
51
|
+
*/
|
|
52
|
+
private mergeConfigurations;
|
|
53
|
+
/**
|
|
54
|
+
* Creates default configuration for skip mode and fallback scenarios.
|
|
55
|
+
*/
|
|
56
|
+
private createDefaultConfig;
|
|
7
57
|
}
|
|
@@ -1,90 +1,159 @@
|
|
|
1
|
-
import { DEFAULT_API_CONFIG, DEFAULT_CONFIG } from '../constants';
|
|
1
|
+
import { DEFAULT_API_CONFIG, DEFAULT_CONFIG, REQUEST_TIMEOUT_MS } from '../constants';
|
|
2
2
|
import { Mode, SpecialProjectId } from '../types';
|
|
3
|
-
import {
|
|
3
|
+
import { sanitizeApiConfig, fetchWithTimeout, normalizeConfig } from '../utils';
|
|
4
4
|
import { debugLog } from '../utils/logging';
|
|
5
|
+
/**
|
|
6
|
+
* Configuration manager responsible for loading and merging application configuration.
|
|
7
|
+
*
|
|
8
|
+
* Handles three configuration sources:
|
|
9
|
+
* 1. Default configuration (fallback values)
|
|
10
|
+
* 2. API configuration (server-side settings)
|
|
11
|
+
* 3. App configuration (client initialization settings)
|
|
12
|
+
*
|
|
13
|
+
* Supports special project IDs for development and testing:
|
|
14
|
+
* - 'skip': Bypasses all network calls, uses defaults
|
|
15
|
+
* - 'localhost:PORT': Loads config from local development server
|
|
16
|
+
*/
|
|
5
17
|
export class ConfigManager {
|
|
18
|
+
/**
|
|
19
|
+
* Gets complete configuration by merging default, API, and app configurations.
|
|
20
|
+
*
|
|
21
|
+
* @param apiUrl - Base URL for the configuration API
|
|
22
|
+
* @param appConfig - Client-side configuration from init()
|
|
23
|
+
* @returns Promise<Config> - Merged configuration object
|
|
24
|
+
*/
|
|
6
25
|
async get(apiUrl, appConfig) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return this.
|
|
26
|
+
// Handle skip mode - no network calls
|
|
27
|
+
if (appConfig.id === SpecialProjectId.Skip) {
|
|
28
|
+
return this.createDefaultConfig(appConfig);
|
|
10
29
|
}
|
|
11
|
-
|
|
12
|
-
const config =
|
|
13
|
-
debugLog.info('ConfigManager', '
|
|
30
|
+
const config = await this.loadFromApi(apiUrl, appConfig);
|
|
31
|
+
const { config: normalizedConfig } = normalizeConfig(config);
|
|
32
|
+
debugLog.info('ConfigManager', 'Configuration loaded', {
|
|
14
33
|
projectId: appConfig.id,
|
|
15
|
-
mode:
|
|
16
|
-
|
|
17
|
-
|
|
34
|
+
mode: normalizedConfig.mode,
|
|
35
|
+
hasTags: !!normalizedConfig.tags?.length,
|
|
36
|
+
hasExclusions: !!normalizedConfig.excludedUrlPaths?.length,
|
|
18
37
|
});
|
|
19
|
-
return
|
|
38
|
+
return normalizedConfig;
|
|
20
39
|
}
|
|
21
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Loads configuration from API and merges with app config.
|
|
42
|
+
*/
|
|
43
|
+
async loadFromApi(apiUrl, appConfig) {
|
|
22
44
|
try {
|
|
23
|
-
const configUrl =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
const response = await fetch(configUrl, {
|
|
45
|
+
const configUrl = this.buildConfigUrl(apiUrl, appConfig);
|
|
46
|
+
const headers = this.buildHeaders(appConfig);
|
|
47
|
+
const response = await fetchWithTimeout(configUrl, {
|
|
28
48
|
method: 'GET',
|
|
29
|
-
headers
|
|
49
|
+
headers,
|
|
50
|
+
timeout: REQUEST_TIMEOUT_MS,
|
|
30
51
|
});
|
|
31
52
|
if (!response.ok) {
|
|
32
|
-
|
|
33
|
-
debugLog.error('ConfigManager', 'Config API request failed', {
|
|
34
|
-
status: response.status,
|
|
35
|
-
statusText: response.statusText,
|
|
36
|
-
configUrl,
|
|
37
|
-
});
|
|
38
|
-
throw new Error(error);
|
|
39
|
-
}
|
|
40
|
-
const rawData = await response.json();
|
|
41
|
-
if (rawData === undefined || rawData === null || typeof rawData !== 'object' || Array.isArray(rawData)) {
|
|
42
|
-
debugLog.error('ConfigManager', 'Invalid config API response format', {
|
|
43
|
-
responseType: typeof rawData,
|
|
44
|
-
isArray: Array.isArray(rawData),
|
|
45
|
-
});
|
|
46
|
-
throw new Error('Invalid config API response: expected object');
|
|
47
|
-
}
|
|
48
|
-
const safeApiConfig = sanitizeApiConfig(rawData);
|
|
49
|
-
const apiConfig = { ...DEFAULT_API_CONFIG, ...safeApiConfig };
|
|
50
|
-
const mergedConfig = { ...apiConfig, ...appConfig };
|
|
51
|
-
// Check if qaMode=true is in URL and automatically set mode to 'qa'
|
|
52
|
-
const urlParameters = new URLSearchParams(window.location.search);
|
|
53
|
-
const isQaMode = urlParameters.get('qaMode') === 'true';
|
|
54
|
-
if (isQaMode && !mergedConfig.mode) {
|
|
55
|
-
mergedConfig.mode = Mode.QA;
|
|
56
|
-
debugLog.info('ConfigManager', 'QA mode enabled via URL parameter');
|
|
53
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
57
54
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
: (mergedConfig.errorSampling ?? 0.1);
|
|
61
|
-
const finalConfig = { ...mergedConfig, errorSampling };
|
|
62
|
-
return finalConfig;
|
|
55
|
+
const rawData = await this.parseJsonResponse(response);
|
|
56
|
+
return this.mergeConfigurations(rawData, appConfig);
|
|
63
57
|
}
|
|
64
58
|
catch (error) {
|
|
65
59
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
66
|
-
debugLog.error('ConfigManager', 'Failed to load
|
|
67
|
-
|
|
60
|
+
debugLog.error('ConfigManager', 'Failed to load configuration', {
|
|
61
|
+
error: errorMessage,
|
|
62
|
+
apiUrl,
|
|
63
|
+
projectId: appConfig.id,
|
|
64
|
+
});
|
|
65
|
+
throw new Error(`Configuration load failed: ${errorMessage}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Builds the configuration URL based on project type and QA mode.
|
|
70
|
+
*/
|
|
71
|
+
buildConfigUrl(apiUrl, appConfig) {
|
|
72
|
+
const isLocalhost = appConfig.id.startsWith(SpecialProjectId.Localhost);
|
|
73
|
+
if (isLocalhost) {
|
|
74
|
+
this.validateLocalhostProjectId(appConfig.id);
|
|
75
|
+
return `http://${appConfig.id}/config`;
|
|
76
|
+
}
|
|
77
|
+
const baseUrl = `${apiUrl}/config`;
|
|
78
|
+
const isQaMode = this.isQaModeEnabled();
|
|
79
|
+
return isQaMode ? `${baseUrl}?qaMode=true` : baseUrl;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Builds request headers based on project configuration.
|
|
83
|
+
*/
|
|
84
|
+
buildHeaders(appConfig) {
|
|
85
|
+
const headers = {
|
|
86
|
+
'Content-Type': 'application/json',
|
|
87
|
+
};
|
|
88
|
+
if (appConfig.id.startsWith(SpecialProjectId.Localhost)) {
|
|
89
|
+
headers['X-TraceLog-Project'] = appConfig.id;
|
|
90
|
+
}
|
|
91
|
+
return headers;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Parses and validates JSON response from config API.
|
|
95
|
+
*/
|
|
96
|
+
async parseJsonResponse(response) {
|
|
97
|
+
const contentType = response.headers.get('content-type');
|
|
98
|
+
if (!contentType?.includes('application/json')) {
|
|
99
|
+
throw new Error('Invalid response content-type, expected JSON');
|
|
100
|
+
}
|
|
101
|
+
const rawData = await response.json();
|
|
102
|
+
if (!rawData || typeof rawData !== 'object' || Array.isArray(rawData)) {
|
|
103
|
+
throw new Error('Invalid response format, expected object');
|
|
68
104
|
}
|
|
105
|
+
return rawData;
|
|
69
106
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Validates localhost project ID format and port range.
|
|
109
|
+
*/
|
|
110
|
+
validateLocalhostProjectId(projectId) {
|
|
111
|
+
if (!ConfigManager.LOCALHOST_PATTERN.test(projectId)) {
|
|
112
|
+
throw new Error(`Invalid localhost format. Expected 'localhost:PORT', got '${projectId}'`);
|
|
76
113
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
throw new Error(
|
|
114
|
+
const port = parseInt(projectId.split(':')[1], 10);
|
|
115
|
+
if (port < 1 || port > 65535) {
|
|
116
|
+
throw new Error(`Port must be between 1 and 65535, got ${port}`);
|
|
80
117
|
}
|
|
81
|
-
return configUrl;
|
|
82
118
|
}
|
|
83
|
-
|
|
84
|
-
|
|
119
|
+
/**
|
|
120
|
+
* Checks if QA mode is enabled via URL parameter.
|
|
121
|
+
*/
|
|
122
|
+
isQaModeEnabled() {
|
|
123
|
+
const params = new URLSearchParams(window.location.search);
|
|
124
|
+
return params.get('qaMode') === 'true';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Merges API configuration with app configuration and applies mode-specific settings.
|
|
128
|
+
*/
|
|
129
|
+
mergeConfigurations(rawApiConfig, appConfig) {
|
|
130
|
+
const safeApiConfig = sanitizeApiConfig(rawApiConfig);
|
|
131
|
+
const apiConfig = { ...DEFAULT_API_CONFIG, ...safeApiConfig };
|
|
132
|
+
const mergedConfig = DEFAULT_CONFIG({ ...appConfig, ...apiConfig });
|
|
133
|
+
const { config: normalizedConfig } = normalizeConfig(mergedConfig);
|
|
134
|
+
// Apply QA mode if enabled via URL parameter
|
|
135
|
+
if (this.isQaModeEnabled() && !normalizedConfig.mode) {
|
|
136
|
+
normalizedConfig.mode = Mode.QA;
|
|
137
|
+
debugLog.info('ConfigManager', 'QA mode enabled via URL parameter');
|
|
138
|
+
}
|
|
139
|
+
// Set error sampling based on mode
|
|
140
|
+
const errorSampling = Object.values(Mode).includes(normalizedConfig.mode)
|
|
141
|
+
? 1 // Full sampling for debug/qa modes
|
|
142
|
+
: (normalizedConfig.errorSampling ?? 0.1); // Default sampling for production
|
|
143
|
+
return { ...normalizedConfig, errorSampling };
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Creates default configuration for skip mode and fallback scenarios.
|
|
147
|
+
*/
|
|
148
|
+
createDefaultConfig(appConfig) {
|
|
149
|
+
const defaultConfig = DEFAULT_CONFIG({
|
|
85
150
|
...appConfig,
|
|
86
151
|
errorSampling: 1,
|
|
87
|
-
...(
|
|
152
|
+
...(appConfig.id === SpecialProjectId.Skip && { mode: Mode.DEBUG }),
|
|
88
153
|
});
|
|
154
|
+
const { config } = normalizeConfig(defaultConfig);
|
|
155
|
+
return config;
|
|
89
156
|
}
|
|
90
157
|
}
|
|
158
|
+
ConfigManager.LOCALHOST_PATTERN = /^localhost:\d{1,5}$/;
|
|
159
|
+
ConfigManager.PRODUCTION_DOMAINS = [/^https:\/\/.*\.tracelog\.app$/, /^https:\/\/.*\.tracelog\.dev$/];
|