@tracelog/lib 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -180
- package/dist/browser/tracelog.esm.js +1007 -1357
- package/dist/browser/tracelog.js +2 -2
- package/dist/cjs/api.d.ts +12 -2
- package/dist/cjs/api.js +63 -27
- package/dist/cjs/app.d.ts +2 -2
- package/dist/cjs/app.js +26 -32
- package/dist/cjs/constants/config.constants.d.ts +4 -2
- package/dist/cjs/constants/config.constants.js +6 -18
- package/dist/cjs/constants/index.d.ts +0 -1
- package/dist/cjs/constants/index.js +0 -1
- package/dist/cjs/constants/storage.constants.d.ts +3 -2
- package/dist/cjs/constants/storage.constants.js +4 -4
- package/dist/cjs/handlers/click.handler.js +3 -6
- package/dist/cjs/handlers/error.handler.js +1 -11
- package/dist/cjs/handlers/page-view.handler.js +0 -4
- package/dist/cjs/handlers/performance.handler.js +14 -29
- package/dist/cjs/handlers/scroll.handler.js +7 -6
- package/dist/cjs/handlers/session.handler.js +7 -6
- package/dist/cjs/integrations/google-analytics.integration.js +2 -6
- package/dist/cjs/listeners/activity-listener-manager.js +3 -3
- package/dist/cjs/listeners/input-listener-managers.js +3 -3
- package/dist/cjs/listeners/touch-listener-manager.js +3 -3
- package/dist/cjs/listeners/unload-listener-manager.js +3 -3
- package/dist/cjs/listeners/visibility-listener-manager.js +3 -3
- package/dist/cjs/managers/event.manager.d.ts +2 -1
- package/dist/cjs/managers/event.manager.js +60 -38
- package/dist/cjs/managers/sender.manager.js +29 -36
- package/dist/cjs/managers/session.manager.js +5 -13
- package/dist/cjs/managers/state.manager.d.ts +0 -3
- package/dist/cjs/managers/state.manager.js +1 -43
- package/dist/cjs/managers/storage.manager.d.ts +16 -2
- package/dist/cjs/managers/storage.manager.js +73 -19
- package/dist/cjs/managers/user.manager.d.ts +1 -1
- package/dist/cjs/managers/user.manager.js +2 -2
- package/dist/cjs/public-api.d.ts +3 -3
- package/dist/cjs/public-api.js +1 -1
- package/dist/cjs/test-bridge.d.ts +1 -0
- package/dist/cjs/test-bridge.js +37 -2
- package/dist/cjs/types/config.types.d.ts +15 -18
- package/dist/cjs/types/config.types.js +6 -0
- package/dist/cjs/types/event.types.d.ts +1 -13
- package/dist/cjs/types/index.d.ts +0 -2
- package/dist/cjs/types/index.js +0 -2
- package/dist/cjs/types/mode.types.d.ts +1 -2
- package/dist/cjs/types/mode.types.js +0 -1
- package/dist/cjs/types/queue.types.d.ts +0 -6
- package/dist/cjs/types/state.types.d.ts +2 -0
- package/dist/cjs/types/test-bridge.types.d.ts +2 -2
- package/dist/cjs/types/validation-error.types.d.ts +0 -6
- package/dist/cjs/types/validation-error.types.js +1 -10
- package/dist/cjs/utils/browser/device-detector.utils.js +2 -24
- package/dist/cjs/utils/browser/index.d.ts +1 -0
- package/dist/cjs/utils/browser/index.js +1 -0
- package/dist/cjs/utils/browser/qa-mode.utils.d.ts +13 -0
- package/dist/cjs/utils/browser/qa-mode.utils.js +43 -0
- package/dist/cjs/utils/browser/utm-params.utils.js +0 -15
- package/dist/cjs/utils/data/uuid.utils.d.ts +13 -0
- package/dist/cjs/utils/data/uuid.utils.js +37 -1
- package/dist/cjs/utils/index.d.ts +1 -1
- package/dist/cjs/utils/index.js +1 -1
- package/dist/cjs/utils/logging.utils.d.ts +6 -0
- package/dist/cjs/utils/logging.utils.js +25 -0
- package/dist/cjs/utils/network/index.d.ts +0 -1
- package/dist/cjs/utils/network/index.js +0 -1
- package/dist/cjs/utils/network/url.utils.d.ts +2 -8
- package/dist/cjs/utils/network/url.utils.js +46 -90
- package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -13
- package/dist/cjs/utils/security/sanitize.utils.js +15 -178
- package/dist/cjs/utils/validations/config-validations.utils.d.ts +3 -9
- package/dist/cjs/utils/validations/config-validations.utils.js +48 -94
- package/dist/cjs/utils/validations/event-validations.utils.js +11 -5
- package/dist/cjs/utils/validations/index.d.ts +0 -1
- package/dist/cjs/utils/validations/index.js +0 -1
- package/dist/cjs/utils/validations/metadata-validations.utils.js +0 -1
- package/dist/cjs/utils/validations/type-guards.utils.d.ts +2 -2
- package/dist/cjs/utils/validations/type-guards.utils.js +50 -4
- package/dist/esm/api.d.ts +12 -2
- package/dist/esm/api.js +62 -27
- package/dist/esm/app.d.ts +2 -2
- package/dist/esm/app.js +28 -34
- package/dist/esm/constants/config.constants.d.ts +4 -2
- package/dist/esm/constants/config.constants.js +4 -16
- package/dist/esm/constants/index.d.ts +0 -1
- package/dist/esm/constants/index.js +0 -1
- package/dist/esm/constants/storage.constants.d.ts +3 -2
- package/dist/esm/constants/storage.constants.js +3 -2
- package/dist/esm/handlers/click.handler.js +3 -6
- package/dist/esm/handlers/error.handler.js +1 -11
- package/dist/esm/handlers/page-view.handler.js +0 -4
- package/dist/esm/handlers/performance.handler.js +14 -29
- package/dist/esm/handlers/scroll.handler.js +7 -6
- package/dist/esm/handlers/session.handler.js +7 -6
- package/dist/esm/integrations/google-analytics.integration.js +3 -7
- package/dist/esm/listeners/activity-listener-manager.js +3 -3
- package/dist/esm/listeners/input-listener-managers.js +3 -3
- package/dist/esm/listeners/touch-listener-manager.js +3 -3
- package/dist/esm/listeners/unload-listener-manager.js +3 -3
- package/dist/esm/listeners/visibility-listener-manager.js +3 -3
- package/dist/esm/managers/event.manager.d.ts +2 -1
- package/dist/esm/managers/event.manager.js +62 -40
- package/dist/esm/managers/sender.manager.js +31 -38
- package/dist/esm/managers/session.manager.js +5 -13
- package/dist/esm/managers/state.manager.d.ts +0 -3
- package/dist/esm/managers/state.manager.js +1 -43
- package/dist/esm/managers/storage.manager.d.ts +16 -2
- package/dist/esm/managers/storage.manager.js +73 -19
- package/dist/esm/managers/user.manager.d.ts +1 -1
- package/dist/esm/managers/user.manager.js +2 -2
- package/dist/esm/public-api.d.ts +3 -3
- package/dist/esm/public-api.js +1 -1
- package/dist/esm/test-bridge.d.ts +1 -0
- package/dist/esm/test-bridge.js +37 -2
- package/dist/esm/types/config.types.d.ts +15 -18
- package/dist/esm/types/config.types.js +5 -1
- package/dist/esm/types/event.types.d.ts +1 -13
- package/dist/esm/types/index.d.ts +0 -2
- package/dist/esm/types/index.js +0 -2
- package/dist/esm/types/mode.types.d.ts +1 -2
- package/dist/esm/types/mode.types.js +0 -1
- package/dist/esm/types/queue.types.d.ts +0 -6
- package/dist/esm/types/state.types.d.ts +2 -0
- package/dist/esm/types/test-bridge.types.d.ts +2 -2
- package/dist/esm/types/validation-error.types.d.ts +0 -6
- package/dist/esm/types/validation-error.types.js +0 -8
- package/dist/esm/utils/browser/device-detector.utils.js +2 -24
- package/dist/esm/utils/browser/index.d.ts +1 -0
- package/dist/esm/utils/browser/index.js +1 -0
- package/dist/esm/utils/browser/qa-mode.utils.d.ts +13 -0
- package/dist/esm/utils/browser/qa-mode.utils.js +39 -0
- package/dist/esm/utils/browser/utm-params.utils.js +0 -15
- package/dist/esm/utils/data/uuid.utils.d.ts +13 -0
- package/dist/esm/utils/data/uuid.utils.js +35 -0
- package/dist/esm/utils/index.d.ts +1 -1
- package/dist/esm/utils/index.js +1 -1
- package/dist/esm/utils/logging.utils.d.ts +6 -0
- package/dist/esm/utils/logging.utils.js +20 -0
- package/dist/esm/utils/network/index.d.ts +0 -1
- package/dist/esm/utils/network/index.js +0 -1
- package/dist/esm/utils/network/url.utils.d.ts +2 -8
- package/dist/esm/utils/network/url.utils.js +45 -88
- package/dist/esm/utils/security/sanitize.utils.d.ts +1 -13
- package/dist/esm/utils/security/sanitize.utils.js +15 -176
- package/dist/esm/utils/validations/config-validations.utils.d.ts +3 -9
- package/dist/esm/utils/validations/config-validations.utils.js +49 -94
- package/dist/esm/utils/validations/event-validations.utils.js +11 -5
- package/dist/esm/utils/validations/index.d.ts +0 -1
- package/dist/esm/utils/validations/index.js +0 -1
- package/dist/esm/utils/validations/metadata-validations.utils.js +0 -1
- package/dist/esm/utils/validations/type-guards.utils.d.ts +2 -2
- package/dist/esm/utils/validations/type-guards.utils.js +50 -4
- package/package.json +1 -1
- package/dist/cjs/app.types.d.ts +0 -2
- package/dist/cjs/app.types.js +0 -12
- package/dist/cjs/constants/api.constants.d.ts +0 -6
- package/dist/cjs/constants/api.constants.js +0 -14
- package/dist/cjs/managers/api.manager.d.ts +0 -13
- package/dist/cjs/managers/api.manager.js +0 -44
- package/dist/cjs/managers/config.builder.d.ts +0 -33
- package/dist/cjs/managers/config.builder.js +0 -116
- package/dist/cjs/managers/config.manager.d.ts +0 -56
- package/dist/cjs/managers/config.manager.js +0 -157
- package/dist/cjs/managers/tags.manager.d.ts +0 -36
- package/dist/cjs/managers/tags.manager.js +0 -171
- package/dist/cjs/types/api.types.d.ts +0 -52
- package/dist/cjs/types/api.types.js +0 -56
- package/dist/cjs/types/tag.types.d.ts +0 -43
- package/dist/cjs/types/tag.types.js +0 -31
- package/dist/cjs/utils/logging/debug-logger.utils.d.ts +0 -14
- package/dist/cjs/utils/logging/debug-logger.utils.js +0 -47
- package/dist/cjs/utils/logging/index.d.ts +0 -1
- package/dist/cjs/utils/logging/index.js +0 -5
- package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +0 -4
- package/dist/cjs/utils/network/fetch-with-timeout.utils.js +0 -25
- package/dist/cjs/utils/validations/url-validations.utils.d.ts +0 -15
- package/dist/cjs/utils/validations/url-validations.utils.js +0 -47
- package/dist/esm/app.types.d.ts +0 -2
- package/dist/esm/app.types.js +0 -1
- package/dist/esm/constants/api.constants.d.ts +0 -6
- package/dist/esm/constants/api.constants.js +0 -11
- package/dist/esm/managers/api.manager.d.ts +0 -13
- package/dist/esm/managers/api.manager.js +0 -41
- package/dist/esm/managers/config.builder.d.ts +0 -33
- package/dist/esm/managers/config.builder.js +0 -112
- package/dist/esm/managers/config.manager.d.ts +0 -56
- package/dist/esm/managers/config.manager.js +0 -153
- package/dist/esm/managers/tags.manager.d.ts +0 -36
- package/dist/esm/managers/tags.manager.js +0 -167
- package/dist/esm/types/api.types.d.ts +0 -52
- package/dist/esm/types/api.types.js +0 -53
- package/dist/esm/types/tag.types.d.ts +0 -43
- package/dist/esm/types/tag.types.js +0 -28
- package/dist/esm/utils/logging/debug-logger.utils.d.ts +0 -14
- package/dist/esm/utils/logging/debug-logger.utils.js +0 -44
- package/dist/esm/utils/logging/index.d.ts +0 -1
- package/dist/esm/utils/logging/index.js +0 -1
- package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +0 -4
- package/dist/esm/utils/network/fetch-with-timeout.utils.js +0 -22
- package/dist/esm/utils/validations/url-validations.utils.d.ts +0 -15
- package/dist/esm/utils/validations/url-validations.utils.js +0 -42
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isOnlyPrimitiveFields = void 0;
|
|
4
|
+
const constants_1 = require("../../constants");
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
+
* Validates if an item in an array is a valid nested object
|
|
7
|
+
* @param item - The item to validate
|
|
8
|
+
* @returns True if the item is a valid nested object
|
|
9
|
+
*/
|
|
10
|
+
const isValidArrayItem = (item) => {
|
|
11
|
+
if (typeof item === 'string') {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
// Allow objects with primitive fields only (one level deep)
|
|
15
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
16
|
+
const entries = Object.entries(item);
|
|
17
|
+
// Check key count limit
|
|
18
|
+
if (entries.length > constants_1.MAX_NESTED_OBJECT_KEYS) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
// All values must be primitives (no nested objects or arrays)
|
|
22
|
+
for (const [, value] of entries) {
|
|
23
|
+
if (value === null || value === undefined) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const type = typeof value;
|
|
27
|
+
if (type !== 'string' && type !== 'number' && type !== 'boolean') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Checks if an object contains only primitive fields, string arrays, or arrays of flat objects
|
|
6
37
|
* @param object - The object to check
|
|
7
|
-
* @returns True if the object contains only
|
|
38
|
+
* @returns True if the object contains only valid fields
|
|
8
39
|
*/
|
|
9
40
|
const isOnlyPrimitiveFields = (object) => {
|
|
10
41
|
if (typeof object !== 'object' || object === null) {
|
|
@@ -19,8 +50,23 @@ const isOnlyPrimitiveFields = (object) => {
|
|
|
19
50
|
continue;
|
|
20
51
|
}
|
|
21
52
|
if (Array.isArray(value)) {
|
|
22
|
-
if (
|
|
23
|
-
|
|
53
|
+
if (value.length === 0) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
// Determine array type from first item
|
|
57
|
+
const firstItem = value[0];
|
|
58
|
+
const isStringArray = typeof firstItem === 'string';
|
|
59
|
+
// All items must be of the same type (all strings OR all objects)
|
|
60
|
+
if (isStringArray) {
|
|
61
|
+
if (!value.every((item) => typeof item === 'string')) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Must be all objects
|
|
67
|
+
if (!value.every((item) => isValidArrayItem(item))) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
24
70
|
}
|
|
25
71
|
continue;
|
|
26
72
|
}
|
package/dist/esm/api.d.ts
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { App } from './app';
|
|
2
|
+
import { MetadataType, Config, EmitterCallback, EmitterMap } from './types';
|
|
2
3
|
import './types/window.types';
|
|
3
|
-
export declare const init: (
|
|
4
|
+
export declare const init: (config: Config) => Promise<void>;
|
|
4
5
|
export declare const event: (name: string, metadata?: Record<string, MetadataType> | Record<string, MetadataType>[]) => void;
|
|
5
6
|
export declare const on: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
6
7
|
export declare const off: <K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>) => void;
|
|
7
8
|
export declare const isInitialized: () => boolean;
|
|
8
9
|
export declare const destroy: () => Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Internal sync function - ONLY for TestBridge in development
|
|
12
|
+
*
|
|
13
|
+
* WARNING: This function is internal and should NEVER be called directly.
|
|
14
|
+
* It's only exported for TestBridge synchronization in dev mode.
|
|
15
|
+
*
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
18
|
+
export declare const __setAppInstance: (instance: App | null) => void;
|
package/dist/esm/api.js
CHANGED
|
@@ -1,48 +1,50 @@
|
|
|
1
1
|
import { App } from './app';
|
|
2
|
-
import {
|
|
2
|
+
import { log, validateAndNormalizeConfig } from './utils';
|
|
3
3
|
import { TestBridge } from './test-bridge';
|
|
4
4
|
import './types/window.types';
|
|
5
|
+
// Buffer for listeners registered before init()
|
|
6
|
+
const pendingListeners = [];
|
|
5
7
|
let app = null;
|
|
6
8
|
let isInitializing = false;
|
|
7
9
|
let isDestroying = false;
|
|
8
|
-
export const init = async (
|
|
10
|
+
export const init = async (config) => {
|
|
9
11
|
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
10
|
-
throw new Error('This library can only be used in a browser environment');
|
|
12
|
+
throw new Error('[TraceLog] This library can only be used in a browser environment');
|
|
11
13
|
}
|
|
12
14
|
if (window.__traceLogDisabled) {
|
|
13
15
|
return;
|
|
14
16
|
}
|
|
15
17
|
if (app) {
|
|
16
|
-
debugLog.debug('API', 'Library already initialized, skipping duplicate initialization');
|
|
17
18
|
return;
|
|
18
19
|
}
|
|
19
20
|
if (isInitializing) {
|
|
20
|
-
|
|
21
|
-
throw new Error('Initialization already in progress');
|
|
21
|
+
return;
|
|
22
22
|
}
|
|
23
23
|
isInitializing = true;
|
|
24
24
|
try {
|
|
25
|
-
|
|
26
|
-
const validatedConfig = validateAndNormalizeConfig(appConfig);
|
|
25
|
+
const validatedConfig = validateAndNormalizeConfig(config);
|
|
27
26
|
const instance = new App();
|
|
28
27
|
try {
|
|
28
|
+
// Attach buffered listeners BEFORE init() so they capture initial events
|
|
29
|
+
pendingListeners.forEach(({ event, callback }) => {
|
|
30
|
+
instance.on(event, callback);
|
|
31
|
+
});
|
|
32
|
+
pendingListeners.length = 0;
|
|
29
33
|
await instance.init(validatedConfig);
|
|
30
34
|
app = instance;
|
|
31
|
-
debugLog.info('API', 'TraceLog initialized successfully', { projectId: validatedConfig.id });
|
|
32
35
|
}
|
|
33
36
|
catch (error) {
|
|
34
37
|
try {
|
|
35
38
|
await instance.destroy(true);
|
|
36
39
|
}
|
|
37
40
|
catch (cleanupError) {
|
|
38
|
-
|
|
41
|
+
log('error', 'Failed to cleanup partially initialized app', { error: cleanupError });
|
|
39
42
|
}
|
|
40
43
|
throw error;
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
46
|
catch (error) {
|
|
44
47
|
app = null;
|
|
45
|
-
debugLog.error('API', 'Initialization failed', { error });
|
|
46
48
|
throw error;
|
|
47
49
|
}
|
|
48
50
|
finally {
|
|
@@ -51,25 +53,29 @@ export const init = async (appConfig) => {
|
|
|
51
53
|
};
|
|
52
54
|
export const event = (name, metadata) => {
|
|
53
55
|
if (!app) {
|
|
54
|
-
throw new Error('TraceLog not initialized. Please call init() first.');
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
app.sendCustomEvent(name, metadata);
|
|
56
|
+
throw new Error('[TraceLog] TraceLog not initialized. Please call init() first.');
|
|
58
57
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw error;
|
|
58
|
+
if (isDestroying) {
|
|
59
|
+
throw new Error('[TraceLog] Cannot send events while TraceLog is being destroyed');
|
|
62
60
|
}
|
|
61
|
+
app.sendCustomEvent(name, metadata);
|
|
63
62
|
};
|
|
64
63
|
export const on = (event, callback) => {
|
|
65
|
-
if (!app) {
|
|
66
|
-
|
|
64
|
+
if (!app || isInitializing) {
|
|
65
|
+
// Buffer listeners registered before or during init()
|
|
66
|
+
pendingListeners.push({ event, callback });
|
|
67
|
+
return;
|
|
67
68
|
}
|
|
68
69
|
app.on(event, callback);
|
|
69
70
|
};
|
|
70
71
|
export const off = (event, callback) => {
|
|
71
72
|
if (!app) {
|
|
72
|
-
|
|
73
|
+
// Remove from pending listeners if not yet initialized
|
|
74
|
+
const index = pendingListeners.findIndex((l) => l.event === event && l.callback === callback);
|
|
75
|
+
if (index !== -1) {
|
|
76
|
+
pendingListeners.splice(index, 1);
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
73
79
|
}
|
|
74
80
|
app.off(event, callback);
|
|
75
81
|
};
|
|
@@ -78,24 +84,28 @@ export const isInitialized = () => {
|
|
|
78
84
|
};
|
|
79
85
|
export const destroy = async () => {
|
|
80
86
|
if (!app) {
|
|
81
|
-
throw new Error('App not initialized');
|
|
87
|
+
throw new Error('[TraceLog] App not initialized');
|
|
82
88
|
}
|
|
83
89
|
if (isDestroying) {
|
|
84
|
-
throw new Error('Destroy operation already in progress');
|
|
90
|
+
throw new Error('[TraceLog] Destroy operation already in progress');
|
|
85
91
|
}
|
|
86
92
|
isDestroying = true;
|
|
87
93
|
try {
|
|
88
|
-
debugLog.info('API', 'Destroying TraceLog instance');
|
|
89
94
|
await app.destroy();
|
|
90
95
|
app = null;
|
|
91
96
|
isInitializing = false;
|
|
92
|
-
|
|
97
|
+
pendingListeners.length = 0;
|
|
98
|
+
// Clear TestBridge reference in dev mode to prevent stale references
|
|
99
|
+
if (process.env.NODE_ENV === 'dev' && typeof window !== 'undefined' && window.__traceLogBridge) {
|
|
100
|
+
// Don't call destroy on bridge (would cause recursion), just clear reference
|
|
101
|
+
window.__traceLogBridge = undefined;
|
|
102
|
+
}
|
|
93
103
|
}
|
|
94
104
|
catch (error) {
|
|
95
|
-
// Force cleanup even if destroy fails
|
|
96
105
|
app = null;
|
|
97
106
|
isInitializing = false;
|
|
98
|
-
|
|
107
|
+
pendingListeners.length = 0;
|
|
108
|
+
log('error', 'Error during destroy, forced cleanup', { error });
|
|
99
109
|
throw error;
|
|
100
110
|
}
|
|
101
111
|
finally {
|
|
@@ -113,3 +123,28 @@ if (process.env.NODE_ENV === 'dev' && typeof window !== 'undefined') {
|
|
|
113
123
|
injectTestingBridge();
|
|
114
124
|
}
|
|
115
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Internal sync function - ONLY for TestBridge in development
|
|
128
|
+
*
|
|
129
|
+
* WARNING: This function is internal and should NEVER be called directly.
|
|
130
|
+
* It's only exported for TestBridge synchronization in dev mode.
|
|
131
|
+
*
|
|
132
|
+
* @internal
|
|
133
|
+
*/
|
|
134
|
+
export const __setAppInstance = (instance) => {
|
|
135
|
+
if (instance !== null) {
|
|
136
|
+
const hasRequiredMethods = typeof instance === 'object' &&
|
|
137
|
+
'init' in instance &&
|
|
138
|
+
'destroy' in instance &&
|
|
139
|
+
'on' in instance &&
|
|
140
|
+
'off' in instance;
|
|
141
|
+
if (!hasRequiredMethods) {
|
|
142
|
+
throw new Error('[TraceLog] Invalid app instance type');
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Prevent overwriting an already initialized app (except when clearing)
|
|
146
|
+
if (app !== null && instance !== null && app !== instance) {
|
|
147
|
+
throw new Error('[TraceLog] Cannot overwrite existing app instance. Call destroy() first.');
|
|
148
|
+
}
|
|
149
|
+
app = instance;
|
|
150
|
+
};
|
package/dist/esm/app.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { SessionHandler } from './handlers/session.handler';
|
|
|
4
4
|
import { PageViewHandler } from './handlers/page-view.handler';
|
|
5
5
|
import { ClickHandler } from './handlers/click.handler';
|
|
6
6
|
import { ScrollHandler } from './handlers/scroll.handler';
|
|
7
|
-
import {
|
|
7
|
+
import { Config, EmitterCallback, EmitterMap } from './types';
|
|
8
8
|
import { GoogleAnalyticsIntegration } from './integrations/google-analytics.integration';
|
|
9
9
|
import { StorageManager } from './managers/storage.manager';
|
|
10
10
|
import { PerformanceHandler } from './handlers/performance.handler';
|
|
@@ -29,7 +29,7 @@ export declare class App extends StateManager {
|
|
|
29
29
|
googleAnalytics?: GoogleAnalyticsIntegration;
|
|
30
30
|
};
|
|
31
31
|
get initialized(): boolean;
|
|
32
|
-
init(
|
|
32
|
+
init(config: Config): Promise<void>;
|
|
33
33
|
sendCustomEvent(name: string, metadata?: Record<string, unknown> | Record<string, unknown>[]): void;
|
|
34
34
|
on<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void;
|
|
35
35
|
off<K extends keyof EmitterMap>(event: K, callback: EmitterCallback<EmitterMap[K]>): void;
|
package/dist/esm/app.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { getApiUrlForProject } from './managers/api.manager';
|
|
2
|
-
import { ConfigManager } from './managers/config.manager';
|
|
3
1
|
import { EventManager } from './managers/event.manager';
|
|
4
2
|
import { UserManager } from './managers/user.manager';
|
|
5
3
|
import { StateManager } from './managers/state.manager';
|
|
@@ -7,9 +5,9 @@ import { SessionHandler } from './handlers/session.handler';
|
|
|
7
5
|
import { PageViewHandler } from './handlers/page-view.handler';
|
|
8
6
|
import { ClickHandler } from './handlers/click.handler';
|
|
9
7
|
import { ScrollHandler } from './handlers/scroll.handler';
|
|
10
|
-
import { EventType } from './types';
|
|
8
|
+
import { EventType, Mode } from './types';
|
|
11
9
|
import { GoogleAnalyticsIntegration } from './integrations/google-analytics.integration';
|
|
12
|
-
import { isEventValid, getDeviceType, normalizeUrl,
|
|
10
|
+
import { isEventValid, getDeviceType, normalizeUrl, Emitter, getApiUrl, detectQaMode, log } from './utils';
|
|
13
11
|
import { StorageManager } from './managers/storage.manager';
|
|
14
12
|
import { SCROLL_DEBOUNCE_TIME_MS, SCROLL_SUPPRESS_MULTIPLIER } from './constants/config.constants';
|
|
15
13
|
import { PerformanceHandler } from './handlers/performance.handler';
|
|
@@ -27,27 +25,25 @@ export class App extends StateManager {
|
|
|
27
25
|
get initialized() {
|
|
28
26
|
return this.isInitialized;
|
|
29
27
|
}
|
|
30
|
-
async init(
|
|
28
|
+
async init(config) {
|
|
31
29
|
if (this.isInitialized) {
|
|
32
30
|
return;
|
|
33
31
|
}
|
|
34
|
-
if (!appConfig.id?.trim()) {
|
|
35
|
-
throw new Error('Project ID is required');
|
|
36
|
-
}
|
|
37
32
|
this.managers.storage = new StorageManager();
|
|
38
33
|
try {
|
|
39
|
-
|
|
34
|
+
this.setupState(config);
|
|
40
35
|
await this.setupIntegrations();
|
|
41
36
|
this.managers.event = new EventManager(this.managers.storage, this.integrations.googleAnalytics, this.emitter);
|
|
42
|
-
this.initializeHandlers();
|
|
43
|
-
await this.managers.event.recoverPersistedEvents().catch(() => {
|
|
44
|
-
|
|
37
|
+
await this.initializeHandlers();
|
|
38
|
+
await this.managers.event.recoverPersistedEvents().catch((error) => {
|
|
39
|
+
log('warn', 'Failed to recover persisted events', { error });
|
|
45
40
|
});
|
|
46
41
|
this.isInitialized = true;
|
|
47
42
|
}
|
|
48
43
|
catch (error) {
|
|
49
44
|
await this.destroy(true);
|
|
50
|
-
|
|
45
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
46
|
+
throw new Error(`[TraceLog] TraceLog initialization failed: ${errorMessage}`);
|
|
51
47
|
}
|
|
52
48
|
}
|
|
53
49
|
sendCustomEvent(name, metadata) {
|
|
@@ -56,9 +52,8 @@ export class App extends StateManager {
|
|
|
56
52
|
}
|
|
57
53
|
const { valid, error, sanitizedMetadata } = isEventValid(name, metadata);
|
|
58
54
|
if (!valid) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
throw new Error(`Custom event "${name}" validation failed: ${error}`);
|
|
55
|
+
if (this.get('mode') === Mode.QA) {
|
|
56
|
+
throw new Error(`[TraceLog] Custom event "${name}" validation failed: ${error}`);
|
|
62
57
|
}
|
|
63
58
|
return;
|
|
64
59
|
}
|
|
@@ -87,8 +82,8 @@ export class App extends StateManager {
|
|
|
87
82
|
try {
|
|
88
83
|
await handler.stopTracking();
|
|
89
84
|
}
|
|
90
|
-
catch {
|
|
91
|
-
|
|
85
|
+
catch (error) {
|
|
86
|
+
log('warn', 'Failed to stop tracking', { error });
|
|
92
87
|
}
|
|
93
88
|
});
|
|
94
89
|
await Promise.allSettled(handlerCleanups);
|
|
@@ -105,22 +100,25 @@ export class App extends StateManager {
|
|
|
105
100
|
this.isInitialized = false;
|
|
106
101
|
this.handlers = {};
|
|
107
102
|
}
|
|
108
|
-
|
|
109
|
-
const apiUrl = getApiUrlForProject(appConfig.id, appConfig.allowHttp);
|
|
110
|
-
this.set('apiUrl', apiUrl);
|
|
111
|
-
const configManager = new ConfigManager();
|
|
112
|
-
const config = await configManager.get(apiUrl, appConfig);
|
|
103
|
+
setupState(config) {
|
|
113
104
|
this.set('config', config);
|
|
114
|
-
const userId = UserManager.getId(this.managers.storage
|
|
105
|
+
const userId = UserManager.getId(this.managers.storage);
|
|
115
106
|
this.set('userId', userId);
|
|
116
|
-
|
|
107
|
+
const apiUrl = getApiUrl(config);
|
|
108
|
+
this.set('apiUrl', apiUrl);
|
|
109
|
+
const device = getDeviceType();
|
|
110
|
+
this.set('device', device);
|
|
117
111
|
const pageUrl = normalizeUrl(window.location.href, config.sensitiveQueryParams);
|
|
118
112
|
this.set('pageUrl', pageUrl);
|
|
113
|
+
const mode = detectQaMode() ? Mode.QA : undefined;
|
|
114
|
+
if (mode) {
|
|
115
|
+
this.set('mode', mode);
|
|
116
|
+
}
|
|
119
117
|
}
|
|
120
118
|
async setupIntegrations() {
|
|
121
119
|
const config = this.get('config');
|
|
122
120
|
const measurementId = config.integrations?.googleAnalytics?.measurementId;
|
|
123
|
-
if (
|
|
121
|
+
if (measurementId?.trim()) {
|
|
124
122
|
try {
|
|
125
123
|
this.integrations.googleAnalytics = new GoogleAnalyticsIntegration();
|
|
126
124
|
await this.integrations.googleAnalytics.initialize();
|
|
@@ -130,13 +128,9 @@ export class App extends StateManager {
|
|
|
130
128
|
}
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
|
-
initializeHandlers() {
|
|
131
|
+
async initializeHandlers() {
|
|
134
132
|
this.handlers.session = new SessionHandler(this.managers.storage, this.managers.event);
|
|
135
|
-
this.handlers.session.startTracking()
|
|
136
|
-
debugLog.error('App', 'Session handler failed to start', {
|
|
137
|
-
message: error instanceof Error ? error.message : 'Unknown error',
|
|
138
|
-
});
|
|
139
|
-
});
|
|
133
|
+
await this.handlers.session.startTracking();
|
|
140
134
|
const onPageView = () => {
|
|
141
135
|
this.set('suppressNextScroll', true);
|
|
142
136
|
if (this.suppressNextScrollTimer) {
|
|
@@ -153,8 +147,8 @@ export class App extends StateManager {
|
|
|
153
147
|
this.handlers.scroll = new ScrollHandler(this.managers.event);
|
|
154
148
|
this.handlers.scroll.startTracking();
|
|
155
149
|
this.handlers.performance = new PerformanceHandler(this.managers.event);
|
|
156
|
-
this.handlers.performance.startTracking().catch(() => {
|
|
157
|
-
|
|
150
|
+
this.handlers.performance.startTracking().catch((error) => {
|
|
151
|
+
log('warn', 'Failed to start performance tracking', { error });
|
|
158
152
|
});
|
|
159
153
|
this.handlers.error = new ErrorHandler(this.managers.event);
|
|
160
154
|
this.handlers.error.startTracking();
|
|
@@ -29,6 +29,7 @@ export declare const MAX_CUSTOM_EVENT_NAME_LENGTH = 120;
|
|
|
29
29
|
export declare const MAX_CUSTOM_EVENT_STRING_SIZE: number;
|
|
30
30
|
export declare const MAX_CUSTOM_EVENT_KEYS = 10;
|
|
31
31
|
export declare const MAX_CUSTOM_EVENT_ARRAY_SIZE = 10;
|
|
32
|
+
export declare const MAX_NESTED_OBJECT_KEYS = 20;
|
|
32
33
|
export declare const MAX_TEXT_LENGTH = 255;
|
|
33
34
|
export declare const MAX_STRING_LENGTH = 1000;
|
|
34
35
|
export declare const MAX_ARRAY_LENGTH = 100;
|
|
@@ -38,7 +39,7 @@ export declare const SYNC_XHR_TIMEOUT_MS = 2000;
|
|
|
38
39
|
export declare const MAX_FINGERPRINTS = 1000;
|
|
39
40
|
export declare const FINGERPRINT_CLEANUP_MULTIPLIER = 10;
|
|
40
41
|
export declare const MAX_FINGERPRINTS_HARD_LIMIT = 2000;
|
|
41
|
-
export declare const HTML_DATA_ATTR_PREFIX = "data-
|
|
42
|
+
export declare const HTML_DATA_ATTR_PREFIX = "data-tlog";
|
|
42
43
|
export declare const INTERACTIVE_SELECTORS: readonly ["button", "a", "input[type=\"button\"]", "input[type=\"submit\"]", "input[type=\"reset\"]", "input[type=\"checkbox\"]", "input[type=\"radio\"]", "select", "textarea", "[role=\"button\"]", "[role=\"link\"]", "[role=\"tab\"]", "[role=\"menuitem\"]", "[role=\"option\"]", "[role=\"checkbox\"]", "[role=\"radio\"]", "[role=\"switch\"]", "[routerLink]", "[ng-click]", "[data-action]", "[data-click]", "[data-navigate]", "[data-toggle]", "[onclick]", ".btn", ".button", ".clickable", ".nav-link", ".menu-item", "[data-testid]", "[tabindex=\"0\"]"];
|
|
43
44
|
export declare const UTM_PARAMS: string[];
|
|
44
45
|
export declare const INITIALIZATION_MAX_CONCURRENT_RETRIES = 20;
|
|
@@ -60,13 +61,14 @@ export declare const RETRY_BACKOFF_INITIAL = 1000;
|
|
|
60
61
|
export declare const RETRY_BACKOFF_MAX = 30000;
|
|
61
62
|
export declare const RATE_LIMIT_INTERVAL = 1000;
|
|
62
63
|
export declare const MAX_RETRY_ATTEMPTS = 10;
|
|
63
|
-
export declare const ALLOWED_API_CONFIG_KEYS: Set<"mode" | "samplingRate" | "excludedUrlPaths" | keyof import("../types").ExclusiveApiConfig>;
|
|
64
64
|
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
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
|
+
readonly INVALID_TRACELOG_PROJECT_ID: "TraceLog project ID is required when integration is enabled";
|
|
71
|
+
readonly INVALID_CUSTOM_API_URL: "Custom API URL is required when integration is enabled";
|
|
70
72
|
readonly INVALID_GOOGLE_ANALYTICS_ID: "Google Analytics measurement ID is required when integration is enabled";
|
|
71
73
|
readonly INVALID_SCROLL_CONTAINER_SELECTORS: "Scroll container selectors must be valid CSS selectors";
|
|
72
74
|
readonly INVALID_GLOBAL_METADATA: "Global metadata must be an object";
|
|
@@ -42,6 +42,7 @@ export const MAX_CUSTOM_EVENT_NAME_LENGTH = 120;
|
|
|
42
42
|
export const MAX_CUSTOM_EVENT_STRING_SIZE = 8 * 1024; // 8KB
|
|
43
43
|
export const MAX_CUSTOM_EVENT_KEYS = 10;
|
|
44
44
|
export const MAX_CUSTOM_EVENT_ARRAY_SIZE = 10;
|
|
45
|
+
export const MAX_NESTED_OBJECT_KEYS = 20; // Maximum keys in nested objects within arrays
|
|
45
46
|
// Text content limits
|
|
46
47
|
export const MAX_TEXT_LENGTH = 255; // For click tracking text content
|
|
47
48
|
// Data sanitization limits
|
|
@@ -59,7 +60,7 @@ export const MAX_FINGERPRINTS_HARD_LIMIT = 2000; // Hard limit for aggressive cl
|
|
|
59
60
|
// ============================================================================
|
|
60
61
|
// BROWSER & HTML
|
|
61
62
|
// ============================================================================
|
|
62
|
-
export const HTML_DATA_ATTR_PREFIX = 'data-
|
|
63
|
+
export const HTML_DATA_ATTR_PREFIX = 'data-tlog';
|
|
63
64
|
// Interactive element selectors for click tracking
|
|
64
65
|
export const INTERACTIVE_SELECTORS = [
|
|
65
66
|
'button',
|
|
@@ -133,31 +134,18 @@ export const MAX_RETRY_ATTEMPTS = 10;
|
|
|
133
134
|
// ============================================================================
|
|
134
135
|
// VALIDATION
|
|
135
136
|
// ============================================================================
|
|
136
|
-
// Allowed API config keys for runtime validation
|
|
137
|
-
export const ALLOWED_API_CONFIG_KEYS = new Set([
|
|
138
|
-
'mode',
|
|
139
|
-
'tags',
|
|
140
|
-
'samplingRate',
|
|
141
|
-
'excludedUrlPaths',
|
|
142
|
-
'ipExcluded',
|
|
143
|
-
]);
|
|
144
137
|
// Validation error messages - standardized across all layers
|
|
145
138
|
export const VALIDATION_MESSAGES = {
|
|
146
|
-
// Project ID validation - consistent message across all layers
|
|
147
139
|
MISSING_PROJECT_ID: 'Project ID is required',
|
|
148
140
|
PROJECT_ID_EMPTY_AFTER_TRIM: 'Project ID is required',
|
|
149
|
-
// Session timeout validation
|
|
150
141
|
INVALID_SESSION_TIMEOUT: `Session timeout must be between ${MIN_SESSION_TIMEOUT_MS}ms (30 seconds) and ${MAX_SESSION_TIMEOUT_MS}ms (24 hours)`,
|
|
151
|
-
// Sampling rate validation
|
|
152
142
|
INVALID_SAMPLING_RATE: 'Sampling rate must be between 0 and 1',
|
|
153
143
|
INVALID_ERROR_SAMPLING_RATE: 'Error sampling must be between 0 and 1',
|
|
154
|
-
|
|
144
|
+
INVALID_TRACELOG_PROJECT_ID: 'TraceLog project ID is required when integration is enabled',
|
|
145
|
+
INVALID_CUSTOM_API_URL: 'Custom API URL is required when integration is enabled',
|
|
155
146
|
INVALID_GOOGLE_ANALYTICS_ID: 'Google Analytics measurement ID is required when integration is enabled',
|
|
156
|
-
// UI validation
|
|
157
147
|
INVALID_SCROLL_CONTAINER_SELECTORS: 'Scroll container selectors must be valid CSS selectors',
|
|
158
|
-
// Global metadata validation
|
|
159
148
|
INVALID_GLOBAL_METADATA: 'Global metadata must be an object',
|
|
160
|
-
// Array validation
|
|
161
149
|
INVALID_SENSITIVE_QUERY_PARAMS: 'Sensitive query params must be an array of strings',
|
|
162
150
|
};
|
|
163
151
|
// ============================================================================
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export declare const STORAGE_BASE_KEY = "
|
|
2
|
-
export declare const
|
|
1
|
+
export declare const STORAGE_BASE_KEY = "tlog";
|
|
2
|
+
export declare const QA_MODE_KEY = "tlog:qa_mode";
|
|
3
|
+
export declare const USER_ID_KEY = "tlog:uid";
|
|
3
4
|
export declare const QUEUE_KEY: (id: string) => string;
|
|
4
5
|
export declare const SESSION_STORAGE_KEY: (id: string) => string;
|
|
5
6
|
export declare const CROSS_TAB_SESSION_KEY: (id: string) => string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export const STORAGE_BASE_KEY = '
|
|
2
|
-
export const
|
|
1
|
+
export const STORAGE_BASE_KEY = 'tlog';
|
|
2
|
+
export const QA_MODE_KEY = `${STORAGE_BASE_KEY}:qa_mode`;
|
|
3
|
+
export const USER_ID_KEY = `${STORAGE_BASE_KEY}:uid`;
|
|
3
4
|
export const QUEUE_KEY = (id) => (id ? `${STORAGE_BASE_KEY}:${id}:queue` : `${STORAGE_BASE_KEY}:queue`);
|
|
4
5
|
export const SESSION_STORAGE_KEY = (id) => id ? `${STORAGE_BASE_KEY}:${id}:session` : `${STORAGE_BASE_KEY}:session`;
|
|
5
6
|
// Cross-tab session management storage keys
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HTML_DATA_ATTR_PREFIX, MAX_TEXT_LENGTH, INTERACTIVE_SELECTORS } from '../constants';
|
|
2
2
|
import { EventType } from '../types';
|
|
3
3
|
import { StateManager } from '../managers/state.manager';
|
|
4
|
-
import {
|
|
4
|
+
import { log } from '@/utils';
|
|
5
5
|
export class ClickHandler extends StateManager {
|
|
6
6
|
constructor(eventManager) {
|
|
7
7
|
super();
|
|
@@ -20,7 +20,7 @@ export class ClickHandler extends StateManager {
|
|
|
20
20
|
? target.parentElement
|
|
21
21
|
: null;
|
|
22
22
|
if (!clickedElement) {
|
|
23
|
-
|
|
23
|
+
log('warn', 'Click target not found or not an element');
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
const trackingElement = this.findTrackingElement(clickedElement);
|
|
@@ -72,10 +72,7 @@ export class ClickHandler extends StateManager {
|
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
catch (error) {
|
|
75
|
-
|
|
76
|
-
selector,
|
|
77
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
78
|
-
});
|
|
75
|
+
log('warn', 'Invalid selector in element search', { error, data: { selector } });
|
|
79
76
|
continue;
|
|
80
77
|
}
|
|
81
78
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { StateManager } from '../managers/state.manager';
|
|
2
2
|
import { ErrorType, EventType } from '../types';
|
|
3
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
|
-
import { debugLog } from '../utils/logging';
|
|
5
4
|
/**
|
|
6
5
|
* Simplified error handler for tracking JavaScript errors and unhandled promise rejections
|
|
7
6
|
* Includes PII sanitization and sampling support
|
|
@@ -18,11 +17,6 @@ export class ErrorHandler extends StateManager {
|
|
|
18
17
|
if (this.shouldSuppressError(ErrorType.JS_ERROR, sanitizedMessage)) {
|
|
19
18
|
return;
|
|
20
19
|
}
|
|
21
|
-
debugLog.warn('ErrorHandler', 'JS error captured', {
|
|
22
|
-
message: sanitizedMessage,
|
|
23
|
-
filename: event.filename,
|
|
24
|
-
line: event.lineno,
|
|
25
|
-
});
|
|
26
20
|
this.eventManager.track({
|
|
27
21
|
type: EventType.ERROR,
|
|
28
22
|
error_data: {
|
|
@@ -43,7 +37,6 @@ export class ErrorHandler extends StateManager {
|
|
|
43
37
|
if (this.shouldSuppressError(ErrorType.PROMISE_REJECTION, sanitizedMessage)) {
|
|
44
38
|
return;
|
|
45
39
|
}
|
|
46
|
-
debugLog.warn('ErrorHandler', 'Promise rejection captured', { message: sanitizedMessage });
|
|
47
40
|
this.eventManager.track({
|
|
48
41
|
type: EventType.ERROR,
|
|
49
42
|
error_data: {
|
|
@@ -107,10 +100,7 @@ export class ErrorHandler extends StateManager {
|
|
|
107
100
|
}
|
|
108
101
|
this.recentErrors.set(key, now);
|
|
109
102
|
if (this.recentErrors.size > MAX_TRACKED_ERRORS_HARD_LIMIT) {
|
|
110
|
-
|
|
111
|
-
size: this.recentErrors.size,
|
|
112
|
-
limit: MAX_TRACKED_ERRORS_HARD_LIMIT,
|
|
113
|
-
});
|
|
103
|
+
// Hard limit exceeded, clearing all tracked errors
|
|
114
104
|
this.recentErrors.clear();
|
|
115
105
|
this.recentErrors.set(key, now);
|
|
116
106
|
return false;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EventType } from '../types';
|
|
2
2
|
import { normalizeUrl } from '../utils';
|
|
3
3
|
import { StateManager } from '../managers/state.manager';
|
|
4
|
-
import { debugLog } from '../utils/logging';
|
|
5
4
|
export class PageViewHandler extends StateManager {
|
|
6
5
|
constructor(eventManager, onTrack) {
|
|
7
6
|
super();
|
|
@@ -13,7 +12,6 @@ export class PageViewHandler extends StateManager {
|
|
|
13
12
|
}
|
|
14
13
|
this.onTrack();
|
|
15
14
|
const fromUrl = this.get('pageUrl');
|
|
16
|
-
debugLog.debug('PageViewHandler', 'Page navigation detected', { from: fromUrl, to: normalizedUrl });
|
|
17
15
|
this.set('pageUrl', normalizedUrl);
|
|
18
16
|
const pageViewData = this.extractPageViewData();
|
|
19
17
|
this.eventManager.track({
|
|
@@ -27,7 +25,6 @@ export class PageViewHandler extends StateManager {
|
|
|
27
25
|
this.onTrack = onTrack;
|
|
28
26
|
}
|
|
29
27
|
startTracking() {
|
|
30
|
-
debugLog.debug('PageViewHandler', 'Starting page view tracking');
|
|
31
28
|
this.trackInitialPageView();
|
|
32
29
|
window.addEventListener('popstate', this.trackCurrentPage, true);
|
|
33
30
|
window.addEventListener('hashchange', this.trackCurrentPage, true);
|
|
@@ -35,7 +32,6 @@ export class PageViewHandler extends StateManager {
|
|
|
35
32
|
this.patchHistory('replaceState');
|
|
36
33
|
}
|
|
37
34
|
stopTracking() {
|
|
38
|
-
debugLog.debug('PageViewHandler', 'Stopping page view tracking');
|
|
39
35
|
window.removeEventListener('popstate', this.trackCurrentPage, true);
|
|
40
36
|
window.removeEventListener('hashchange', this.trackCurrentPage, true);
|
|
41
37
|
if (this.originalPushState) {
|