@tracelog/lib 0.6.0 → 0.6.3

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 (58) hide show
  1. package/README.md +9 -9
  2. package/dist/browser/tracelog.esm.js +406 -304
  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 +1 -1
  7. package/dist/cjs/api.js +13 -4
  8. package/dist/cjs/app.d.ts +1 -1
  9. package/dist/cjs/app.js +4 -4
  10. package/dist/cjs/constants/config.constants.d.ts +3 -0
  11. package/dist/cjs/constants/config.constants.js +5 -2
  12. package/dist/cjs/handlers/click.handler.js +2 -2
  13. package/dist/cjs/handlers/scroll.handler.js +1 -1
  14. package/dist/cjs/handlers/session.handler.js +1 -1
  15. package/dist/cjs/managers/event.manager.d.ts +3 -0
  16. package/dist/cjs/managers/event.manager.js +47 -6
  17. package/dist/cjs/managers/sender.manager.js +4 -5
  18. package/dist/cjs/managers/storage.manager.d.ts +5 -0
  19. package/dist/cjs/managers/storage.manager.js +95 -6
  20. package/dist/cjs/public-api.d.ts +1 -1
  21. package/dist/cjs/test-bridge.d.ts +1 -1
  22. package/dist/cjs/test-bridge.js +1 -1
  23. package/dist/cjs/types/config.types.d.ts +4 -4
  24. package/dist/cjs/types/state.types.d.ts +1 -1
  25. package/dist/cjs/types/test-bridge.types.d.ts +1 -1
  26. package/dist/cjs/utils/logging.utils.d.ts +16 -1
  27. package/dist/cjs/utils/logging.utils.js +65 -4
  28. package/dist/cjs/utils/network/url.utils.d.ts +1 -1
  29. package/dist/cjs/utils/network/url.utils.js +11 -12
  30. package/dist/cjs/utils/validations/config-validations.utils.d.ts +2 -2
  31. package/dist/cjs/utils/validations/config-validations.utils.js +30 -18
  32. package/dist/esm/api.d.ts +1 -1
  33. package/dist/esm/api.js +13 -4
  34. package/dist/esm/app.d.ts +1 -1
  35. package/dist/esm/app.js +5 -5
  36. package/dist/esm/constants/config.constants.d.ts +3 -0
  37. package/dist/esm/constants/config.constants.js +3 -0
  38. package/dist/esm/handlers/click.handler.js +2 -2
  39. package/dist/esm/handlers/scroll.handler.js +1 -1
  40. package/dist/esm/handlers/session.handler.js +1 -1
  41. package/dist/esm/managers/event.manager.d.ts +3 -0
  42. package/dist/esm/managers/event.manager.js +48 -7
  43. package/dist/esm/managers/sender.manager.js +4 -5
  44. package/dist/esm/managers/storage.manager.d.ts +5 -0
  45. package/dist/esm/managers/storage.manager.js +95 -6
  46. package/dist/esm/public-api.d.ts +1 -1
  47. package/dist/esm/test-bridge.d.ts +1 -1
  48. package/dist/esm/test-bridge.js +1 -1
  49. package/dist/esm/types/config.types.d.ts +4 -4
  50. package/dist/esm/types/state.types.d.ts +1 -1
  51. package/dist/esm/types/test-bridge.types.d.ts +1 -1
  52. package/dist/esm/utils/logging.utils.d.ts +16 -1
  53. package/dist/esm/utils/logging.utils.js +65 -4
  54. package/dist/esm/utils/network/url.utils.d.ts +1 -1
  55. package/dist/esm/utils/network/url.utils.js +9 -10
  56. package/dist/esm/utils/validations/config-validations.utils.d.ts +2 -2
  57. package/dist/esm/utils/validations/config-validations.utils.js +30 -18
  58. package/package.json +7 -6
@@ -37,6 +37,9 @@ export class StorageManager {
37
37
  * Stores an item in storage
38
38
  */
39
39
  setItem(key, value) {
40
+ // Always update fallback FIRST for consistency
41
+ // This ensures fallback is in sync and can serve as backup if storage fails
42
+ this.fallbackStorage.set(key, value);
40
43
  try {
41
44
  if (this.storage) {
42
45
  this.storage.setItem(key, value);
@@ -46,15 +49,37 @@ export class StorageManager {
46
49
  catch (error) {
47
50
  if (error instanceof DOMException && error.name === 'QuotaExceededError') {
48
51
  this.hasQuotaExceededError = true;
49
- log('error', 'localStorage quota exceeded - data will not persist after reload', {
50
- error,
52
+ log('warn', 'localStorage quota exceeded, attempting cleanup', {
51
53
  data: { key, valueSize: value.length },
52
54
  });
55
+ // Attempt to free up space by removing old TraceLog data
56
+ const cleanedUp = this.cleanupOldData();
57
+ if (cleanedUp) {
58
+ // Retry after cleanup
59
+ try {
60
+ if (this.storage) {
61
+ this.storage.setItem(key, value);
62
+ // Successfully stored after cleanup
63
+ return;
64
+ }
65
+ }
66
+ catch (retryError) {
67
+ log('error', 'localStorage quota exceeded even after cleanup - data will not persist', {
68
+ error: retryError,
69
+ data: { key, valueSize: value.length },
70
+ });
71
+ }
72
+ }
73
+ else {
74
+ log('error', 'localStorage quota exceeded and no data to cleanup - data will not persist', {
75
+ error,
76
+ data: { key, valueSize: value.length },
77
+ });
78
+ }
53
79
  }
54
80
  // Else: Silent fallback - user already warned in constructor
81
+ // Data is already in fallbackStorage (set at beginning)
55
82
  }
56
- // Always update fallback for consistency
57
- this.fallbackStorage.set(key, value);
58
83
  }
59
84
  /**
60
85
  * Removes an item from storage
@@ -108,6 +133,69 @@ export class StorageManager {
108
133
  hasQuotaError() {
109
134
  return this.hasQuotaExceededError;
110
135
  }
136
+ /**
137
+ * Attempts to cleanup old TraceLog data from storage to free up space
138
+ * Returns true if any data was removed, false otherwise
139
+ */
140
+ cleanupOldData() {
141
+ if (!this.storage) {
142
+ return false;
143
+ }
144
+ try {
145
+ const tracelogKeys = [];
146
+ const persistedEventsKeys = [];
147
+ // Collect all TraceLog keys
148
+ for (let i = 0; i < this.storage.length; i++) {
149
+ const key = this.storage.key(i);
150
+ if (key?.startsWith('tracelog_')) {
151
+ tracelogKeys.push(key);
152
+ // Prioritize removing old persisted events
153
+ if (key.startsWith('tracelog_persisted_events_')) {
154
+ persistedEventsKeys.push(key);
155
+ }
156
+ }
157
+ }
158
+ // First, try to remove old persisted events (usually the largest data)
159
+ if (persistedEventsKeys.length > 0) {
160
+ persistedEventsKeys.forEach((key) => {
161
+ try {
162
+ this.storage.removeItem(key);
163
+ }
164
+ catch {
165
+ // Ignore errors during cleanup
166
+ }
167
+ });
168
+ // Successfully cleaned up - no need to log in production
169
+ return true;
170
+ }
171
+ // If no persisted events, remove non-critical keys
172
+ // Define critical key prefixes that should be preserved
173
+ const criticalPrefixes = ['tracelog_session_', 'tracelog_user_id', 'tracelog_device_id', 'tracelog_config'];
174
+ const nonCriticalKeys = tracelogKeys.filter((key) => {
175
+ // Keep keys that start with any critical prefix
176
+ return !criticalPrefixes.some((prefix) => key.startsWith(prefix));
177
+ });
178
+ if (nonCriticalKeys.length > 0) {
179
+ // Remove up to 5 non-critical keys
180
+ const keysToRemove = nonCriticalKeys.slice(0, 5);
181
+ keysToRemove.forEach((key) => {
182
+ try {
183
+ this.storage.removeItem(key);
184
+ }
185
+ catch {
186
+ // Ignore errors during cleanup
187
+ }
188
+ });
189
+ // Successfully cleaned up - no need to log in production
190
+ return true;
191
+ }
192
+ return false;
193
+ }
194
+ catch (error) {
195
+ log('error', 'Failed to cleanup old data', { error });
196
+ return false;
197
+ }
198
+ }
111
199
  /**
112
200
  * Initialize storage (localStorage or sessionStorage) with feature detection
113
201
  */
@@ -145,6 +233,8 @@ export class StorageManager {
145
233
  * Stores an item in sessionStorage
146
234
  */
147
235
  setSessionItem(key, value) {
236
+ // Always update fallback FIRST for consistency
237
+ this.fallbackSessionStorage.set(key, value);
148
238
  try {
149
239
  if (this.sessionStorageRef) {
150
240
  this.sessionStorageRef.setItem(key, value);
@@ -159,9 +249,8 @@ export class StorageManager {
159
249
  });
160
250
  }
161
251
  // Else: Silent fallback - user already warned in constructor
252
+ // Data is already in fallbackSessionStorage (set at beginning)
162
253
  }
163
- // Always update fallback for consistency
164
- this.fallbackSessionStorage.set(key, value);
165
254
  }
166
255
  /**
167
256
  * Removes an item from sessionStorage
@@ -1,7 +1,7 @@
1
1
  export * from './app.constants';
2
2
  export * from './types';
3
3
  export declare const tracelog: {
4
- init: (config: import("./types").Config) => Promise<void>;
4
+ init: (config?: import("./types").Config) => Promise<void>;
5
5
  event: (name: string, metadata?: Record<string, import("./types").MetadataType> | Record<string, import("./types").MetadataType>[]) => void;
6
6
  on: <K extends keyof import("./types").EmitterMap>(event: K, callback: import("./types").EmitterCallback<import("./types").EmitterMap[K]>) => void;
7
7
  off: <K extends keyof import("./types").EmitterMap>(event: K, callback: import("./types").EmitterCallback<import("./types").EmitterMap[K]>) => void;
@@ -16,7 +16,7 @@ export declare class TestBridge extends App implements TraceLogTestBridge {
16
16
  private _isInitializing;
17
17
  private _isDestroying;
18
18
  constructor(isInitializing: boolean, isDestroying: boolean);
19
- init(config: any): Promise<void>;
19
+ init(config?: any): Promise<void>;
20
20
  isInitializing(): boolean;
21
21
  sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[]): void;
22
22
  getSessionData(): Record<string, unknown> | null;
@@ -74,7 +74,7 @@ export class TestBridge extends App {
74
74
  throw new Error('Storage manager not available');
75
75
  }
76
76
  const config = this.get('config');
77
- const projectId = config?.integrations?.tracelog?.projectId ?? config?.integrations?.custom?.apiUrl ?? 'test';
77
+ const projectId = config?.integrations?.tracelog?.projectId ?? config?.integrations?.custom?.collectApiUrl ?? 'test';
78
78
  const userId = this.get('userId');
79
79
  const sessionId = this.get('sessionId');
80
80
  if (!projectId || !userId) {
@@ -6,8 +6,6 @@ export interface Config {
6
6
  globalMetadata?: Record<string, MetadataType>;
7
7
  /** Selectors defining custom scroll containers to monitor. */
8
8
  scrollContainerSelectors?: string | string[];
9
- /** Enables HTTP requests for testing and development flows. */
10
- allowHttp?: boolean;
11
9
  /** Query parameters to remove before tracking URLs. */
12
10
  sensitiveQueryParams?: string[];
13
11
  /** Error event sampling rate between 0 and 1. */
@@ -23,8 +21,10 @@ export interface Config {
23
21
  };
24
22
  /** Custom integration options. */
25
23
  custom?: {
26
- /** Required API URL for custom integration. */
27
- apiUrl: string;
24
+ /** Endpoint for collecting events. */
25
+ collectApiUrl: string;
26
+ /** Allow HTTP URLs (not recommended for production). @default false */
27
+ allowHttp?: boolean;
28
28
  };
29
29
  /** Google Analytics integration options. */
30
30
  googleAnalytics?: {
@@ -3,7 +3,7 @@ import { DeviceType } from './device.types';
3
3
  import { Mode } from './mode.types';
4
4
  export interface State {
5
5
  mode?: Mode;
6
- apiUrl: string;
6
+ collectApiUrl: string;
7
7
  config: Config;
8
8
  sessionId: string | null;
9
9
  userId: string;
@@ -15,7 +15,7 @@ import { State } from './state.types';
15
15
  */
16
16
  export interface TraceLogTestBridge {
17
17
  readonly initialized: boolean;
18
- init(config: Config): Promise<void>;
18
+ init(config?: Config): Promise<void>;
19
19
  destroy(): Promise<void>;
20
20
  isInitializing(): boolean;
21
21
  sendCustomEvent(name: string, data?: Record<string, unknown> | Record<string, unknown>[]): void;
@@ -1,5 +1,20 @@
1
1
  export declare const formatLogMsg: (msg: string, error?: unknown) => string;
2
- export declare const log: (type: "info" | "warn" | "error", msg: string, extra?: {
2
+ /**
3
+ * Safe logging utility that respects production environment
4
+ *
5
+ * @param type - Log level (info, warn, error, debug)
6
+ * @param msg - Message to log
7
+ * @param extra - Optional extra data
8
+ *
9
+ * Production behavior:
10
+ * - debug: Never logged in production
11
+ * - info: Only logged if showToClient=true
12
+ * - warn: Always logged (important for debugging production issues)
13
+ * - error: Always logged
14
+ * - Stack traces are sanitized
15
+ * - Data objects are sanitized
16
+ */
17
+ export declare const log: (type: "info" | "warn" | "error" | "debug", msg: string, extra?: {
3
18
  error?: unknown;
4
19
  data?: Record<string, unknown>;
5
20
  showToClient?: boolean;
@@ -1,20 +1,81 @@
1
1
  export const formatLogMsg = (msg, error) => {
2
2
  if (error) {
3
+ // In production, sanitize error messages to avoid exposing sensitive paths
4
+ if (process.env.NODE_ENV !== 'dev' && error instanceof Error) {
5
+ // Remove file paths and line numbers from error messages
6
+ const sanitizedMessage = error.message.replace(/\s+at\s+.*$/gm, '').replace(/\(.*?:\d+:\d+\)/g, '');
7
+ return `[TraceLog] ${msg}: ${sanitizedMessage}`;
8
+ }
3
9
  return `[TraceLog] ${msg}: ${error instanceof Error ? error.message : 'Unknown error'}`;
4
10
  }
5
11
  return `[TraceLog] ${msg}`;
6
12
  };
13
+ /**
14
+ * Safe logging utility that respects production environment
15
+ *
16
+ * @param type - Log level (info, warn, error, debug)
17
+ * @param msg - Message to log
18
+ * @param extra - Optional extra data
19
+ *
20
+ * Production behavior:
21
+ * - debug: Never logged in production
22
+ * - info: Only logged if showToClient=true
23
+ * - warn: Always logged (important for debugging production issues)
24
+ * - error: Always logged
25
+ * - Stack traces are sanitized
26
+ * - Data objects are sanitized
27
+ */
7
28
  export const log = (type, msg, extra) => {
8
- const { error, data, showToClient } = extra ?? {};
29
+ const { error, data, showToClient = false } = extra ?? {};
9
30
  const formattedMsg = error ? formatLogMsg(msg, error) : `[TraceLog] ${msg}`;
10
31
  const method = type === 'error' ? 'error' : type === 'warn' ? 'warn' : 'log';
11
- if (process.env.NODE_ENV !== 'dev' && !showToClient) {
12
- return;
32
+ // Production logging strategy:
33
+ // - Development: Log everything
34
+ // - Production:
35
+ // - debug: never logged
36
+ // - info: only if showToClient=true
37
+ // - warn: always logged (critical for debugging)
38
+ // - error: always logged
39
+ const isProduction = process.env.NODE_ENV !== 'dev';
40
+ if (isProduction) {
41
+ // Never log debug in production
42
+ if (type === 'debug') {
43
+ return;
44
+ }
45
+ // Log info only if explicitly flagged
46
+ if (type === 'info' && !showToClient) {
47
+ return;
48
+ }
49
+ // warn and error always logged in production
13
50
  }
14
- if (data !== undefined) {
51
+ // In production, sanitize data to avoid exposing sensitive information
52
+ if (isProduction && data !== undefined) {
53
+ const sanitizedData = sanitizeLogData(data);
54
+ console[method](formattedMsg, sanitizedData);
55
+ }
56
+ else if (data !== undefined) {
15
57
  console[method](formattedMsg, data);
16
58
  }
17
59
  else {
18
60
  console[method](formattedMsg);
19
61
  }
20
62
  };
63
+ /**
64
+ * Sanitizes log data in production to prevent sensitive information leakage
65
+ * Simple approach: redact sensitive keys only
66
+ */
67
+ const sanitizeLogData = (data) => {
68
+ const sanitized = {};
69
+ const sensitiveKeys = ['token', 'password', 'secret', 'key', 'apikey', 'api_key', 'sessionid', 'session_id'];
70
+ for (const [key, value] of Object.entries(data)) {
71
+ const lowerKey = key.toLowerCase();
72
+ // Redact sensitive keys
73
+ if (sensitiveKeys.some((sensitiveKey) => lowerKey.includes(sensitiveKey))) {
74
+ sanitized[key] = '[REDACTED]';
75
+ }
76
+ else {
77
+ sanitized[key] = value;
78
+ }
79
+ }
80
+ return sanitized;
81
+ };
@@ -4,7 +4,7 @@ import { Config } from '@/types';
4
4
  * @param id - The project ID
5
5
  * @returns The generated API URL
6
6
  */
7
- export declare const getApiUrl: (config: Config) => string;
7
+ export declare const getCollectApiUrl: (config: Config) => string;
8
8
  /**
9
9
  * Normalizes a URL by removing sensitive query parameters
10
10
  * @param url - The URL to normalize
@@ -21,8 +21,7 @@ const isValidUrl = (url, allowHttp = false) => {
21
21
  * @param id - The project ID
22
22
  * @returns The generated API URL
23
23
  */
24
- export const getApiUrl = (config) => {
25
- const allowHttp = config.allowHttp ?? false;
24
+ export const getCollectApiUrl = (config) => {
26
25
  if (config.integrations?.tracelog?.projectId) {
27
26
  const url = new URL(window.location.href);
28
27
  const host = url.hostname;
@@ -32,21 +31,21 @@ export const getApiUrl = (config) => {
32
31
  }
33
32
  const projectId = config.integrations.tracelog.projectId;
34
33
  const cleanDomain = parts.slice(-2).join('.');
35
- const protocol = allowHttp && url.protocol === 'http:' ? 'http' : 'https';
36
- const apiUrl = `${protocol}://${projectId}.${cleanDomain}`;
37
- const isValid = isValidUrl(apiUrl, allowHttp);
34
+ const collectApiUrl = `https://${projectId}.${cleanDomain}/collect`;
35
+ const isValid = isValidUrl(collectApiUrl);
38
36
  if (!isValid) {
39
37
  throw new Error('Invalid URL');
40
38
  }
41
- return apiUrl;
39
+ return collectApiUrl;
42
40
  }
43
- if (config.integrations?.custom?.apiUrl) {
44
- const apiUrl = config.integrations.custom.apiUrl;
45
- const isValid = isValidUrl(apiUrl, allowHttp);
41
+ const collectApiUrl = config.integrations?.custom?.collectApiUrl;
42
+ if (collectApiUrl) {
43
+ const allowHttp = config.integrations?.custom?.allowHttp ?? false;
44
+ const isValid = isValidUrl(collectApiUrl, allowHttp);
46
45
  if (!isValid) {
47
46
  throw new Error('Invalid URL');
48
47
  }
49
- return apiUrl;
48
+ return collectApiUrl;
50
49
  }
51
50
  return '';
52
51
  };
@@ -6,7 +6,7 @@ import { Config } from '../../types';
6
6
  * @throws {ProjectIdValidationError} If project ID validation fails
7
7
  * @throws {AppConfigValidationError} If other configuration validation fails
8
8
  */
9
- export declare const validateAppConfig: (config: Config) => void;
9
+ export declare const validateAppConfig: (config?: Config) => void;
10
10
  /**
11
11
  * Validates and normalizes the app configuration
12
12
  * This is the primary validation entry point that ensures consistent behavior
@@ -15,4 +15,4 @@ export declare const validateAppConfig: (config: Config) => void;
15
15
  * @throws {ProjectIdValidationError} If project ID validation fails after normalization
16
16
  * @throws {AppConfigValidationError} If other configuration validation fails
17
17
  */
18
- export declare const validateAndNormalizeConfig: (config: Config) => Config;
18
+ export declare const validateAndNormalizeConfig: (config?: Config) => Config;
@@ -9,9 +9,12 @@ import { log } from '../logging.utils';
9
9
  * @throws {AppConfigValidationError} If other configuration validation fails
10
10
  */
11
11
  export const validateAppConfig = (config) => {
12
- if (!config || typeof config !== 'object') {
12
+ if (config !== undefined && (config === null || typeof config !== 'object')) {
13
13
  throw new AppConfigValidationError('Configuration must be an object', 'config');
14
14
  }
15
+ if (!config) {
16
+ return;
17
+ }
15
18
  if (config.sessionTimeout !== undefined) {
16
19
  if (typeof config.sessionTimeout !== 'number' ||
17
20
  config.sessionTimeout < MIN_SESSION_TIMEOUT_MS ||
@@ -50,11 +53,6 @@ export const validateAppConfig = (config) => {
50
53
  throw new SamplingRateValidationError(VALIDATION_MESSAGES.INVALID_SAMPLING_RATE, 'config');
51
54
  }
52
55
  }
53
- if (config.allowHttp !== undefined) {
54
- if (typeof config.allowHttp !== 'boolean') {
55
- throw new AppConfigValidationError('allowHttp must be a boolean', 'config');
56
- }
57
- }
58
56
  };
59
57
  /**
60
58
  * Validates CSS selector syntax without executing querySelector (XSS prevention)
@@ -145,13 +143,21 @@ const validateIntegrations = (integrations) => {
145
143
  }
146
144
  }
147
145
  if (integrations.custom) {
148
- if (!integrations.custom.apiUrl ||
149
- typeof integrations.custom.apiUrl !== 'string' ||
150
- integrations.custom.apiUrl.trim() === '') {
146
+ if (!integrations.custom.collectApiUrl ||
147
+ typeof integrations.custom.collectApiUrl !== 'string' ||
148
+ integrations.custom.collectApiUrl.trim() === '') {
151
149
  throw new IntegrationValidationError(VALIDATION_MESSAGES.INVALID_CUSTOM_API_URL, 'config');
152
150
  }
153
- if (!integrations.custom.apiUrl.startsWith('http')) {
154
- throw new IntegrationValidationError('Custom API URL must start with "http"', 'config');
151
+ if (integrations.custom.allowHttp !== undefined && typeof integrations.custom.allowHttp !== 'boolean') {
152
+ throw new IntegrationValidationError('allowHttp must be a boolean', 'config');
153
+ }
154
+ const collectApiUrl = integrations.custom.collectApiUrl.trim();
155
+ if (!collectApiUrl.startsWith('http://') && !collectApiUrl.startsWith('https://')) {
156
+ throw new IntegrationValidationError('Custom API URL must start with "http://" or "https://"', 'config');
157
+ }
158
+ const allowHttp = integrations.custom.allowHttp ?? false;
159
+ if (!allowHttp && collectApiUrl.startsWith('http://')) {
160
+ throw new IntegrationValidationError('Custom API URL must use HTTPS in production. Set allowHttp: true in integration config to allow HTTP (not recommended)', 'config');
155
161
  }
156
162
  }
157
163
  if (integrations.googleAnalytics) {
@@ -177,13 +183,19 @@ const validateIntegrations = (integrations) => {
177
183
  export const validateAndNormalizeConfig = (config) => {
178
184
  validateAppConfig(config);
179
185
  const normalizedConfig = {
180
- ...config,
181
- sessionTimeout: config.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT,
182
- globalMetadata: config.globalMetadata ?? {},
183
- sensitiveQueryParams: config.sensitiveQueryParams ?? [],
184
- errorSampling: config.errorSampling ?? 1,
185
- samplingRate: config.samplingRate ?? 1,
186
- allowHttp: config.allowHttp ?? false,
186
+ ...(config ?? {}),
187
+ sessionTimeout: config?.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT,
188
+ globalMetadata: config?.globalMetadata ?? {},
189
+ sensitiveQueryParams: config?.sensitiveQueryParams ?? [],
190
+ errorSampling: config?.errorSampling ?? 1,
191
+ samplingRate: config?.samplingRate ?? 1,
187
192
  };
193
+ // Normalize integrations
194
+ if (normalizedConfig.integrations?.custom) {
195
+ normalizedConfig.integrations.custom = {
196
+ ...normalizedConfig.integrations.custom,
197
+ allowHttp: normalizedConfig.integrations.custom.allowHttp ?? false,
198
+ };
199
+ }
188
200
  return normalizedConfig;
189
201
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tracelog/lib",
3
3
  "description": "JavaScript library for web analytics and real-time event tracking",
4
4
  "license": "MIT",
5
- "version": "0.6.0",
5
+ "version": "0.6.3",
6
6
  "main": "./dist/cjs/public-api.js",
7
7
  "module": "./dist/esm/public-api.js",
8
8
  "types": "./dist/esm/public-api.d.ts",
@@ -40,10 +40,11 @@
40
40
  "test:unit:watch": "vitest",
41
41
  "test:coverage": "vitest run --coverage",
42
42
  "test:integration": "vitest run --config vitest.integration.config.mjs",
43
- "serve": "http-server playground -p 3000 --cors",
44
- "playground:setup": "npm run build:browser:dev && cp dist/browser/tracelog.esm.js playground/tracelog.js",
45
- "playground:dev": "npm run playground:setup && npm run serve",
46
- "test:e2e": "npm run build:browser:dev && cp dist/browser/tracelog.esm.js playground/tracelog.js && NODE_ENV=dev playwright test",
43
+ "serve": "http-server docs -p 3000 --cors",
44
+ "docs:setup": "npm run build:browser:dev && cp dist/browser/tracelog.esm.js docs/tracelog.js",
45
+ "docs:dev": "npm run docs:setup && npm run serve",
46
+ "docs:gh-pages": "npm run build:browser && cp dist/browser/tracelog.esm.js docs/tracelog.js",
47
+ "test:e2e": "npm run docs:setup && NODE_ENV=dev playwright test",
47
48
  "ci:build": "npm run build:all",
48
49
  "prepare": "husky",
49
50
  "release": "node scripts/release.js",
@@ -55,7 +56,7 @@
55
56
  "changelog:preview": "node scripts/generate-changelog.js --dry-run"
56
57
  },
57
58
  "dependencies": {
58
- "web-vitals": "^4.2.4"
59
+ "web-vitals": "4.2.4"
59
60
  },
60
61
  "devDependencies": {
61
62
  "@commitlint/config-conventional": "^19.8.1",