@armco/analytics 0.2.11 → 0.3.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 (44) hide show
  1. package/core/analytics.d.ts +49 -0
  2. package/core/analytics.js +354 -0
  3. package/core/errors.d.ts +26 -0
  4. package/core/errors.js +54 -0
  5. package/core/types.d.ts +137 -0
  6. package/core/types.js +1 -0
  7. package/global-modules.d.ts +14 -0
  8. package/index.d.ts +24 -0
  9. package/index.js +24 -0
  10. package/package.json +6 -36
  11. package/plugins/auto-track/click.d.ts +15 -0
  12. package/plugins/auto-track/click.js +99 -0
  13. package/plugins/auto-track/error.d.ts +15 -0
  14. package/plugins/auto-track/error.js +65 -0
  15. package/plugins/auto-track/form.d.ts +13 -0
  16. package/plugins/auto-track/form.js +54 -0
  17. package/plugins/auto-track/page.d.ts +14 -0
  18. package/plugins/auto-track/page.js +72 -0
  19. package/plugins/enrichment/session.d.ts +18 -0
  20. package/plugins/enrichment/session.js +81 -0
  21. package/plugins/enrichment/user.d.ts +20 -0
  22. package/plugins/enrichment/user.js +159 -0
  23. package/plugins/node/http-request-tracking.d.ts +54 -0
  24. package/plugins/node/http-request-tracking.js +158 -0
  25. package/storage/cookie-storage.d.ts +8 -0
  26. package/storage/cookie-storage.js +60 -0
  27. package/storage/hybrid-storage.d.ts +12 -0
  28. package/storage/hybrid-storage.js +108 -0
  29. package/storage/local-storage.d.ts +8 -0
  30. package/storage/local-storage.js +65 -0
  31. package/storage/memory-storage.d.ts +9 -0
  32. package/storage/memory-storage.js +22 -0
  33. package/transport/beacon-transport.d.ts +16 -0
  34. package/transport/beacon-transport.js +50 -0
  35. package/transport/fetch-transport.d.ts +20 -0
  36. package/transport/fetch-transport.js +112 -0
  37. package/utils/config-loader.d.ts +6 -0
  38. package/utils/config-loader.js +168 -0
  39. package/utils/helpers.d.ts +15 -0
  40. package/utils/helpers.js +148 -0
  41. package/utils/logging.d.ts +17 -0
  42. package/utils/logging.js +54 -0
  43. package/utils/validation.d.ts +9 -0
  44. package/utils/validation.js +146 -0
@@ -0,0 +1,50 @@
1
+ import { NetworkError } from "../core/errors";
2
+ import { getLogger } from "../utils/logging";
3
+ export class BeaconTransport {
4
+ constructor(options = {}) {
5
+ this.logger = getLogger();
6
+ this.options = options;
7
+ }
8
+ async send(endpoint, event) {
9
+ return this.sendBeacon(endpoint, { event });
10
+ }
11
+ async sendBatch(endpoint, events) {
12
+ return this.sendBeacon(endpoint, { events });
13
+ }
14
+ async update(endpoint, payload) {
15
+ this.logger.warn("BeaconTransport does not support update operations. Use FetchTransport for user identification updates.");
16
+ return {
17
+ success: false,
18
+ error: "Update not supported by BeaconTransport",
19
+ };
20
+ }
21
+ async sendBeacon(endpoint, payload) {
22
+ if (!navigator.sendBeacon) {
23
+ this.logger.warn("Beacon API not available, data may be lost");
24
+ throw new NetworkError("Beacon API not available", undefined, endpoint);
25
+ }
26
+ try {
27
+ const blob = new Blob([JSON.stringify(payload)], {
28
+ type: "application/json",
29
+ });
30
+ const success = navigator.sendBeacon(endpoint, blob);
31
+ if (success) {
32
+ this.logger.debug(`Successfully queued beacon to ${endpoint}`);
33
+ return {
34
+ success: true,
35
+ };
36
+ }
37
+ else {
38
+ this.logger.warn(`Failed to queue beacon to ${endpoint}`);
39
+ return {
40
+ success: false,
41
+ error: "Beacon queue full or rejected",
42
+ };
43
+ }
44
+ }
45
+ catch (error) {
46
+ this.logger.error(`Error sending beacon to ${endpoint}:`, error);
47
+ throw new NetworkError(`Failed to send beacon: ${error}`, undefined, endpoint);
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,20 @@
1
+ import type { Transport, TransportResponse, TrackingEvent } from "../core/types";
2
+ export interface FetchTransportOptions {
3
+ apiKey?: string;
4
+ timeout?: number;
5
+ maxRetries?: number;
6
+ retryDelay?: number;
7
+ }
8
+ export declare class FetchTransport implements Transport {
9
+ private options;
10
+ private logger;
11
+ constructor(options?: FetchTransportOptions);
12
+ send(endpoint: string, event: TrackingEvent): Promise<TransportResponse>;
13
+ sendBatch(endpoint: string, events: TrackingEvent[]): Promise<TransportResponse>;
14
+ private sendWithRetry;
15
+ update(endpoint: string, payload: {
16
+ email: string;
17
+ anonymousId: string;
18
+ }): Promise<TransportResponse>;
19
+ private delay;
20
+ }
@@ -0,0 +1,112 @@
1
+ import { NetworkError } from "../core/errors";
2
+ import { getLogger } from "../utils/logging";
3
+ export class FetchTransport {
4
+ constructor(options = {}) {
5
+ this.logger = getLogger();
6
+ this.options = {
7
+ timeout: 5000,
8
+ maxRetries: 3,
9
+ retryDelay: 1000,
10
+ ...options,
11
+ };
12
+ }
13
+ async send(endpoint, event) {
14
+ return this.sendWithRetry(endpoint, { event }, 0);
15
+ }
16
+ async sendBatch(endpoint, events) {
17
+ return this.sendWithRetry(endpoint, { events }, 0);
18
+ }
19
+ async sendWithRetry(endpoint, payload, attempt) {
20
+ try {
21
+ const controller = new AbortController();
22
+ const timeoutId = setTimeout(() => controller.abort(), this.options.timeout);
23
+ const headers = {
24
+ "Content-Type": "application/json",
25
+ };
26
+ if (this.options.apiKey) {
27
+ headers["Authorization"] = `Bearer ${this.options.apiKey}`;
28
+ }
29
+ const response = await fetch(endpoint, {
30
+ method: "POST",
31
+ headers,
32
+ body: JSON.stringify(payload),
33
+ signal: controller.signal,
34
+ });
35
+ clearTimeout(timeoutId);
36
+ if (response.ok) {
37
+ this.logger.debug(`Successfully sent data to ${endpoint}`);
38
+ return {
39
+ success: true,
40
+ statusCode: response.status,
41
+ };
42
+ }
43
+ else {
44
+ const errorText = await response.text().catch(() => "Unknown error");
45
+ this.logger.warn(`Failed to send data to ${endpoint}: ${response.status} ${errorText}`);
46
+ if (response.status >= 500 &&
47
+ attempt < (this.options.maxRetries ?? 3)) {
48
+ await this.delay(this.options.retryDelay ?? 1000);
49
+ return this.sendWithRetry(endpoint, payload, attempt + 1);
50
+ }
51
+ return {
52
+ success: false,
53
+ statusCode: response.status,
54
+ error: errorText,
55
+ };
56
+ }
57
+ }
58
+ catch (error) {
59
+ this.logger.error(`Network error sending to ${endpoint}:`, error);
60
+ if (attempt < (this.options.maxRetries ?? 3)) {
61
+ await this.delay(this.options.retryDelay ?? 1000);
62
+ return this.sendWithRetry(endpoint, payload, attempt + 1);
63
+ }
64
+ throw new NetworkError(`Failed to send data after ${attempt + 1} attempts`, undefined, endpoint);
65
+ }
66
+ }
67
+ async update(endpoint, payload) {
68
+ try {
69
+ const controller = new AbortController();
70
+ const timeoutId = setTimeout(() => controller.abort(), this.options.timeout);
71
+ const headers = {
72
+ "Content-Type": "application/json",
73
+ };
74
+ if (this.options.apiKey) {
75
+ headers["Authorization"] = `Bearer ${this.options.apiKey}`;
76
+ }
77
+ const response = await fetch(endpoint, {
78
+ method: "POST",
79
+ headers,
80
+ body: JSON.stringify(payload),
81
+ signal: controller.signal,
82
+ });
83
+ clearTimeout(timeoutId);
84
+ if (response.ok) {
85
+ this.logger.debug(`Successfully updated events for ${payload.email}`);
86
+ return {
87
+ success: true,
88
+ statusCode: response.status,
89
+ };
90
+ }
91
+ else {
92
+ const errorText = await response.text().catch(() => "Unknown error");
93
+ this.logger.warn(`Failed to update events: ${response.status} ${errorText}`);
94
+ return {
95
+ success: false,
96
+ statusCode: response.status,
97
+ error: errorText,
98
+ };
99
+ }
100
+ }
101
+ catch (error) {
102
+ this.logger.error(`Network error updating events:`, error);
103
+ return {
104
+ success: false,
105
+ error: error instanceof Error ? error.message : String(error),
106
+ };
107
+ }
108
+ }
109
+ delay(ms) {
110
+ return new Promise((resolve) => setTimeout(resolve, ms));
111
+ }
112
+ }
@@ -0,0 +1,6 @@
1
+ import type { AnalyticsConfig, EnvironmentName } from "../core/types";
2
+ export declare function loadConfigFromFile(): Promise<Partial<AnalyticsConfig> | null>;
3
+ export declare function loadConfig(overrides?: Partial<AnalyticsConfig>): Promise<Partial<AnalyticsConfig>>;
4
+ export declare function detectEnvironment(): EnvironmentName;
5
+ export declare function checkEndpointHealth(url: string): Promise<boolean>;
6
+ export declare function resolveEndpoint(config: Partial<AnalyticsConfig>): Promise<string>;
@@ -0,0 +1,168 @@
1
+ import { getEnvironmentType } from "./helpers";
2
+ import { getLogger } from "./logging";
3
+ const CONFIG_FILE_NAME = "analyticsrc";
4
+ const DEFAULT_FALLBACK_ENDPOINT = "http://localhost:5001/events/add";
5
+ const logger = getLogger();
6
+ export async function loadConfigFromFile() {
7
+ const envType = getEnvironmentType();
8
+ if (envType !== "node") {
9
+ logger.warn("Config file loading is only supported in Node.js environment");
10
+ return null;
11
+ }
12
+ try {
13
+ const fs = await import("fs");
14
+ const path = await import("path");
15
+ const projectRoot = process.cwd();
16
+ const configPaths = [
17
+ path.resolve(projectRoot, `${CONFIG_FILE_NAME}.json`),
18
+ path.resolve(projectRoot, `${CONFIG_FILE_NAME}.ts`),
19
+ path.resolve(projectRoot, `${CONFIG_FILE_NAME}.js`),
20
+ ];
21
+ for (const configPath of configPaths) {
22
+ if (fs.existsSync(configPath)) {
23
+ logger.debug(`Loading config from: ${configPath}`);
24
+ try {
25
+ if (configPath.endsWith(".json")) {
26
+ const content = fs.readFileSync(configPath, "utf-8");
27
+ const config = JSON.parse(content);
28
+ logger.info(`Successfully loaded config from ${configPath}`);
29
+ return config;
30
+ }
31
+ else {
32
+ const config = await import(configPath);
33
+ logger.info(`Successfully loaded config from ${configPath}`);
34
+ return config.default || config;
35
+ }
36
+ }
37
+ catch (error) {
38
+ logger.error(`Failed to parse config from ${configPath}:`, error);
39
+ continue;
40
+ }
41
+ }
42
+ }
43
+ logger.warn(`No ${CONFIG_FILE_NAME} file found in project root: ${projectRoot}`);
44
+ return null;
45
+ }
46
+ catch (error) {
47
+ logger.error("Failed to load config from file:", error);
48
+ return null;
49
+ }
50
+ }
51
+ export async function loadConfig(overrides) {
52
+ const fileConfig = await loadConfigFromFile();
53
+ if (!fileConfig && !overrides) {
54
+ throw new Error("No configuration provided. Either create an analyticsrc.json file or provide config programmatically.");
55
+ }
56
+ return {
57
+ ...fileConfig,
58
+ ...overrides,
59
+ };
60
+ }
61
+ export function detectEnvironment() {
62
+ const envType = getEnvironmentType();
63
+ if (envType === "node") {
64
+ const nodeEnv = process.env.NODE_ENV?.toLowerCase();
65
+ if (nodeEnv) {
66
+ return nodeEnv;
67
+ }
68
+ return "development";
69
+ }
70
+ if (envType === "browser") {
71
+ const hostname = window.location.hostname;
72
+ if (hostname === "localhost" || hostname === "127.0.0.1") {
73
+ return "development";
74
+ }
75
+ if (hostname.includes("staging") || hostname.includes("stage")) {
76
+ return "staging";
77
+ }
78
+ if (hostname.includes("test") || hostname.includes("qa")) {
79
+ return "test";
80
+ }
81
+ return "production";
82
+ }
83
+ return "development";
84
+ }
85
+ export async function checkEndpointHealth(url) {
86
+ try {
87
+ const envType = getEnvironmentType();
88
+ if (envType === "node") {
89
+ const controller = new AbortController();
90
+ const timeout = setTimeout(() => controller.abort(), 5000);
91
+ try {
92
+ const response = await fetch(url.replace("/events/add", "/health"), {
93
+ method: "GET",
94
+ signal: controller.signal,
95
+ });
96
+ clearTimeout(timeout);
97
+ return response.ok || response.status === 404;
98
+ }
99
+ catch {
100
+ clearTimeout(timeout);
101
+ return false;
102
+ }
103
+ }
104
+ else if (envType === "browser") {
105
+ const controller = new AbortController();
106
+ const timeout = setTimeout(() => controller.abort(), 5000);
107
+ try {
108
+ const response = await fetch(url.replace("/events/add", "/health"), {
109
+ method: "GET",
110
+ mode: "no-cors",
111
+ signal: controller.signal,
112
+ });
113
+ clearTimeout(timeout);
114
+ return true;
115
+ }
116
+ catch {
117
+ clearTimeout(timeout);
118
+ return false;
119
+ }
120
+ }
121
+ return true;
122
+ }
123
+ catch {
124
+ return false;
125
+ }
126
+ }
127
+ export async function resolveEndpoint(config) {
128
+ const currentEnv = config.environment || detectEnvironment();
129
+ logger.info(`Identified environment: ${currentEnv}`);
130
+ if (typeof config.endpoint === "string") {
131
+ logger.debug(`Using single endpoint: ${config.endpoint}`);
132
+ return config.endpoint;
133
+ }
134
+ if (config.endpoint && typeof config.endpoint === "object") {
135
+ const endpoints = config.endpoint;
136
+ const envEndpoint = endpoints[currentEnv];
137
+ if (envEndpoint) {
138
+ logger.info(`Found endpoint for environment '${currentEnv}': ${envEndpoint}`);
139
+ const isHealthy = await checkEndpointHealth(envEndpoint);
140
+ if (isHealthy) {
141
+ logger.info(`Endpoint ${envEndpoint} is reachable`);
142
+ return envEndpoint;
143
+ }
144
+ else {
145
+ logger.warn(`Endpoint ${envEndpoint} is not reachable, falling back to ${DEFAULT_FALLBACK_ENDPOINT}`);
146
+ return DEFAULT_FALLBACK_ENDPOINT;
147
+ }
148
+ }
149
+ else {
150
+ const fallbackOrder = ["development", "local", "staging", "production"];
151
+ for (const fallbackEnv of fallbackOrder) {
152
+ if (endpoints[fallbackEnv]) {
153
+ logger.warn(`No endpoint configured for '${currentEnv}', using '${fallbackEnv}' endpoint: ${endpoints[fallbackEnv]}`);
154
+ return endpoints[fallbackEnv];
155
+ }
156
+ }
157
+ logger.warn(`No endpoint found in configuration, using fallback: ${DEFAULT_FALLBACK_ENDPOINT}`);
158
+ return DEFAULT_FALLBACK_ENDPOINT;
159
+ }
160
+ }
161
+ if (config.apiKey) {
162
+ const defaultEndpoint = "https://telemetry.armco.dev/events/add";
163
+ logger.info(`Using default Armco endpoint: ${defaultEndpoint}`);
164
+ return defaultEndpoint;
165
+ }
166
+ logger.warn(`No endpoint configured, using fallback: ${DEFAULT_FALLBACK_ENDPOINT}`);
167
+ return DEFAULT_FALLBACK_ENDPOINT;
168
+ }
@@ -0,0 +1,15 @@
1
+ import type { Environment } from "../core/types";
2
+ export declare function generateId(): string;
3
+ export declare function getEnvironmentType(): Environment;
4
+ export declare function getEnvironment(): string;
5
+ export declare function isDoNotTrackEnabled(): boolean;
6
+ export declare function isBrowser(): boolean;
7
+ export declare function areCookiesAvailable(): boolean;
8
+ export declare function isLocalStorageAvailable(): boolean;
9
+ export declare function debounce<T extends (...args: unknown[]) => void>(func: T, wait: number): (...args: Parameters<T>) => void;
10
+ export declare function throttle<T extends (...args: unknown[]) => void>(func: T, limit: number): (...args: Parameters<T>) => void;
11
+ export declare function deepClone<T>(obj: T): T;
12
+ export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
13
+ export declare function deepMerge<T extends Record<string, unknown>>(target: T, ...sources: Partial<T>[]): T;
14
+ export declare function getTimestamp(): Date;
15
+ export declare function isArmcoClient(name: string | null): boolean;
@@ -0,0 +1,148 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ export function generateId() {
3
+ return uuidv4();
4
+ }
5
+ export function getEnvironmentType() {
6
+ if (typeof window !== "undefined" && typeof window.document !== "undefined") {
7
+ return "browser";
8
+ }
9
+ else if (typeof process !== "undefined" &&
10
+ process.versions != null &&
11
+ process.versions.node != null) {
12
+ return "node";
13
+ }
14
+ else {
15
+ return "unknown";
16
+ }
17
+ }
18
+ export function getEnvironment() {
19
+ if (typeof process !== "undefined" && process.env && process.env.NODE_ENV) {
20
+ return process.env.NODE_ENV;
21
+ }
22
+ try {
23
+ const getImportMeta = new Function('return typeof import.meta !== "undefined" ? import.meta : null');
24
+ const meta = getImportMeta();
25
+ if (meta && meta.env && meta.env.MODE) {
26
+ return meta.env.MODE;
27
+ }
28
+ }
29
+ catch (e) {
30
+ }
31
+ return "development";
32
+ }
33
+ export function isDoNotTrackEnabled() {
34
+ if (typeof navigator !== "undefined" && "doNotTrack" in navigator) {
35
+ const doNotTrackValue = navigator.doNotTrack;
36
+ return doNotTrackValue === "1" || doNotTrackValue === "yes";
37
+ }
38
+ return false;
39
+ }
40
+ export function isBrowser() {
41
+ return getEnvironmentType() === "browser";
42
+ }
43
+ export function areCookiesAvailable() {
44
+ if (!isBrowser()) {
45
+ return false;
46
+ }
47
+ try {
48
+ document.cookie = "test=1";
49
+ const result = document.cookie.indexOf("test=") !== -1;
50
+ document.cookie = "test=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
51
+ return result;
52
+ }
53
+ catch {
54
+ return false;
55
+ }
56
+ }
57
+ export function isLocalStorageAvailable() {
58
+ if (!isBrowser()) {
59
+ return false;
60
+ }
61
+ try {
62
+ const test = "__storage_test__";
63
+ localStorage.setItem(test, test);
64
+ localStorage.removeItem(test);
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ export function debounce(func, wait) {
72
+ let timeout = null;
73
+ return function (...args) {
74
+ const later = () => {
75
+ timeout = null;
76
+ func.apply(this, args);
77
+ };
78
+ if (timeout !== null) {
79
+ clearTimeout(timeout);
80
+ }
81
+ timeout = setTimeout(later, wait);
82
+ };
83
+ }
84
+ export function throttle(func, limit) {
85
+ let inThrottle;
86
+ return function (...args) {
87
+ if (!inThrottle) {
88
+ func.apply(this, args);
89
+ inThrottle = true;
90
+ setTimeout(() => (inThrottle = false), limit);
91
+ }
92
+ };
93
+ }
94
+ export function deepClone(obj) {
95
+ if (obj === null || typeof obj !== "object") {
96
+ return obj;
97
+ }
98
+ if (obj instanceof Date) {
99
+ return new Date(obj.getTime());
100
+ }
101
+ if (obj instanceof Array) {
102
+ return obj.map((item) => deepClone(item));
103
+ }
104
+ if (obj instanceof Object) {
105
+ const cloned = {};
106
+ for (const key in obj) {
107
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
108
+ cloned[key] = deepClone(obj[key]);
109
+ }
110
+ }
111
+ return cloned;
112
+ }
113
+ return obj;
114
+ }
115
+ export function isPlainObject(value) {
116
+ return (typeof value === "object" &&
117
+ value !== null &&
118
+ value.constructor === Object &&
119
+ Object.prototype.toString.call(value) === "[object Object]");
120
+ }
121
+ export function deepMerge(target, ...sources) {
122
+ if (!sources.length) {
123
+ return target;
124
+ }
125
+ const source = sources.shift();
126
+ if (!source) {
127
+ return deepMerge(target, ...sources);
128
+ }
129
+ for (const key in source) {
130
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
131
+ const sourceValue = source[key];
132
+ const targetValue = target[key];
133
+ if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
134
+ target[key] = deepMerge({ ...targetValue }, sourceValue);
135
+ }
136
+ else if (sourceValue !== undefined) {
137
+ target[key] = sourceValue;
138
+ }
139
+ }
140
+ }
141
+ return deepMerge(target, ...sources);
142
+ }
143
+ export function getTimestamp() {
144
+ return new Date();
145
+ }
146
+ export function isArmcoClient(name) {
147
+ return !!(name && name.startsWith("@armco"));
148
+ }
@@ -0,0 +1,17 @@
1
+ import type { LogLevel } from "../core/types";
2
+ export declare class Logger {
3
+ private level;
4
+ private prefix;
5
+ constructor(level?: LogLevel, prefix?: string);
6
+ setLevel(level: LogLevel): void;
7
+ getLevel(): LogLevel;
8
+ debug(message: string, ...args: unknown[]): void;
9
+ info(message: string, ...args: unknown[]): void;
10
+ warn(message: string, ...args: unknown[]): void;
11
+ error(message: string, ...args: unknown[]): void;
12
+ private format;
13
+ private shouldLog;
14
+ }
15
+ export declare function getLogger(): Logger;
16
+ export declare function setLogger(logger: Logger): void;
17
+ export declare function createLogger(level?: LogLevel, prefix?: string): Logger;
@@ -0,0 +1,54 @@
1
+ export class Logger {
2
+ constructor(level = "info", prefix = "[ANALYTICS]") {
3
+ this.level = level;
4
+ this.prefix = prefix;
5
+ }
6
+ setLevel(level) {
7
+ this.level = level;
8
+ }
9
+ getLevel() {
10
+ return this.level;
11
+ }
12
+ debug(message, ...args) {
13
+ if (this.shouldLog("debug")) {
14
+ console.debug(this.format(message), ...args);
15
+ }
16
+ }
17
+ info(message, ...args) {
18
+ if (this.shouldLog("info")) {
19
+ console.info(this.format(message), ...args);
20
+ }
21
+ }
22
+ warn(message, ...args) {
23
+ if (this.shouldLog("warn")) {
24
+ console.warn(this.format(message), ...args);
25
+ }
26
+ }
27
+ error(message, ...args) {
28
+ if (this.shouldLog("error")) {
29
+ console.error(this.format(message), ...args);
30
+ }
31
+ }
32
+ format(message) {
33
+ return `${this.prefix} ${message}`;
34
+ }
35
+ shouldLog(messageLevel) {
36
+ if (this.level === "none") {
37
+ return false;
38
+ }
39
+ const levels = ["debug", "info", "warn", "error", "none"];
40
+ const currentLevelIndex = levels.indexOf(this.level);
41
+ const messageLevelIndex = levels.indexOf(messageLevel);
42
+ return messageLevelIndex >= currentLevelIndex;
43
+ }
44
+ }
45
+ let defaultLogger = new Logger();
46
+ export function getLogger() {
47
+ return defaultLogger;
48
+ }
49
+ export function setLogger(logger) {
50
+ defaultLogger = logger;
51
+ }
52
+ export function createLogger(level = "info", prefix = "[Analytics]") {
53
+ return new Logger(level, prefix);
54
+ }
@@ -0,0 +1,9 @@
1
+ import type { AnalyticsConfig, EventData, User, PageViewEvent, ClickEvent, FormEvent, ErrorEvent } from "../core/types";
2
+ export declare function validateConfig(config: unknown): AnalyticsConfig;
3
+ export declare function validateUser(user: unknown): User;
4
+ export declare function validatePageView(data: unknown): PageViewEvent;
5
+ export declare function validateClickEvent(data: unknown): ClickEvent;
6
+ export declare function validateFormEvent(data: unknown): FormEvent;
7
+ export declare function validateErrorEvent(data: unknown): ErrorEvent;
8
+ export declare function validateEventType(eventType: unknown): string;
9
+ export declare function sanitizeEventData(data: EventData): EventData;