@tracelog/lib 0.5.5 → 0.6.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.
Files changed (202) hide show
  1. package/README.md +157 -180
  2. package/dist/browser/tracelog.esm.js +1124 -1377
  3. package/dist/browser/tracelog.esm.js.map +1 -0
  4. package/dist/browser/tracelog.js +2 -2
  5. package/dist/browser/tracelog.js.map +1 -0
  6. package/dist/cjs/api.d.ts +12 -2
  7. package/dist/cjs/api.js +74 -29
  8. package/dist/cjs/app.d.ts +2 -2
  9. package/dist/cjs/app.js +26 -32
  10. package/dist/cjs/constants/config.constants.d.ts +7 -2
  11. package/dist/cjs/constants/config.constants.js +9 -18
  12. package/dist/cjs/constants/index.d.ts +0 -1
  13. package/dist/cjs/constants/index.js +0 -1
  14. package/dist/cjs/constants/storage.constants.d.ts +3 -2
  15. package/dist/cjs/constants/storage.constants.js +4 -4
  16. package/dist/cjs/handlers/click.handler.js +3 -6
  17. package/dist/cjs/handlers/error.handler.js +1 -11
  18. package/dist/cjs/handlers/page-view.handler.js +0 -4
  19. package/dist/cjs/handlers/performance.handler.js +14 -29
  20. package/dist/cjs/handlers/scroll.handler.js +7 -6
  21. package/dist/cjs/handlers/session.handler.js +7 -6
  22. package/dist/cjs/integrations/google-analytics.integration.js +2 -6
  23. package/dist/cjs/listeners/activity-listener-manager.js +3 -3
  24. package/dist/cjs/listeners/input-listener-managers.js +3 -3
  25. package/dist/cjs/listeners/touch-listener-manager.js +3 -3
  26. package/dist/cjs/listeners/unload-listener-manager.js +3 -3
  27. package/dist/cjs/listeners/visibility-listener-manager.js +3 -3
  28. package/dist/cjs/managers/event.manager.d.ts +5 -1
  29. package/dist/cjs/managers/event.manager.js +103 -40
  30. package/dist/cjs/managers/sender.manager.js +29 -36
  31. package/dist/cjs/managers/session.manager.js +5 -13
  32. package/dist/cjs/managers/state.manager.d.ts +0 -3
  33. package/dist/cjs/managers/state.manager.js +1 -43
  34. package/dist/cjs/managers/storage.manager.d.ts +21 -2
  35. package/dist/cjs/managers/storage.manager.js +164 -21
  36. package/dist/cjs/managers/user.manager.d.ts +1 -1
  37. package/dist/cjs/managers/user.manager.js +2 -2
  38. package/dist/cjs/public-api.d.ts +3 -3
  39. package/dist/cjs/public-api.js +1 -1
  40. package/dist/cjs/test-bridge.d.ts +1 -0
  41. package/dist/cjs/test-bridge.js +37 -2
  42. package/dist/cjs/types/config.types.d.ts +17 -20
  43. package/dist/cjs/types/config.types.js +6 -0
  44. package/dist/cjs/types/event.types.d.ts +1 -13
  45. package/dist/cjs/types/index.d.ts +0 -2
  46. package/dist/cjs/types/index.js +0 -2
  47. package/dist/cjs/types/mode.types.d.ts +1 -2
  48. package/dist/cjs/types/mode.types.js +0 -1
  49. package/dist/cjs/types/queue.types.d.ts +0 -6
  50. package/dist/cjs/types/state.types.d.ts +2 -0
  51. package/dist/cjs/types/test-bridge.types.d.ts +2 -2
  52. package/dist/cjs/types/validation-error.types.d.ts +0 -6
  53. package/dist/cjs/types/validation-error.types.js +1 -10
  54. package/dist/cjs/utils/browser/device-detector.utils.js +2 -24
  55. package/dist/cjs/utils/browser/index.d.ts +1 -0
  56. package/dist/cjs/utils/browser/index.js +1 -0
  57. package/dist/cjs/utils/browser/qa-mode.utils.d.ts +13 -0
  58. package/dist/cjs/utils/browser/qa-mode.utils.js +43 -0
  59. package/dist/cjs/utils/browser/utm-params.utils.js +0 -15
  60. package/dist/cjs/utils/data/uuid.utils.d.ts +13 -0
  61. package/dist/cjs/utils/data/uuid.utils.js +37 -1
  62. package/dist/cjs/utils/index.d.ts +1 -1
  63. package/dist/cjs/utils/index.js +1 -1
  64. package/dist/cjs/utils/logging.utils.d.ts +21 -0
  65. package/dist/cjs/utils/logging.utils.js +86 -0
  66. package/dist/cjs/utils/network/index.d.ts +0 -1
  67. package/dist/cjs/utils/network/index.js +0 -1
  68. package/dist/cjs/utils/network/url.utils.d.ts +2 -8
  69. package/dist/cjs/utils/network/url.utils.js +45 -90
  70. package/dist/cjs/utils/security/sanitize.utils.d.ts +1 -13
  71. package/dist/cjs/utils/security/sanitize.utils.js +15 -178
  72. package/dist/cjs/utils/validations/config-validations.utils.d.ts +3 -9
  73. package/dist/cjs/utils/validations/config-validations.utils.js +56 -93
  74. package/dist/cjs/utils/validations/event-validations.utils.js +11 -5
  75. package/dist/cjs/utils/validations/index.d.ts +0 -1
  76. package/dist/cjs/utils/validations/index.js +0 -1
  77. package/dist/cjs/utils/validations/metadata-validations.utils.js +0 -1
  78. package/dist/cjs/utils/validations/type-guards.utils.d.ts +2 -2
  79. package/dist/cjs/utils/validations/type-guards.utils.js +50 -4
  80. package/dist/esm/api.d.ts +12 -2
  81. package/dist/esm/api.js +73 -29
  82. package/dist/esm/app.d.ts +2 -2
  83. package/dist/esm/app.js +28 -34
  84. package/dist/esm/constants/config.constants.d.ts +7 -2
  85. package/dist/esm/constants/config.constants.js +7 -16
  86. package/dist/esm/constants/index.d.ts +0 -1
  87. package/dist/esm/constants/index.js +0 -1
  88. package/dist/esm/constants/storage.constants.d.ts +3 -2
  89. package/dist/esm/constants/storage.constants.js +3 -2
  90. package/dist/esm/handlers/click.handler.js +3 -6
  91. package/dist/esm/handlers/error.handler.js +1 -11
  92. package/dist/esm/handlers/page-view.handler.js +0 -4
  93. package/dist/esm/handlers/performance.handler.js +14 -29
  94. package/dist/esm/handlers/scroll.handler.js +7 -6
  95. package/dist/esm/handlers/session.handler.js +7 -6
  96. package/dist/esm/integrations/google-analytics.integration.js +3 -7
  97. package/dist/esm/listeners/activity-listener-manager.js +3 -3
  98. package/dist/esm/listeners/input-listener-managers.js +3 -3
  99. package/dist/esm/listeners/touch-listener-manager.js +3 -3
  100. package/dist/esm/listeners/unload-listener-manager.js +3 -3
  101. package/dist/esm/listeners/visibility-listener-manager.js +3 -3
  102. package/dist/esm/managers/event.manager.d.ts +5 -1
  103. package/dist/esm/managers/event.manager.js +106 -43
  104. package/dist/esm/managers/sender.manager.js +31 -38
  105. package/dist/esm/managers/session.manager.js +5 -13
  106. package/dist/esm/managers/state.manager.d.ts +0 -3
  107. package/dist/esm/managers/state.manager.js +1 -43
  108. package/dist/esm/managers/storage.manager.d.ts +21 -2
  109. package/dist/esm/managers/storage.manager.js +164 -21
  110. package/dist/esm/managers/user.manager.d.ts +1 -1
  111. package/dist/esm/managers/user.manager.js +2 -2
  112. package/dist/esm/public-api.d.ts +3 -3
  113. package/dist/esm/public-api.js +1 -1
  114. package/dist/esm/test-bridge.d.ts +1 -0
  115. package/dist/esm/test-bridge.js +37 -2
  116. package/dist/esm/types/config.types.d.ts +17 -20
  117. package/dist/esm/types/config.types.js +5 -1
  118. package/dist/esm/types/event.types.d.ts +1 -13
  119. package/dist/esm/types/index.d.ts +0 -2
  120. package/dist/esm/types/index.js +0 -2
  121. package/dist/esm/types/mode.types.d.ts +1 -2
  122. package/dist/esm/types/mode.types.js +0 -1
  123. package/dist/esm/types/queue.types.d.ts +0 -6
  124. package/dist/esm/types/state.types.d.ts +2 -0
  125. package/dist/esm/types/test-bridge.types.d.ts +2 -2
  126. package/dist/esm/types/validation-error.types.d.ts +0 -6
  127. package/dist/esm/types/validation-error.types.js +0 -8
  128. package/dist/esm/utils/browser/device-detector.utils.js +2 -24
  129. package/dist/esm/utils/browser/index.d.ts +1 -0
  130. package/dist/esm/utils/browser/index.js +1 -0
  131. package/dist/esm/utils/browser/qa-mode.utils.d.ts +13 -0
  132. package/dist/esm/utils/browser/qa-mode.utils.js +39 -0
  133. package/dist/esm/utils/browser/utm-params.utils.js +0 -15
  134. package/dist/esm/utils/data/uuid.utils.d.ts +13 -0
  135. package/dist/esm/utils/data/uuid.utils.js +35 -0
  136. package/dist/esm/utils/index.d.ts +1 -1
  137. package/dist/esm/utils/index.js +1 -1
  138. package/dist/esm/utils/logging.utils.d.ts +21 -0
  139. package/dist/esm/utils/logging.utils.js +81 -0
  140. package/dist/esm/utils/network/index.d.ts +0 -1
  141. package/dist/esm/utils/network/index.js +0 -1
  142. package/dist/esm/utils/network/url.utils.d.ts +2 -8
  143. package/dist/esm/utils/network/url.utils.js +44 -88
  144. package/dist/esm/utils/security/sanitize.utils.d.ts +1 -13
  145. package/dist/esm/utils/security/sanitize.utils.js +15 -176
  146. package/dist/esm/utils/validations/config-validations.utils.d.ts +3 -9
  147. package/dist/esm/utils/validations/config-validations.utils.js +57 -93
  148. package/dist/esm/utils/validations/event-validations.utils.js +11 -5
  149. package/dist/esm/utils/validations/index.d.ts +0 -1
  150. package/dist/esm/utils/validations/index.js +0 -1
  151. package/dist/esm/utils/validations/metadata-validations.utils.js +0 -1
  152. package/dist/esm/utils/validations/type-guards.utils.d.ts +2 -2
  153. package/dist/esm/utils/validations/type-guards.utils.js +50 -4
  154. package/package.json +3 -2
  155. package/dist/cjs/app.types.d.ts +0 -2
  156. package/dist/cjs/app.types.js +0 -12
  157. package/dist/cjs/constants/api.constants.d.ts +0 -6
  158. package/dist/cjs/constants/api.constants.js +0 -14
  159. package/dist/cjs/managers/api.manager.d.ts +0 -13
  160. package/dist/cjs/managers/api.manager.js +0 -44
  161. package/dist/cjs/managers/config.builder.d.ts +0 -33
  162. package/dist/cjs/managers/config.builder.js +0 -116
  163. package/dist/cjs/managers/config.manager.d.ts +0 -56
  164. package/dist/cjs/managers/config.manager.js +0 -157
  165. package/dist/cjs/managers/tags.manager.d.ts +0 -36
  166. package/dist/cjs/managers/tags.manager.js +0 -171
  167. package/dist/cjs/types/api.types.d.ts +0 -52
  168. package/dist/cjs/types/api.types.js +0 -56
  169. package/dist/cjs/types/tag.types.d.ts +0 -43
  170. package/dist/cjs/types/tag.types.js +0 -31
  171. package/dist/cjs/utils/logging/debug-logger.utils.d.ts +0 -14
  172. package/dist/cjs/utils/logging/debug-logger.utils.js +0 -47
  173. package/dist/cjs/utils/logging/index.d.ts +0 -1
  174. package/dist/cjs/utils/logging/index.js +0 -5
  175. package/dist/cjs/utils/network/fetch-with-timeout.utils.d.ts +0 -4
  176. package/dist/cjs/utils/network/fetch-with-timeout.utils.js +0 -25
  177. package/dist/cjs/utils/validations/url-validations.utils.d.ts +0 -15
  178. package/dist/cjs/utils/validations/url-validations.utils.js +0 -47
  179. package/dist/esm/app.types.d.ts +0 -2
  180. package/dist/esm/app.types.js +0 -1
  181. package/dist/esm/constants/api.constants.d.ts +0 -6
  182. package/dist/esm/constants/api.constants.js +0 -11
  183. package/dist/esm/managers/api.manager.d.ts +0 -13
  184. package/dist/esm/managers/api.manager.js +0 -41
  185. package/dist/esm/managers/config.builder.d.ts +0 -33
  186. package/dist/esm/managers/config.builder.js +0 -112
  187. package/dist/esm/managers/config.manager.d.ts +0 -56
  188. package/dist/esm/managers/config.manager.js +0 -153
  189. package/dist/esm/managers/tags.manager.d.ts +0 -36
  190. package/dist/esm/managers/tags.manager.js +0 -167
  191. package/dist/esm/types/api.types.d.ts +0 -52
  192. package/dist/esm/types/api.types.js +0 -53
  193. package/dist/esm/types/tag.types.d.ts +0 -43
  194. package/dist/esm/types/tag.types.js +0 -28
  195. package/dist/esm/utils/logging/debug-logger.utils.d.ts +0 -14
  196. package/dist/esm/utils/logging/debug-logger.utils.js +0 -44
  197. package/dist/esm/utils/logging/index.d.ts +0 -1
  198. package/dist/esm/utils/logging/index.js +0 -1
  199. package/dist/esm/utils/network/fetch-with-timeout.utils.d.ts +0 -4
  200. package/dist/esm/utils/network/fetch-with-timeout.utils.js +0 -22
  201. package/dist/esm/utils/validations/url-validations.utils.d.ts +0 -15
  202. package/dist/esm/utils/validations/url-validations.utils.js +0 -42
@@ -1,112 +0,0 @@
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
- }
@@ -1,56 +0,0 @@
1
- import { AppConfig, Config } from '../types';
2
- /**
3
- * Configuration manager responsible for loading and merging application configuration.
4
- *
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.
10
- *
11
- * Supports special project IDs for development and testing:
12
- * - 'skip': Bypasses all network calls, uses defaults
13
- * - 'localhost:8080': Loads config from local development server
14
- */
15
- export declare class ConfigManager {
16
- private static readonly PRODUCTION_DOMAINS;
17
- /**
18
- * Gets complete configuration by loading API config and building final config.
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
- */
24
- get(apiUrl: string, appConfig: AppConfig): Promise<Config>;
25
- /**
26
- * Loads configuration from API and returns sanitized API config.
27
- * Only returns values explicitly provided by the API.
28
- */
29
- private loadFromApi;
30
- /**
31
- * Builds the configuration URL based on project type and QA mode.
32
- */
33
- private buildConfigUrl;
34
- /**
35
- * Builds request headers based on project configuration.
36
- * Always includes X-TraceLog-Project header for consistent identification.
37
- */
38
- private buildHeaders;
39
- /**
40
- * Parses and validates JSON response from config API.
41
- */
42
- private parseJsonResponse;
43
- /**
44
- * Checks if QA mode is enabled via URL parameter.
45
- */
46
- private isQaModeEnabled;
47
- /**
48
- * Applies QA mode to API config if enabled via URL parameter.
49
- */
50
- private applyQaModeIfEnabled;
51
- /**
52
- * Creates default configuration for skip mode and fallback scenarios.
53
- * Only uses API defaults for fields not provided by the app config.
54
- */
55
- private createDefaultConfig;
56
- }
@@ -1,153 +0,0 @@
1
- import { DEFAULT_API_CONFIG, REQUEST_TIMEOUT_MS } from '../constants';
2
- import { Mode, SpecialProjectId } from '../types';
3
- import { sanitizeApiConfig, fetchWithTimeout } from '../utils';
4
- import { debugLog } from '../utils/logging';
5
- import { ConfigBuilder } from './config.builder';
6
- /**
7
- * Configuration manager responsible for loading and merging application configuration.
8
- *
9
- * Handles configuration from two sources:
10
- * 1. API configuration (server-side settings)
11
- * 2. App configuration (client initialization settings)
12
- *
13
- * Uses ConfigBuilder for centralized merge logic.
14
- *
15
- * Supports special project IDs for development and testing:
16
- * - 'skip': Bypasses all network calls, uses defaults
17
- * - 'localhost:8080': Loads config from local development server
18
- */
19
- export class ConfigManager {
20
- /**
21
- * Gets complete configuration by loading API config and building final config.
22
- *
23
- * @param apiUrl - Base URL for the configuration API
24
- * @param appConfig - Client-side configuration from init()
25
- * @returns Promise<Config> - Merged configuration object
26
- */
27
- async get(apiUrl, appConfig) {
28
- // Handle skip mode - no network calls for config
29
- // Support 'skip' or any ID starting with 'skip-' (e.g., 'skip-1', 'skip-2')
30
- // Also handle 'fail' mode (SpecialProjectId.Fail) - skip config but fail event sends
31
- if (appConfig.id === SpecialProjectId.Skip ||
32
- appConfig.id === SpecialProjectId.Fail ||
33
- appConfig.id.toLowerCase().startsWith('skip-')) {
34
- return this.createDefaultConfig(appConfig);
35
- }
36
- const apiConfig = await this.loadFromApi(apiUrl, appConfig);
37
- // Apply QA mode from URL parameter if set
38
- const finalApiConfig = this.applyQaModeIfEnabled(apiConfig);
39
- const config = ConfigBuilder.build(appConfig, finalApiConfig);
40
- debugLog.info('ConfigManager', 'Configuration loaded', {
41
- projectId: config.id,
42
- mode: config.mode,
43
- hasTags: !!config.tags?.length,
44
- hasExclusions: !!config.excludedUrlPaths?.length,
45
- });
46
- return config;
47
- }
48
- /**
49
- * Loads configuration from API and returns sanitized API config.
50
- * Only returns values explicitly provided by the API.
51
- */
52
- async loadFromApi(apiUrl, appConfig) {
53
- try {
54
- const configUrl = this.buildConfigUrl(apiUrl, appConfig);
55
- const headers = this.buildHeaders(appConfig);
56
- const response = await fetchWithTimeout(configUrl, {
57
- method: 'GET',
58
- headers,
59
- timeout: REQUEST_TIMEOUT_MS,
60
- });
61
- if (!response.ok) {
62
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
63
- }
64
- const rawData = await this.parseJsonResponse(response);
65
- const apiConfig = sanitizeApiConfig(rawData);
66
- // Only merge defaults for fields that are arrays (to ensure they're never undefined)
67
- return {
68
- ...apiConfig,
69
- excludedUrlPaths: apiConfig.excludedUrlPaths ?? DEFAULT_API_CONFIG.excludedUrlPaths,
70
- tags: apiConfig.tags ?? DEFAULT_API_CONFIG.tags,
71
- };
72
- }
73
- catch (error) {
74
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
75
- debugLog.error('ConfigManager', 'Failed to load configuration', {
76
- error: errorMessage,
77
- apiUrl,
78
- projectId: appConfig.id,
79
- });
80
- throw new Error(`Configuration load failed: ${errorMessage}`);
81
- }
82
- }
83
- /**
84
- * Builds the configuration URL based on project type and QA mode.
85
- */
86
- buildConfigUrl(apiUrl, appConfig) {
87
- const isLocalhost = appConfig.id === SpecialProjectId.Localhost || appConfig.id === SpecialProjectId.Fail;
88
- if (isLocalhost) {
89
- return `http://${appConfig.id}/config`;
90
- }
91
- const baseUrl = `${apiUrl}/config`;
92
- const isQaMode = this.isQaModeEnabled();
93
- return isQaMode ? `${baseUrl}?qaMode=true` : baseUrl;
94
- }
95
- /**
96
- * Builds request headers based on project configuration.
97
- * Always includes X-TraceLog-Project header for consistent identification.
98
- */
99
- buildHeaders(appConfig) {
100
- return {
101
- 'Content-Type': 'application/json',
102
- 'X-TraceLog-Project': appConfig.id,
103
- };
104
- }
105
- /**
106
- * Parses and validates JSON response from config API.
107
- */
108
- async parseJsonResponse(response) {
109
- const contentType = response.headers.get('content-type');
110
- if (!contentType?.includes('application/json')) {
111
- throw new Error('Invalid response content-type, expected JSON');
112
- }
113
- const rawData = await response.json();
114
- if (!rawData || typeof rawData !== 'object' || Array.isArray(rawData)) {
115
- throw new Error('Invalid response format, expected object');
116
- }
117
- return rawData;
118
- }
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
- * Applies QA mode to API config if enabled via URL parameter.
128
- */
129
- applyQaModeIfEnabled(apiConfig) {
130
- if (this.isQaModeEnabled() && !apiConfig.mode) {
131
- debugLog.info('ConfigManager', 'QA mode enabled via URL parameter');
132
- return { ...apiConfig, mode: Mode.QA };
133
- }
134
- return apiConfig;
135
- }
136
- /**
137
- * Creates default configuration for skip mode and fallback scenarios.
138
- * Only uses API defaults for fields not provided by the app config.
139
- */
140
- createDefaultConfig(appConfig) {
141
- // Only use DEFAULT_API_CONFIG for fields not provided in appConfig
142
- const apiConfig = {
143
- // Only use defaults if app config doesn't provide these values
144
- tags: DEFAULT_API_CONFIG.tags,
145
- ipExcluded: DEFAULT_API_CONFIG.ipExcluded,
146
- ...(appConfig.samplingRate === undefined && { samplingRate: DEFAULT_API_CONFIG.samplingRate }),
147
- // Don't override excludedUrlPaths if provided by app config
148
- // ConfigBuilder will handle the fallback to [] if both are undefined
149
- };
150
- return ConfigBuilder.build(appConfig, apiConfig);
151
- }
152
- }
153
- ConfigManager.PRODUCTION_DOMAINS = [/^https:\/\/.*\.tracelog\.app$/, /^https:\/\/.*\.tracelog\.dev$/];
@@ -1,36 +0,0 @@
1
- import { DeviceType, EventData } from '../types';
2
- import { StateManager } from './state.manager';
3
- export declare class TagsManager extends StateManager {
4
- /**
5
- * Gets matching tag IDs for an event based on configured tag conditions
6
- */
7
- getEventTagsIds(event: EventData, deviceType: DeviceType): string[];
8
- /**
9
- * Evaluates all conditions for a tag using logical operators
10
- */
11
- private evaluateTagConditions;
12
- /**
13
- * Evaluates a single tag condition
14
- */
15
- private evaluateCondition;
16
- /**
17
- * Unified string matching logic for all string-based conditions
18
- */
19
- private matchStringCondition;
20
- /**
21
- * Element-specific matching logic with optimized data extraction
22
- */
23
- private matchElementCondition;
24
- /**
25
- * Exact field matching for element EQUALS operations
26
- */
27
- private matchElementFieldExact;
28
- /**
29
- * Builds searchable element data string with null safety
30
- */
31
- private buildElementDataString;
32
- /**
33
- * Safe regex testing with error handling
34
- */
35
- private testRegex;
36
- }
@@ -1,167 +0,0 @@
1
- import { TagConditionOperator, TagConditionType, TagLogicalOperator, } from '../types';
2
- import { StateManager } from './state.manager';
3
- export class TagsManager extends StateManager {
4
- /**
5
- * Gets matching tag IDs for an event based on configured tag conditions
6
- */
7
- getEventTagsIds(event, deviceType) {
8
- const tags = this.get('config')?.tags?.filter((tag) => tag.triggerType === event.type) ?? [];
9
- if (tags.length === 0) {
10
- return [];
11
- }
12
- const context = {
13
- event,
14
- deviceType,
15
- clickData: event.click_data,
16
- };
17
- return tags.filter((tag) => this.evaluateTagConditions(tag, context)).map((tag) => tag.id);
18
- }
19
- /**
20
- * Evaluates all conditions for a tag using logical operators
21
- */
22
- evaluateTagConditions(tag, context) {
23
- const { conditions, logicalOperator = TagLogicalOperator.OR } = tag;
24
- if (!conditions || conditions.length === 0) {
25
- return false;
26
- }
27
- const results = conditions.map((condition) => this.evaluateCondition(condition, context));
28
- return logicalOperator === TagLogicalOperator.AND ? results.every(Boolean) : results.some(Boolean);
29
- }
30
- /**
31
- * Evaluates a single tag condition
32
- */
33
- evaluateCondition(condition, context) {
34
- try {
35
- switch (condition.type) {
36
- case TagConditionType.URL_MATCHES:
37
- return this.matchStringCondition(condition, context.event.page_url);
38
- case TagConditionType.DEVICE_TYPE:
39
- return this.matchStringCondition(condition, context.deviceType);
40
- case TagConditionType.UTM_SOURCE:
41
- return this.matchStringCondition(condition, context.event.utm?.source ?? '');
42
- case TagConditionType.UTM_MEDIUM:
43
- return this.matchStringCondition(condition, context.event.utm?.medium ?? '');
44
- case TagConditionType.UTM_CAMPAIGN:
45
- return this.matchStringCondition(condition, context.event.utm?.campaign ?? '');
46
- case TagConditionType.ELEMENT_MATCHES:
47
- return context.clickData ? this.matchElementCondition(condition, context.clickData) : false;
48
- default:
49
- return false;
50
- }
51
- }
52
- catch {
53
- return false;
54
- }
55
- }
56
- /**
57
- * Unified string matching logic for all string-based conditions
58
- */
59
- matchStringCondition(condition, value) {
60
- if (!value &&
61
- condition.operator !== TagConditionOperator.EXISTS &&
62
- condition.operator !== TagConditionOperator.NOT_EXISTS) {
63
- return false;
64
- }
65
- const conditionValue = condition.value.toLowerCase();
66
- const targetValue = value.toLowerCase();
67
- switch (condition.operator) {
68
- case TagConditionOperator.EQUALS:
69
- return targetValue === conditionValue;
70
- case TagConditionOperator.CONTAINS:
71
- return targetValue.includes(conditionValue);
72
- case TagConditionOperator.STARTS_WITH:
73
- return targetValue.startsWith(conditionValue);
74
- case TagConditionOperator.ENDS_WITH:
75
- return targetValue.endsWith(conditionValue);
76
- case TagConditionOperator.REGEX:
77
- return this.testRegex(conditionValue, targetValue);
78
- case TagConditionOperator.EXISTS:
79
- return !!value;
80
- case TagConditionOperator.NOT_EXISTS:
81
- return !value;
82
- default:
83
- return false;
84
- }
85
- }
86
- /**
87
- * Element-specific matching logic with optimized data extraction
88
- */
89
- matchElementCondition(condition, clickData) {
90
- if (condition.operator === TagConditionOperator.EQUALS) {
91
- return this.matchElementFieldExact(condition, clickData);
92
- }
93
- // Build searchable element data string once
94
- const elementData = this.buildElementDataString(clickData).toLowerCase();
95
- const conditionValue = condition.value.toLowerCase();
96
- switch (condition.operator) {
97
- case TagConditionOperator.CONTAINS:
98
- return elementData.includes(conditionValue);
99
- case TagConditionOperator.STARTS_WITH:
100
- return elementData.startsWith(conditionValue);
101
- case TagConditionOperator.ENDS_WITH:
102
- return elementData.endsWith(conditionValue);
103
- case TagConditionOperator.REGEX:
104
- return this.testRegex(conditionValue, elementData);
105
- default:
106
- return false;
107
- }
108
- }
109
- /**
110
- * Exact field matching for element EQUALS operations
111
- */
112
- matchElementFieldExact(condition, clickData) {
113
- const conditionValue = condition.value.toLowerCase();
114
- const fields = [
115
- clickData.id,
116
- clickData.class,
117
- clickData.tag,
118
- clickData.text,
119
- clickData.href,
120
- clickData.title,
121
- clickData.alt,
122
- clickData.role,
123
- clickData.ariaLabel,
124
- ];
125
- // Check standard fields
126
- if (fields.some((field) => field && field.toLowerCase() === conditionValue)) {
127
- return true;
128
- }
129
- // Check data attributes
130
- if (clickData.dataAttributes) {
131
- return Object.values(clickData.dataAttributes).some((value) => value.toLowerCase() === conditionValue);
132
- }
133
- return false;
134
- }
135
- /**
136
- * Builds searchable element data string with null safety
137
- */
138
- buildElementDataString(clickData) {
139
- const parts = [
140
- clickData.id,
141
- clickData.class,
142
- clickData.tag,
143
- clickData.text,
144
- clickData.href,
145
- clickData.title,
146
- clickData.alt,
147
- clickData.role,
148
- clickData.ariaLabel,
149
- ].filter(Boolean);
150
- if (clickData.dataAttributes) {
151
- parts.push(...Object.values(clickData.dataAttributes));
152
- }
153
- return parts.join(' ');
154
- }
155
- /**
156
- * Safe regex testing with error handling
157
- */
158
- testRegex(pattern, text) {
159
- try {
160
- const regex = new RegExp(pattern, 'gi');
161
- return regex.test(text);
162
- }
163
- catch {
164
- return false;
165
- }
166
- }
167
- }
@@ -1,52 +0,0 @@
1
- /**
2
- * Special project IDs for testing and development
3
- *
4
- * All automatically force mode: 'debug' but differ in HTTP behavior:
5
- * - Skip: NO network calls (pure offline testing)
6
- * - Localhost: Makes network calls to local server (integration testing)
7
- * - Fail: Makes network calls that intentionally fail (persistence testing)
8
- */
9
- export declare enum SpecialProjectId {
10
- /**
11
- * Value: 'skip'
12
- *
13
- * Skips ALL HTTP calls - no config fetch, no event sending
14
- * Uses default local config, forces debug mode
15
- * Perfect for pure offline E2E testing
16
- *
17
- * @example
18
- * await TraceLog.init({ id: SpecialProjectId.Skip });
19
- * // or
20
- * await TraceLog.init({ id: 'skip' });
21
- */
22
- Skip = "skip",
23
- /**
24
- * Value: 'localhost:8080'
25
- *
26
- * Makes HTTP calls to local development server on port 8080
27
- * Converts to http://localhost:8080/config for requests
28
- * Requires origin to be in ALLOWED_ORIGINS list, forces debug mode
29
- * Perfect for local development with running middleware
30
- *
31
- * @example
32
- * await TraceLog.init({ id: SpecialProjectId.Localhost });
33
- * // or
34
- * await TraceLog.init({ id: 'localhost:8080' });
35
- * // Makes requests to: http://localhost:8080/config
36
- */
37
- Localhost = "localhost:8080",
38
- /**
39
- * Value: 'localhost:9999'
40
- *
41
- * Makes HTTP calls to non-existent server (port 9999)
42
- * All HTTP requests will fail naturally, triggering persistence
43
- * Forces debug mode, perfect for testing event persistence & recovery
44
- *
45
- * @example
46
- * await TraceLog.init({ id: SpecialProjectId.Fail });
47
- * // or
48
- * await TraceLog.init({ id: 'localhost:9999' });
49
- * // Makes requests to: http://localhost:9999 (will fail)
50
- */
51
- Fail = "localhost:9999"
52
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Special project IDs for testing and development
3
- *
4
- * All automatically force mode: 'debug' but differ in HTTP behavior:
5
- * - Skip: NO network calls (pure offline testing)
6
- * - Localhost: Makes network calls to local server (integration testing)
7
- * - Fail: Makes network calls that intentionally fail (persistence testing)
8
- */
9
- export var SpecialProjectId;
10
- (function (SpecialProjectId) {
11
- /**
12
- * Value: 'skip'
13
- *
14
- * Skips ALL HTTP calls - no config fetch, no event sending
15
- * Uses default local config, forces debug mode
16
- * Perfect for pure offline E2E testing
17
- *
18
- * @example
19
- * await TraceLog.init({ id: SpecialProjectId.Skip });
20
- * // or
21
- * await TraceLog.init({ id: 'skip' });
22
- */
23
- SpecialProjectId["Skip"] = "skip";
24
- /**
25
- * Value: 'localhost:8080'
26
- *
27
- * Makes HTTP calls to local development server on port 8080
28
- * Converts to http://localhost:8080/config for requests
29
- * Requires origin to be in ALLOWED_ORIGINS list, forces debug mode
30
- * Perfect for local development with running middleware
31
- *
32
- * @example
33
- * await TraceLog.init({ id: SpecialProjectId.Localhost });
34
- * // or
35
- * await TraceLog.init({ id: 'localhost:8080' });
36
- * // Makes requests to: http://localhost:8080/config
37
- */
38
- SpecialProjectId["Localhost"] = "localhost:8080";
39
- /**
40
- * Value: 'localhost:9999'
41
- *
42
- * Makes HTTP calls to non-existent server (port 9999)
43
- * All HTTP requests will fail naturally, triggering persistence
44
- * Forces debug mode, perfect for testing event persistence & recovery
45
- *
46
- * @example
47
- * await TraceLog.init({ id: SpecialProjectId.Fail });
48
- * // or
49
- * await TraceLog.init({ id: 'localhost:9999' });
50
- * // Makes requests to: http://localhost:9999 (will fail)
51
- */
52
- SpecialProjectId["Fail"] = "localhost:9999";
53
- })(SpecialProjectId || (SpecialProjectId = {}));
@@ -1,43 +0,0 @@
1
- import { EventType } from './event.types';
2
- export declare enum TagLogicalOperator {
3
- AND = "AND",
4
- OR = "OR"
5
- }
6
- export declare enum TagConditionType {
7
- URL_MATCHES = "url_matches",
8
- ELEMENT_MATCHES = "element_matches",
9
- DEVICE_TYPE = "device_type",
10
- ELEMENT_TEXT = "element_text",
11
- ELEMENT_ATTRIBUTE = "element_attribute",
12
- UTM_SOURCE = "utm_source",
13
- UTM_MEDIUM = "utm_medium",
14
- UTM_CAMPAIGN = "utm_campaign"
15
- }
16
- export declare enum TagConditionOperator {
17
- EQUALS = "equals",
18
- CONTAINS = "contains",
19
- STARTS_WITH = "starts_with",
20
- ENDS_WITH = "ends_with",
21
- REGEX = "regex",
22
- GREATER_THAN = "greater_than",
23
- LESS_THAN = "less_than",
24
- EXISTS = "exists",
25
- NOT_EXISTS = "not_exists"
26
- }
27
- export type TagConfig = Pick<Tag, 'key' | 'triggerType' | 'logicalOperator' | 'conditions'> & {
28
- id: string;
29
- };
30
- export interface Tag {
31
- key: string;
32
- name: string;
33
- description?: string;
34
- active: boolean;
35
- triggerType: EventType;
36
- logicalOperator?: TagLogicalOperator;
37
- conditions: TagCondition[];
38
- }
39
- export interface TagCondition {
40
- type: TagConditionType;
41
- operator: TagConditionOperator;
42
- value: string;
43
- }