@snovasys/usage-analytics-sdk 1.1.1 → 1.2.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/dist/clarity.cjs +15 -2
- package/dist/clarity.cjs.map +1 -1
- package/dist/clarity.js +15 -2
- package/dist/clarity.js.map +1 -1
- package/dist/index.cjs +16 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +16 -3
- package/dist/index.js.map +1 -1
- package/dist/usage-analytics.min.js +1 -1
- package/dist/usage-analytics.min.js.map +1 -1
- package/package.json +1 -1
package/dist/clarity.cjs
CHANGED
|
@@ -84,10 +84,23 @@ function createClarityListener(config) {
|
|
|
84
84
|
} catch {
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
|
-
identify(userId,
|
|
87
|
+
identify(userId, traits) {
|
|
88
88
|
try {
|
|
89
89
|
if (!isBrowser() || !ready || typeof window.clarity !== "function") return;
|
|
90
|
-
window.clarity("identify", userId
|
|
90
|
+
window.clarity("identify", userId);
|
|
91
|
+
if (traits) {
|
|
92
|
+
const piiKeys = /* @__PURE__ */ new Set(["email", "name", "displayName", "firstName", "lastName", "username", "phone", "address"]);
|
|
93
|
+
for (const [key, value] of Object.entries(traits)) {
|
|
94
|
+
if (piiKeys.has(key)) continue;
|
|
95
|
+
if (typeof value === "string") {
|
|
96
|
+
window.clarity("set", key, value);
|
|
97
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
98
|
+
window.clarity("set", key, String(value));
|
|
99
|
+
} else if (Array.isArray(value)) {
|
|
100
|
+
window.clarity("set", key, value);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
91
104
|
} catch {
|
|
92
105
|
}
|
|
93
106
|
}
|
package/dist/clarity.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../clarity.ts","../src/listeners/clarity.ts"],"sourcesContent":["/**\r\n * Entry point: @snovasys/usage-analytics-sdk/clarity\r\n * Export Clarity listener factory for manual wiring or use via built-in registry (id: clarity).\r\n */\r\nexport { createClarityListener } from './src/listeners/clarity.js';\r\nexport type { ClarityListenerConfig } from './src/listeners/clarity.js';\r\nexport type { AnalyticsListener, EventEnvelope } from './src/types/index.js';\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string,
|
|
1
|
+
{"version":3,"sources":["../clarity.ts","../src/listeners/clarity.ts"],"sourcesContent":["/**\r\n * Entry point: @snovasys/usage-analytics-sdk/clarity\r\n * Export Clarity listener factory for manual wiring or use via built-in registry (id: clarity).\r\n */\r\nexport { createClarityListener } from './src/listeners/clarity.js';\r\nexport type { ClarityListenerConfig } from './src/listeners/clarity.js';\r\nexport type { AnalyticsListener, EventEnvelope } from './src/types/index.js';\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n\r\n // PII Safety: Only pass the pseudonymous userId to Clarity's identify.\r\n // Do NOT forward PII fields (email, name, displayName) to a third-party service.\r\n // The userId (GUID) can be correlated back to a real user in your own system.\r\n window.clarity('identify', userId);\r\n\r\n // Forward only non-PII traits as Clarity custom tags.\r\n // Keys containing PII are stripped to stay compliant with GDPR / CCPA.\r\n if (traits) {\r\n const piiKeys = new Set(['email', 'name', 'displayName', 'firstName', 'lastName', 'username', 'phone', 'address']);\r\n for (const [key, value] of Object.entries(traits)) {\r\n if (piiKeys.has(key)) continue;\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOA,IAAM,sBAAsB;AAQ5B,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,kBAAkB,WAAkC;AAC3D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAAC,UAAU,GAAG;AAChB,cAAQ;AACR;AAAA,IACF;AACA,QAAI,OAAO,OAAO,YAAY,YAAY;AACxC,cAAQ;AACR;AAAA,IACF;AAMA,UAAM,IAAI;AACV,MAAE,SAAS,IAAI,EAAE,SAAS,KAAK,YAAa,MAAiB;AAC3D,YAAM,KAAK,EAAE,SAAS;AACtB,OAAC,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,IAAI;AAAA,IAC/B;AACA,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,MAAM,GAAG,mBAAmB,GAAG,SAAS;AAC/C,WAAO,SAAS,MAAM,QAAQ;AAC9B,WAAO,UAAU,MAAM,QAAQ;AAC/B,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAOO,SAAS,sBAAsB,QAAkD;AACtF,QAAM,YAAY,OAAO;AACzB,MAAI,QAAQ;AAEZ,SAAO;AAAA,IACL,MAAM,KAAK,KAA6B;AACtC,UAAI;AACF,cAAM,IAAK,OAAO;AAClB,cAAM,KAAK,EAAE;AACb,YAAI,CAAC,MAAM,CAAC,UAAU,EAAG;AACzB,cAAM,kBAAkB,EAAE;AAC1B,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAAC,UAAU,KAAK,CAAC,SAAS,OAAO,OAAO,YAAY,WAAY;AACpE,eAAO,QAAQ,SAAS,SAAS,IAAI;AACrC,YAAI,SAAS,cAAc,OAAO,OAAO,YAAY,YAAY;AAC/D,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,UAAU,GAAG;AAG9D,gBAAI,OAAO,UAAU,UAAU;AAC7B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAClE,qBAAO,QAAQ,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,YAC1C,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,SAAS,QAAgB,QAAwC;AAC/D,UAAI;AACF,YAAI,CAAC,UAAU,KAAK,CAAC,SAAS,OAAO,OAAO,YAAY,WAAY;AAKpE,eAAO,QAAQ,YAAY,MAAM;AAIjC,YAAI,QAAQ;AACV,gBAAM,UAAU,oBAAI,IAAI,CAAC,SAAS,QAAQ,eAAe,aAAa,YAAY,YAAY,SAAS,SAAS,CAAC;AACjH,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,gBAAI,QAAQ,IAAI,GAAG,EAAG;AACtB,gBAAI,OAAO,UAAU,UAAU;AAC7B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAClE,qBAAO,QAAQ,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,YAC1C,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/clarity.js
CHANGED
|
@@ -58,10 +58,23 @@ function createClarityListener(config) {
|
|
|
58
58
|
} catch {
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
|
-
identify(userId,
|
|
61
|
+
identify(userId, traits) {
|
|
62
62
|
try {
|
|
63
63
|
if (!isBrowser() || !ready || typeof window.clarity !== "function") return;
|
|
64
|
-
window.clarity("identify", userId
|
|
64
|
+
window.clarity("identify", userId);
|
|
65
|
+
if (traits) {
|
|
66
|
+
const piiKeys = /* @__PURE__ */ new Set(["email", "name", "displayName", "firstName", "lastName", "username", "phone", "address"]);
|
|
67
|
+
for (const [key, value] of Object.entries(traits)) {
|
|
68
|
+
if (piiKeys.has(key)) continue;
|
|
69
|
+
if (typeof value === "string") {
|
|
70
|
+
window.clarity("set", key, value);
|
|
71
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
72
|
+
window.clarity("set", key, String(value));
|
|
73
|
+
} else if (Array.isArray(value)) {
|
|
74
|
+
window.clarity("set", key, value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
65
78
|
} catch {
|
|
66
79
|
}
|
|
67
80
|
}
|
package/dist/clarity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/listeners/clarity.ts"],"sourcesContent":["import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string,
|
|
1
|
+
{"version":3,"sources":["../src/listeners/clarity.ts"],"sourcesContent":["import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n\r\n // PII Safety: Only pass the pseudonymous userId to Clarity's identify.\r\n // Do NOT forward PII fields (email, name, displayName) to a third-party service.\r\n // The userId (GUID) can be correlated back to a real user in your own system.\r\n window.clarity('identify', userId);\r\n\r\n // Forward only non-PII traits as Clarity custom tags.\r\n // Keys containing PII are stripped to stay compliant with GDPR / CCPA.\r\n if (traits) {\r\n const piiKeys = new Set(['email', 'name', 'displayName', 'firstName', 'lastName', 'username', 'phone', 'address']);\r\n for (const [key, value] of Object.entries(traits)) {\r\n if (piiKeys.has(key)) continue;\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n"],"mappings":";AAOA,IAAM,sBAAsB;AAQ5B,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,kBAAkB,WAAkC;AAC3D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAAC,UAAU,GAAG;AAChB,cAAQ;AACR;AAAA,IACF;AACA,QAAI,OAAO,OAAO,YAAY,YAAY;AACxC,cAAQ;AACR;AAAA,IACF;AAMA,UAAM,IAAI;AACV,MAAE,SAAS,IAAI,EAAE,SAAS,KAAK,YAAa,MAAiB;AAC3D,YAAM,KAAK,EAAE,SAAS;AACtB,OAAC,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,IAAI;AAAA,IAC/B;AACA,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,MAAM,GAAG,mBAAmB,GAAG,SAAS;AAC/C,WAAO,SAAS,MAAM,QAAQ;AAC9B,WAAO,UAAU,MAAM,QAAQ;AAC/B,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAOO,SAAS,sBAAsB,QAAkD;AACtF,QAAM,YAAY,OAAO;AACzB,MAAI,QAAQ;AAEZ,SAAO;AAAA,IACL,MAAM,KAAK,KAA6B;AACtC,UAAI;AACF,cAAM,IAAK,OAAO;AAClB,cAAM,KAAK,EAAE;AACb,YAAI,CAAC,MAAM,CAAC,UAAU,EAAG;AACzB,cAAM,kBAAkB,EAAE;AAC1B,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAAC,UAAU,KAAK,CAAC,SAAS,OAAO,OAAO,YAAY,WAAY;AACpE,eAAO,QAAQ,SAAS,SAAS,IAAI;AACrC,YAAI,SAAS,cAAc,OAAO,OAAO,YAAY,YAAY;AAC/D,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,UAAU,GAAG;AAG9D,gBAAI,OAAO,UAAU,UAAU;AAC7B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAClE,qBAAO,QAAQ,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,YAC1C,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,SAAS,QAAgB,QAAwC;AAC/D,UAAI;AACF,YAAI,CAAC,UAAU,KAAK,CAAC,SAAS,OAAO,OAAO,YAAY,WAAY;AAKpE,eAAO,QAAQ,YAAY,MAAM;AAIjC,YAAI,QAAQ;AACV,gBAAM,UAAU,oBAAI,IAAI,CAAC,SAAS,QAAQ,eAAe,aAAa,YAAY,YAAY,SAAS,SAAS,CAAC;AACjH,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,gBAAI,QAAQ,IAAI,GAAG,EAAG;AACtB,gBAAI,OAAO,UAAU,UAAU;AAC7B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAClE,qBAAO,QAAQ,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,YAC1C,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -291,10 +291,23 @@ function createClarityListener(config) {
|
|
|
291
291
|
} catch {
|
|
292
292
|
}
|
|
293
293
|
},
|
|
294
|
-
identify(userId,
|
|
294
|
+
identify(userId, traits) {
|
|
295
295
|
try {
|
|
296
296
|
if (!isBrowser() || !ready || typeof window.clarity !== "function") return;
|
|
297
|
-
window.clarity("identify", userId
|
|
297
|
+
window.clarity("identify", userId);
|
|
298
|
+
if (traits) {
|
|
299
|
+
const piiKeys = /* @__PURE__ */ new Set(["email", "name", "displayName", "firstName", "lastName", "username", "phone", "address"]);
|
|
300
|
+
for (const [key, value] of Object.entries(traits)) {
|
|
301
|
+
if (piiKeys.has(key)) continue;
|
|
302
|
+
if (typeof value === "string") {
|
|
303
|
+
window.clarity("set", key, value);
|
|
304
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
305
|
+
window.clarity("set", key, String(value));
|
|
306
|
+
} else if (Array.isArray(value)) {
|
|
307
|
+
window.clarity("set", key, value);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
298
311
|
} catch {
|
|
299
312
|
}
|
|
300
313
|
}
|
|
@@ -536,7 +549,7 @@ function validateConfig(options) {
|
|
|
536
549
|
}
|
|
537
550
|
|
|
538
551
|
// src/version.ts
|
|
539
|
-
var SDK_VERSION = "1.
|
|
552
|
+
var SDK_VERSION = "1.2.0";
|
|
540
553
|
|
|
541
554
|
// src/facade.ts
|
|
542
555
|
var enricherState = null;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts"],"sourcesContent":["export { init, track, identify, flush, reset, isInitialized } from './facade.js';\r\nexport type { AnalyticsInitConfig, AnalyticsListener, EventEnvelope, ListenerEntry, OnErrorContext, PreInitBehavior } from './types/index.js';\r\n","import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\r\n\r\nexport interface PreInitBufferOptions {\r\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\r\n mode: 'drop' | 'queue';\r\n maxSize: number;\r\n ttlMs: number;\r\n /** For testing: override browser check so queue works in Node. */\r\n isBrowserOverride?: boolean;\r\n}\r\n\r\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\r\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\r\n\r\n/**\r\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\r\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\r\n */\r\nexport class PreInitBuffer {\r\n private readonly options: PreInitBufferOptions;\r\n private readonly queue: BufferedItem[] = [];\r\n private readonly isBrowser: boolean;\r\n\r\n constructor(options: PreInitBufferOptions) {\r\n this.options = options;\r\n this.isBrowser =\r\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\r\n }\r\n\r\n /**\r\n * Push a track call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedTrack = {\r\n type: 'track',\r\n name,\r\n properties,\r\n timestamp,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n /**\r\n * Push an identify call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedIdentify = {\r\n type: 'identify',\r\n userId,\r\n traits,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n private push(item: BufferedItem): void {\r\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\r\n this.queue.shift(); // drop oldest (FIFO)\r\n }\r\n this.queue.push(item);\r\n }\r\n\r\n /**\r\n * Replay queued items: all identify first (order), then all track (order).\r\n * Items older than ttlMs are dropped. Then clear the queue.\r\n */\r\n flush(\r\n onTrack: FlushTrackHandler,\r\n onIdentify: FlushIdentifyHandler\r\n ): void {\r\n if (!this.isBrowser || this.queue.length === 0) {\r\n this.queue.length = 0;\r\n return;\r\n }\r\n const now = Date.now();\r\n const cutoff = now - this.options.ttlMs;\r\n const identifyItems = this.queue.filter(\r\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\r\n );\r\n const trackItems = this.queue.filter(\r\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\r\n );\r\n for (const item of identifyItems) {\r\n onIdentify(item.userId, item.traits);\r\n }\r\n for (const item of trackItems) {\r\n onTrack({\r\n name: item.name,\r\n properties: item.properties,\r\n timestamp: item.timestamp,\r\n });\r\n }\r\n this.queue.length = 0;\r\n }\r\n\r\n /** Number of items currently queued (for tests). */\r\n get length(): number {\r\n return this.queue.length;\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\n\r\nexport interface RegisteredListener {\r\n id?: string;\r\n listener: AnalyticsListener;\r\n}\r\n\r\n/**\r\n * Holds enabled listener instances. Populated at init.\r\n */\r\nexport class ListenerRegistry {\r\n private readonly listeners: RegisteredListener[] = [];\r\n\r\n add(id: string | undefined, listener: AnalyticsListener): void {\r\n this.listeners.push({ id, listener });\r\n }\r\n\r\n getAll(): RegisteredListener[] {\r\n return [...this.listeners];\r\n }\r\n\r\n clear(): void {\r\n this.listeners.length = 0;\r\n }\r\n}\r\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\r\n\r\n/**\r\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\r\n */\r\nexport function generateId(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID();\r\n }\r\n return fallbackUuidV4();\r\n}\r\n\r\nfunction fallbackUuidV4(): string {\r\n const hex = '0123456789abcdef';\r\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\r\n result = result.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return hex[v];\r\n });\r\n return result;\r\n}\r\n\r\n/**\r\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\r\n */\r\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\r\n if (providedSessionId) return providedSessionId;\r\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\r\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\r\n try {\r\n const existing = sessionStorage.getItem(key);\r\n if (existing) return existing;\r\n const newId = generateId();\r\n sessionStorage.setItem(key, newId);\r\n return newId;\r\n } catch {\r\n return generateId();\r\n }\r\n }\r\n return generateId();\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport { generateId, getOrCreateSessionId } from './session.js';\r\n\r\n/** Raw track payload from app (before enrichment). */\r\nexport interface TrackPayload {\r\n name: string;\r\n properties?: Record<string, unknown>;\r\n timestamp?: string;\r\n}\r\n\r\nexport interface EnricherState {\r\n appId: string;\r\n environment: string;\r\n sdkVersion: string;\r\n sessionId?: string;\r\n userId?: string;\r\n /** Provided at init or generated per session. */\r\n resolvedSessionId?: string;\r\n}\r\n\r\nconst ENVELOPE_VERSION = 1;\r\n\r\n/**\r\n * Creates enricher state from init config. Call once at init.\r\n */\r\nexport function createEnricherState(\r\n appId: string,\r\n environment: string,\r\n sdkVersion: string,\r\n sessionId?: string\r\n): EnricherState {\r\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\r\n return {\r\n appId,\r\n environment,\r\n sdkVersion,\r\n sessionId,\r\n resolvedSessionId,\r\n };\r\n}\r\n\r\n/**\r\n * Enrich a raw track payload into a full EventEnvelope.\r\n */\r\nexport function enrich(\r\n payload: TrackPayload,\r\n state: EnricherState\r\n): EventEnvelope {\r\n const timestamp = payload.timestamp ?? new Date().toISOString();\r\n const envelope: EventEnvelope = {\r\n name: payload.name,\r\n timestamp,\r\n eventId: generateId(),\r\n appId: state.appId,\r\n environment: state.environment,\r\n sdkVersion: state.sdkVersion,\r\n version: ENVELOPE_VERSION,\r\n };\r\n if (payload.properties != null) envelope.properties = payload.properties;\r\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\r\n if (state.userId) envelope.userId = state.userId;\r\n return envelope;\r\n}\r\n\r\n/**\r\n * Set userId (and optional traits) on state after identify. Cleared on reset.\r\n */\r\nexport function setUserId(state: EnricherState, userId: string): void {\r\n state.userId = userId;\r\n}\r\n\r\n/**\r\n * Clear userId from state (e.g. on reset).\r\n */\r\nexport function clearUserId(state: EnricherState): void {\r\n state.userId = undefined;\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport type { OnErrorContext } from '../types/config.js';\r\nimport type { ListenerRegistry } from './registry.js';\r\nimport type { EnricherState } from '../enricher/enricher.js';\r\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\r\n\r\nexport interface DispatcherDeps {\r\n registry: ListenerRegistry;\r\n getEnricherState: () => EnricherState;\r\n onError?: (error: Error, context?: OnErrorContext) => void;\r\n}\r\n\r\n/**\r\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\r\n */\r\nexport function dispatchTrack(\r\n deps: DispatcherDeps,\r\n payload: TrackPayload\r\n): void {\r\n const state = deps.getEnricherState();\r\n const envelope = enrich(payload, state);\r\n dispatchEnvelope(deps, envelope, 'track');\r\n}\r\n\r\n/**\r\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\r\n */\r\nfunction dispatchEnvelope(\r\n deps: DispatcherDeps,\r\n envelope: EventEnvelope,\r\n kind: 'track'\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n try {\r\n listener.track(envelope);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Call identify on all listeners that implement it (try/catch per listener).\r\n */\r\nexport function dispatchIdentify(\r\n deps: DispatcherDeps,\r\n userId: string,\r\n traits?: Record<string, unknown>\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n if (typeof listener.identify !== 'function') continue;\r\n try {\r\n listener.identify(userId, traits);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\n/**\r\n * No-op listener: implements the interface with no side effects.\r\n * Use when analytics is disabled or in tests.\r\n */\r\nexport function createNoopListener(): AnalyticsListener {\r\n return {\r\n init(_config: unknown): void {\r\n // no-op\r\n },\r\n track(_envelope: EventEnvelope): void {\r\n // no-op\r\n },\r\n };\r\n}\r\n\r\n/** Singleton no-op listener instance. */\r\nexport const noopListener: AnalyticsListener = createNoopListener();\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, _traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('identify', userId, undefined, undefined, undefined);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface GAListenerConfig {\r\n measurementId: string;\r\n}\r\n\r\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\r\n\r\ndeclare global {\r\n interface Window {\r\n dataLayer?: unknown[];\r\n gtag?: (...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadGtagScript(measurementId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (window.gtag) {\r\n window.gtag('config', measurementId);\r\n resolve();\r\n return;\r\n }\r\n window.dataLayer = window.dataLayer ?? [];\r\n const gtag = (...args: unknown[]) => {\r\n window.dataLayer!.push(args);\r\n };\r\n window.gtag = gtag;\r\n gtag('js', new Date());\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\r\n script.onload = () => {\r\n gtag('config', measurementId);\r\n resolve();\r\n };\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Google Analytics 4 via gtag.\r\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\r\n const measurementId = config.measurementId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as GAListenerConfig;\r\n const id = c.measurementId;\r\n if (!id || !isBrowser()) return;\r\n await loadGtagScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\r\n const params = envelope.properties ?? {};\r\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\r\n window.gtag('event', envelope.name, params);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || typeof window.gtag !== 'function') return;\r\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface CustomApiListenerConfig {\r\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\r\n endpoint: string;\r\n /** Optional API key or bearer token. */\r\n apiKey?: string;\r\n /** Optional: batch size before sending. Default 1 (no batching). */\r\n batchSize?: number;\r\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\r\n flushIntervalMs?: number;\r\n}\r\n\r\nconst DEFAULT_BATCH_SIZE = 1;\r\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\r\n\r\n/**\r\n * Listener that POSTs events to a configurable endpoint.\r\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\r\n */\r\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\r\n const endpoint = config.endpoint;\r\n const apiKey = config.apiKey;\r\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\r\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\r\n\r\n let batch: EventEnvelope[] = [];\r\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n function scheduleFlush(): void {\r\n if (flushTimer != null) return;\r\n flushTimer = setTimeout(() => {\r\n flushTimer = null;\r\n sendBatch();\r\n }, flushIntervalMs);\r\n }\r\n\r\n function sendBatch(): void {\r\n if (batch.length === 0) return;\r\n const toSend = [...batch];\r\n batch = [];\r\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\r\n if (typeof fetch !== 'undefined') {\r\n fetch(endpoint, {\r\n method: 'POST',\r\n headers,\r\n body,\r\n keepalive: true,\r\n }).catch(() => {});\r\n }\r\n }\r\n\r\n return {\r\n init(_config: unknown): void {\r\n // config was passed at createCustomApiListener\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n batch.push(envelope);\r\n if (batch.length >= batchSize) {\r\n sendBatch();\r\n } else if (flushIntervalMs > 0) {\r\n scheduleFlush();\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n flush(): Promise<void> {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n sendBatch();\r\n return Promise.resolve();\r\n },\r\n teardown(): void {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n batch.length = 0;\r\n },\r\n };\r\n}\r\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\r\nimport type { AnalyticsListener } from '../types/listener.js';\r\nimport { createNoopListener } from './noop.js';\r\nimport { createClarityListener } from './clarity.js';\r\nimport { createGAListener } from './ga.js';\r\nimport { createCustomApiListener } from './custom-api.js';\r\n\r\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\r\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\r\n\r\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\r\n noop: () => createNoopListener(),\r\n clarity: (config) => createClarityListener(config as { projectId: string }),\r\n ga: (config) => createGAListener(config as { measurementId: string }),\r\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\r\n};\r\n\r\n/**\r\n * Returns the built-in listener factory for the given id, or undefined if unknown.\r\n */\r\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\r\n return BUILT_IN_FACTORIES[id];\r\n}\r\n\r\nexport interface BuildListenersFromRemoteConfigOptions {\r\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\r\n factories?: Record<string, ListenerFactory>;\r\n /** Called when a remote listener id has no factory (unknown id). */\r\n onError?: (error: Error, context?: { listenerId?: string }) => void;\r\n}\r\n\r\n/**\r\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\r\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\r\n */\r\nexport function buildListenersFromRemoteConfig(\r\n remote: RemoteAnalyticsConfig,\r\n options?: BuildListenersFromRemoteConfigOptions\r\n): ListenerEntry[] {\r\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\r\n const onError = options?.onError;\r\n const entries: ListenerEntry[] = [];\r\n\r\n if (!Array.isArray(remote.listeners)) {\r\n return entries;\r\n }\r\n\r\n for (const entry of remote.listeners) {\r\n if (entry.enabled === false) continue;\r\n const id = entry.id;\r\n if (id == null || typeof id !== 'string') continue;\r\n\r\n const factory = factories[id];\r\n if (factory == null) {\r\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\r\n continue;\r\n }\r\n\r\n let listener: AnalyticsListener;\r\n try {\r\n listener = factory(entry.config);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n continue;\r\n }\r\n\r\n entries.push({\r\n id,\r\n enabled: entry.enabled,\r\n listener,\r\n config: entry.config,\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n PreInitBehavior,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport type { AnalyticsListener } from './types/listener.js';\r\n\r\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\r\n\r\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\r\n if (entry == null || typeof entry !== 'object') return false;\r\n const o = entry as Record<string, unknown>;\r\n return (\r\n typeof o.id === 'string' &&\r\n o.id.trim() !== '' &&\r\n 'config' in o &&\r\n !('listener' in o && o.listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Validate init config. Throws with clear message on invalid config.\r\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\r\n */\r\nexport function validateConfig(options: AnalyticsInitConfig): void {\r\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\r\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\r\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\r\n }\r\n if (options.listeners != null && !Array.isArray(options.listeners)) {\r\n throw new Error('Analytics init failed: listeners must be an array.');\r\n }\r\n const behavior = options.preInitBehavior ?? 'drop';\r\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\r\n throw new Error(\r\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\r\n );\r\n }\r\n if (behavior === 'queue') {\r\n const maxSize = options.queueMaxSize ?? 100;\r\n const ttlMs = options.queueTtlMs ?? 5000;\r\n if (typeof maxSize !== 'number' || maxSize <= 0) {\r\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\r\n }\r\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\r\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\r\n }\r\n }\r\n if (useRemoteConfig) {\r\n return;\r\n }\r\n const listeners = options.listeners ?? [];\r\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\r\n if (isRemoteShape) {\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as RemoteListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\r\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\r\n }\r\n }\r\n return;\r\n }\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as ListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (entry.listener == null || typeof entry.listener !== 'object') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\r\n }\r\n const listener = entry.listener as AnalyticsListener;\r\n if (typeof listener.track !== 'function') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\r\n }\r\n }\r\n}\r\n","/** SDK version; set at build time or from package.json. */\r\nexport const SDK_VERSION = '1.0.0';\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n OnErrorContext,\r\n RemoteAnalyticsConfig,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\r\nimport { ListenerRegistry } from './dispatcher/registry.js';\r\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\r\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\r\nimport {\r\n createEnricherState,\r\n setUserId,\r\n clearUserId,\r\n type EnricherState,\r\n} from './enricher/enricher.js';\r\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\r\nimport { validateConfig } from './validate-config.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nlet enricherState: EnricherState | null = null;\r\nlet registry: ListenerRegistry | null = null;\r\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\r\nlet preInitBuffer: PreInitBuffer | null = null;\r\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\r\nlet initialized = false;\r\n\r\n/** Default buffer before init: queue mode so events before init can be replayed. */\r\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\r\n if (preInitBuffer == null) {\r\n preInitBuffer = new PreInitBuffer({\r\n mode: 'queue',\r\n maxSize: 100,\r\n ttlMs: 5000,\r\n });\r\n }\r\n return preInitBuffer;\r\n}\r\n\r\nfunction getEnricherState(): EnricherState {\r\n if (enricherState == null) {\r\n throw new Error('Analytics not initialized.');\r\n }\r\n return enricherState;\r\n}\r\n\r\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\r\n if (options.getConfig) {\r\n return options.getConfig();\r\n }\r\n if (options.configUrl) {\r\n const fetchFn = options.fetch ?? globalThis.fetch;\r\n const res = await fetchFn(options.configUrl);\r\n if (!res.ok) {\r\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\r\n }\r\n const json = await res.json();\r\n if (json == null || typeof json !== 'object') {\r\n throw new Error('Analytics config response must be a JSON object.');\r\n }\r\n return json as RemoteAnalyticsConfig;\r\n }\r\n return null;\r\n}\r\n\r\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\r\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\r\n return listeners.every(\r\n (entry): entry is RemoteListenerEntry =>\r\n entry != null &&\r\n typeof entry === 'object' &&\r\n typeof (entry as RemoteListenerEntry).id === 'string' &&\r\n 'config' in entry &&\r\n !('listener' in entry && (entry as ListenerEntry).listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Initialize the SDK. Idempotent; calling again re-applies config.\r\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\r\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\r\n */\r\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\r\n validateConfig(options);\r\n\r\n let appId: string;\r\n let environment: string;\r\n let listeners: ListenerEntry[];\r\n\r\n const remote = await fetchRemoteConfig(options);\r\n if (remote != null) {\r\n appId = (options.appId ?? '').toString().trim();\r\n environment = options.environment ?? 'production';\r\n if (!appId) {\r\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\r\n }\r\n listeners = buildListenersFromRemoteConfig(remote, {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n });\r\n } else {\r\n appId = options.appId.trim();\r\n environment = options.environment ?? 'production';\r\n const rawListeners = options.listeners ?? [];\r\n if (isInlineRemoteShape(rawListeners)) {\r\n listeners = buildListenersFromRemoteConfig(\r\n { listeners: rawListeners },\r\n {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n }\r\n );\r\n } else {\r\n listeners = rawListeners as ListenerEntry[];\r\n }\r\n }\r\n\r\n const preInitBehavior = options.preInitBehavior ?? 'drop';\r\n const queueMaxSize = options.queueMaxSize ?? 100;\r\n const queueTtlMs = options.queueTtlMs ?? 5000;\r\n onError = options.onError;\r\n\r\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\r\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\r\n\r\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\r\n registry = new ListenerRegistry();\r\n\r\n for (const entry of listeners) {\r\n if (entry.enabled === false) continue;\r\n try {\r\n const initResult = entry.listener.init(entry.config);\r\n if (initResult instanceof Promise) {\r\n await initResult;\r\n }\r\n registry.add(entry.id, entry.listener);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: entry.id });\r\n }\r\n }\r\n\r\n const deps: DispatcherDeps = {\r\n registry,\r\n getEnricherState,\r\n onError,\r\n };\r\n\r\n if (preInitBuffer) {\r\n if (preInitBehavior === 'queue') {\r\n preInitBuffer.flush(\r\n (payload) => dispatchTrack(deps, payload),\r\n (userId, traits) => {\r\n setUserId(enricherState!, userId);\r\n dispatchIdentify(deps, userId, traits);\r\n }\r\n );\r\n } else {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n preInitBuffer = null;\r\n }\r\n\r\n initialized = true;\r\n}\r\n\r\n/**\r\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\r\n return;\r\n }\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchTrack(deps, event);\r\n}\r\n\r\n/**\r\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\r\n return;\r\n }\r\n if (enricherState) setUserId(enricherState, userId);\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchIdentify(deps, userId, traits);\r\n}\r\n\r\n/**\r\n * Flush listeners that batch (e.g. before page unload).\r\n */\r\nexport async function flush(): Promise<void> {\r\n if (!initialized || registry == null) return;\r\n const promises: Promise<void>[] = [];\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.flush === 'function') {\r\n const result = listener.flush();\r\n if (result instanceof Promise) promises.push(result);\r\n }\r\n }\r\n await Promise.all(promises);\r\n}\r\n\r\n/**\r\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\r\n */\r\nexport async function reset(): Promise<void> {\r\n if (preInitBuffer) {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n if (enricherState) clearUserId(enricherState);\r\n if (registry) {\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.teardown === 'function') {\r\n try {\r\n const result = listener.teardown();\r\n if (result instanceof Promise) await result;\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, {});\r\n }\r\n }\r\n }\r\n registry.clear();\r\n }\r\n preInitBuffer = null;\r\n enricherState = null;\r\n registry = null;\r\n onError = undefined;\r\n initialized = false;\r\n}\r\n\r\nexport function isInitialized(): boolean {\r\n return initialized;\r\n}\r\n"],"mappings":"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","names":["registry","onError","isBrowser","onError"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts"],"sourcesContent":["export { init, track, identify, flush, reset, isInitialized } from './facade.js';\r\nexport type { AnalyticsInitConfig, AnalyticsListener, EventEnvelope, ListenerEntry, OnErrorContext, PreInitBehavior } from './types/index.js';\r\n","import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\r\n\r\nexport interface PreInitBufferOptions {\r\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\r\n mode: 'drop' | 'queue';\r\n maxSize: number;\r\n ttlMs: number;\r\n /** For testing: override browser check so queue works in Node. */\r\n isBrowserOverride?: boolean;\r\n}\r\n\r\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\r\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\r\n\r\n/**\r\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\r\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\r\n */\r\nexport class PreInitBuffer {\r\n private readonly options: PreInitBufferOptions;\r\n private readonly queue: BufferedItem[] = [];\r\n private readonly isBrowser: boolean;\r\n\r\n constructor(options: PreInitBufferOptions) {\r\n this.options = options;\r\n this.isBrowser =\r\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\r\n }\r\n\r\n /**\r\n * Push a track call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedTrack = {\r\n type: 'track',\r\n name,\r\n properties,\r\n timestamp,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n /**\r\n * Push an identify call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedIdentify = {\r\n type: 'identify',\r\n userId,\r\n traits,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n private push(item: BufferedItem): void {\r\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\r\n this.queue.shift(); // drop oldest (FIFO)\r\n }\r\n this.queue.push(item);\r\n }\r\n\r\n /**\r\n * Replay queued items: all identify first (order), then all track (order).\r\n * Items older than ttlMs are dropped. Then clear the queue.\r\n */\r\n flush(\r\n onTrack: FlushTrackHandler,\r\n onIdentify: FlushIdentifyHandler\r\n ): void {\r\n if (!this.isBrowser || this.queue.length === 0) {\r\n this.queue.length = 0;\r\n return;\r\n }\r\n const now = Date.now();\r\n const cutoff = now - this.options.ttlMs;\r\n const identifyItems = this.queue.filter(\r\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\r\n );\r\n const trackItems = this.queue.filter(\r\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\r\n );\r\n for (const item of identifyItems) {\r\n onIdentify(item.userId, item.traits);\r\n }\r\n for (const item of trackItems) {\r\n onTrack({\r\n name: item.name,\r\n properties: item.properties,\r\n timestamp: item.timestamp,\r\n });\r\n }\r\n this.queue.length = 0;\r\n }\r\n\r\n /** Number of items currently queued (for tests). */\r\n get length(): number {\r\n return this.queue.length;\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\n\r\nexport interface RegisteredListener {\r\n id?: string;\r\n listener: AnalyticsListener;\r\n}\r\n\r\n/**\r\n * Holds enabled listener instances. Populated at init.\r\n */\r\nexport class ListenerRegistry {\r\n private readonly listeners: RegisteredListener[] = [];\r\n\r\n add(id: string | undefined, listener: AnalyticsListener): void {\r\n this.listeners.push({ id, listener });\r\n }\r\n\r\n getAll(): RegisteredListener[] {\r\n return [...this.listeners];\r\n }\r\n\r\n clear(): void {\r\n this.listeners.length = 0;\r\n }\r\n}\r\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\r\n\r\n/**\r\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\r\n */\r\nexport function generateId(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID();\r\n }\r\n return fallbackUuidV4();\r\n}\r\n\r\nfunction fallbackUuidV4(): string {\r\n const hex = '0123456789abcdef';\r\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\r\n result = result.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return hex[v];\r\n });\r\n return result;\r\n}\r\n\r\n/**\r\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\r\n */\r\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\r\n if (providedSessionId) return providedSessionId;\r\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\r\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\r\n try {\r\n const existing = sessionStorage.getItem(key);\r\n if (existing) return existing;\r\n const newId = generateId();\r\n sessionStorage.setItem(key, newId);\r\n return newId;\r\n } catch {\r\n return generateId();\r\n }\r\n }\r\n return generateId();\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport { generateId, getOrCreateSessionId } from './session.js';\r\n\r\n/** Raw track payload from app (before enrichment). */\r\nexport interface TrackPayload {\r\n name: string;\r\n properties?: Record<string, unknown>;\r\n timestamp?: string;\r\n}\r\n\r\nexport interface EnricherState {\r\n appId: string;\r\n environment: string;\r\n sdkVersion: string;\r\n sessionId?: string;\r\n userId?: string;\r\n /** Provided at init or generated per session. */\r\n resolvedSessionId?: string;\r\n}\r\n\r\nconst ENVELOPE_VERSION = 1;\r\n\r\n/**\r\n * Creates enricher state from init config. Call once at init.\r\n */\r\nexport function createEnricherState(\r\n appId: string,\r\n environment: string,\r\n sdkVersion: string,\r\n sessionId?: string\r\n): EnricherState {\r\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\r\n return {\r\n appId,\r\n environment,\r\n sdkVersion,\r\n sessionId,\r\n resolvedSessionId,\r\n };\r\n}\r\n\r\n/**\r\n * Enrich a raw track payload into a full EventEnvelope.\r\n */\r\nexport function enrich(\r\n payload: TrackPayload,\r\n state: EnricherState\r\n): EventEnvelope {\r\n const timestamp = payload.timestamp ?? new Date().toISOString();\r\n const envelope: EventEnvelope = {\r\n name: payload.name,\r\n timestamp,\r\n eventId: generateId(),\r\n appId: state.appId,\r\n environment: state.environment,\r\n sdkVersion: state.sdkVersion,\r\n version: ENVELOPE_VERSION,\r\n };\r\n if (payload.properties != null) envelope.properties = payload.properties;\r\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\r\n if (state.userId) envelope.userId = state.userId;\r\n return envelope;\r\n}\r\n\r\n/**\r\n * Set userId (and optional traits) on state after identify. Cleared on reset.\r\n */\r\nexport function setUserId(state: EnricherState, userId: string): void {\r\n state.userId = userId;\r\n}\r\n\r\n/**\r\n * Clear userId from state (e.g. on reset).\r\n */\r\nexport function clearUserId(state: EnricherState): void {\r\n state.userId = undefined;\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport type { OnErrorContext } from '../types/config.js';\r\nimport type { ListenerRegistry } from './registry.js';\r\nimport type { EnricherState } from '../enricher/enricher.js';\r\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\r\n\r\nexport interface DispatcherDeps {\r\n registry: ListenerRegistry;\r\n getEnricherState: () => EnricherState;\r\n onError?: (error: Error, context?: OnErrorContext) => void;\r\n}\r\n\r\n/**\r\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\r\n */\r\nexport function dispatchTrack(\r\n deps: DispatcherDeps,\r\n payload: TrackPayload\r\n): void {\r\n const state = deps.getEnricherState();\r\n const envelope = enrich(payload, state);\r\n dispatchEnvelope(deps, envelope, 'track');\r\n}\r\n\r\n/**\r\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\r\n */\r\nfunction dispatchEnvelope(\r\n deps: DispatcherDeps,\r\n envelope: EventEnvelope,\r\n kind: 'track'\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n try {\r\n listener.track(envelope);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Call identify on all listeners that implement it (try/catch per listener).\r\n */\r\nexport function dispatchIdentify(\r\n deps: DispatcherDeps,\r\n userId: string,\r\n traits?: Record<string, unknown>\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n if (typeof listener.identify !== 'function') continue;\r\n try {\r\n listener.identify(userId, traits);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\n/**\r\n * No-op listener: implements the interface with no side effects.\r\n * Use when analytics is disabled or in tests.\r\n */\r\nexport function createNoopListener(): AnalyticsListener {\r\n return {\r\n init(_config: unknown): void {\r\n // no-op\r\n },\r\n track(_envelope: EventEnvelope): void {\r\n // no-op\r\n },\r\n };\r\n}\r\n\r\n/** Singleton no-op listener instance. */\r\nexport const noopListener: AnalyticsListener = createNoopListener();\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n\r\n // PII Safety: Only pass the pseudonymous userId to Clarity's identify.\r\n // Do NOT forward PII fields (email, name, displayName) to a third-party service.\r\n // The userId (GUID) can be correlated back to a real user in your own system.\r\n window.clarity('identify', userId);\r\n\r\n // Forward only non-PII traits as Clarity custom tags.\r\n // Keys containing PII are stripped to stay compliant with GDPR / CCPA.\r\n if (traits) {\r\n const piiKeys = new Set(['email', 'name', 'displayName', 'firstName', 'lastName', 'username', 'phone', 'address']);\r\n for (const [key, value] of Object.entries(traits)) {\r\n if (piiKeys.has(key)) continue;\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface GAListenerConfig {\r\n measurementId: string;\r\n}\r\n\r\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\r\n\r\ndeclare global {\r\n interface Window {\r\n dataLayer?: unknown[];\r\n gtag?: (...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadGtagScript(measurementId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (window.gtag) {\r\n window.gtag('config', measurementId);\r\n resolve();\r\n return;\r\n }\r\n window.dataLayer = window.dataLayer ?? [];\r\n const gtag = (...args: unknown[]) => {\r\n window.dataLayer!.push(args);\r\n };\r\n window.gtag = gtag;\r\n gtag('js', new Date());\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\r\n script.onload = () => {\r\n gtag('config', measurementId);\r\n resolve();\r\n };\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Google Analytics 4 via gtag.\r\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\r\n const measurementId = config.measurementId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as GAListenerConfig;\r\n const id = c.measurementId;\r\n if (!id || !isBrowser()) return;\r\n await loadGtagScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\r\n const params = envelope.properties ?? {};\r\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\r\n window.gtag('event', envelope.name, params);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || typeof window.gtag !== 'function') return;\r\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface CustomApiListenerConfig {\r\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\r\n endpoint: string;\r\n /** Optional API key or bearer token. */\r\n apiKey?: string;\r\n /** Optional: batch size before sending. Default 1 (no batching). */\r\n batchSize?: number;\r\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\r\n flushIntervalMs?: number;\r\n}\r\n\r\nconst DEFAULT_BATCH_SIZE = 1;\r\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\r\n\r\n/**\r\n * Listener that POSTs events to a configurable endpoint.\r\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\r\n */\r\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\r\n const endpoint = config.endpoint;\r\n const apiKey = config.apiKey;\r\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\r\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\r\n\r\n let batch: EventEnvelope[] = [];\r\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n function scheduleFlush(): void {\r\n if (flushTimer != null) return;\r\n flushTimer = setTimeout(() => {\r\n flushTimer = null;\r\n sendBatch();\r\n }, flushIntervalMs);\r\n }\r\n\r\n function sendBatch(): void {\r\n if (batch.length === 0) return;\r\n const toSend = [...batch];\r\n batch = [];\r\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\r\n if (typeof fetch !== 'undefined') {\r\n fetch(endpoint, {\r\n method: 'POST',\r\n headers,\r\n body,\r\n keepalive: true,\r\n }).catch(() => {});\r\n }\r\n }\r\n\r\n return {\r\n init(_config: unknown): void {\r\n // config was passed at createCustomApiListener\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n batch.push(envelope);\r\n if (batch.length >= batchSize) {\r\n sendBatch();\r\n } else if (flushIntervalMs > 0) {\r\n scheduleFlush();\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n flush(): Promise<void> {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n sendBatch();\r\n return Promise.resolve();\r\n },\r\n teardown(): void {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n batch.length = 0;\r\n },\r\n };\r\n}\r\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\r\nimport type { AnalyticsListener } from '../types/listener.js';\r\nimport { createNoopListener } from './noop.js';\r\nimport { createClarityListener } from './clarity.js';\r\nimport { createGAListener } from './ga.js';\r\nimport { createCustomApiListener } from './custom-api.js';\r\n\r\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\r\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\r\n\r\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\r\n noop: () => createNoopListener(),\r\n clarity: (config) => createClarityListener(config as { projectId: string }),\r\n ga: (config) => createGAListener(config as { measurementId: string }),\r\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\r\n};\r\n\r\n/**\r\n * Returns the built-in listener factory for the given id, or undefined if unknown.\r\n */\r\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\r\n return BUILT_IN_FACTORIES[id];\r\n}\r\n\r\nexport interface BuildListenersFromRemoteConfigOptions {\r\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\r\n factories?: Record<string, ListenerFactory>;\r\n /** Called when a remote listener id has no factory (unknown id). */\r\n onError?: (error: Error, context?: { listenerId?: string }) => void;\r\n}\r\n\r\n/**\r\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\r\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\r\n */\r\nexport function buildListenersFromRemoteConfig(\r\n remote: RemoteAnalyticsConfig,\r\n options?: BuildListenersFromRemoteConfigOptions\r\n): ListenerEntry[] {\r\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\r\n const onError = options?.onError;\r\n const entries: ListenerEntry[] = [];\r\n\r\n if (!Array.isArray(remote.listeners)) {\r\n return entries;\r\n }\r\n\r\n for (const entry of remote.listeners) {\r\n if (entry.enabled === false) continue;\r\n const id = entry.id;\r\n if (id == null || typeof id !== 'string') continue;\r\n\r\n const factory = factories[id];\r\n if (factory == null) {\r\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\r\n continue;\r\n }\r\n\r\n let listener: AnalyticsListener;\r\n try {\r\n listener = factory(entry.config);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n continue;\r\n }\r\n\r\n entries.push({\r\n id,\r\n enabled: entry.enabled,\r\n listener,\r\n config: entry.config,\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n PreInitBehavior,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport type { AnalyticsListener } from './types/listener.js';\r\n\r\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\r\n\r\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\r\n if (entry == null || typeof entry !== 'object') return false;\r\n const o = entry as Record<string, unknown>;\r\n return (\r\n typeof o.id === 'string' &&\r\n o.id.trim() !== '' &&\r\n 'config' in o &&\r\n !('listener' in o && o.listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Validate init config. Throws with clear message on invalid config.\r\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\r\n */\r\nexport function validateConfig(options: AnalyticsInitConfig): void {\r\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\r\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\r\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\r\n }\r\n if (options.listeners != null && !Array.isArray(options.listeners)) {\r\n throw new Error('Analytics init failed: listeners must be an array.');\r\n }\r\n const behavior = options.preInitBehavior ?? 'drop';\r\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\r\n throw new Error(\r\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\r\n );\r\n }\r\n if (behavior === 'queue') {\r\n const maxSize = options.queueMaxSize ?? 100;\r\n const ttlMs = options.queueTtlMs ?? 5000;\r\n if (typeof maxSize !== 'number' || maxSize <= 0) {\r\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\r\n }\r\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\r\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\r\n }\r\n }\r\n if (useRemoteConfig) {\r\n return;\r\n }\r\n const listeners = options.listeners ?? [];\r\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\r\n if (isRemoteShape) {\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as RemoteListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\r\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\r\n }\r\n }\r\n return;\r\n }\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as ListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (entry.listener == null || typeof entry.listener !== 'object') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\r\n }\r\n const listener = entry.listener as AnalyticsListener;\r\n if (typeof listener.track !== 'function') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\r\n }\r\n }\r\n}\r\n","/** SDK version; set at build time or from package.json. */\r\nexport const SDK_VERSION = '1.2.0';\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n OnErrorContext,\r\n RemoteAnalyticsConfig,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\r\nimport { ListenerRegistry } from './dispatcher/registry.js';\r\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\r\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\r\nimport {\r\n createEnricherState,\r\n setUserId,\r\n clearUserId,\r\n type EnricherState,\r\n} from './enricher/enricher.js';\r\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\r\nimport { validateConfig } from './validate-config.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nlet enricherState: EnricherState | null = null;\r\nlet registry: ListenerRegistry | null = null;\r\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\r\nlet preInitBuffer: PreInitBuffer | null = null;\r\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\r\nlet initialized = false;\r\n\r\n/** Default buffer before init: queue mode so events before init can be replayed. */\r\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\r\n if (preInitBuffer == null) {\r\n preInitBuffer = new PreInitBuffer({\r\n mode: 'queue',\r\n maxSize: 100,\r\n ttlMs: 5000,\r\n });\r\n }\r\n return preInitBuffer;\r\n}\r\n\r\nfunction getEnricherState(): EnricherState {\r\n if (enricherState == null) {\r\n throw new Error('Analytics not initialized.');\r\n }\r\n return enricherState;\r\n}\r\n\r\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\r\n if (options.getConfig) {\r\n return options.getConfig();\r\n }\r\n if (options.configUrl) {\r\n const fetchFn = options.fetch ?? globalThis.fetch;\r\n const res = await fetchFn(options.configUrl);\r\n if (!res.ok) {\r\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\r\n }\r\n const json = await res.json();\r\n if (json == null || typeof json !== 'object') {\r\n throw new Error('Analytics config response must be a JSON object.');\r\n }\r\n return json as RemoteAnalyticsConfig;\r\n }\r\n return null;\r\n}\r\n\r\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\r\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\r\n return listeners.every(\r\n (entry): entry is RemoteListenerEntry =>\r\n entry != null &&\r\n typeof entry === 'object' &&\r\n typeof (entry as RemoteListenerEntry).id === 'string' &&\r\n 'config' in entry &&\r\n !('listener' in entry && (entry as ListenerEntry).listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Initialize the SDK. Idempotent; calling again re-applies config.\r\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\r\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\r\n */\r\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\r\n validateConfig(options);\r\n\r\n let appId: string;\r\n let environment: string;\r\n let listeners: ListenerEntry[];\r\n\r\n const remote = await fetchRemoteConfig(options);\r\n if (remote != null) {\r\n appId = (options.appId ?? '').toString().trim();\r\n environment = options.environment ?? 'production';\r\n if (!appId) {\r\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\r\n }\r\n listeners = buildListenersFromRemoteConfig(remote, {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n });\r\n } else {\r\n appId = options.appId.trim();\r\n environment = options.environment ?? 'production';\r\n const rawListeners = options.listeners ?? [];\r\n if (isInlineRemoteShape(rawListeners)) {\r\n listeners = buildListenersFromRemoteConfig(\r\n { listeners: rawListeners },\r\n {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n }\r\n );\r\n } else {\r\n listeners = rawListeners as ListenerEntry[];\r\n }\r\n }\r\n\r\n const preInitBehavior = options.preInitBehavior ?? 'drop';\r\n const queueMaxSize = options.queueMaxSize ?? 100;\r\n const queueTtlMs = options.queueTtlMs ?? 5000;\r\n onError = options.onError;\r\n\r\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\r\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\r\n\r\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\r\n registry = new ListenerRegistry();\r\n\r\n for (const entry of listeners) {\r\n if (entry.enabled === false) continue;\r\n try {\r\n const initResult = entry.listener.init(entry.config);\r\n if (initResult instanceof Promise) {\r\n await initResult;\r\n }\r\n registry.add(entry.id, entry.listener);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: entry.id });\r\n }\r\n }\r\n\r\n const deps: DispatcherDeps = {\r\n registry,\r\n getEnricherState,\r\n onError,\r\n };\r\n\r\n if (preInitBuffer) {\r\n if (preInitBehavior === 'queue') {\r\n preInitBuffer.flush(\r\n (payload) => dispatchTrack(deps, payload),\r\n (userId, traits) => {\r\n setUserId(enricherState!, userId);\r\n dispatchIdentify(deps, userId, traits);\r\n }\r\n );\r\n } else {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n preInitBuffer = null;\r\n }\r\n\r\n initialized = true;\r\n}\r\n\r\n/**\r\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\r\n return;\r\n }\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchTrack(deps, event);\r\n}\r\n\r\n/**\r\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\r\n return;\r\n }\r\n if (enricherState) setUserId(enricherState, userId);\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchIdentify(deps, userId, traits);\r\n}\r\n\r\n/**\r\n * Flush listeners that batch (e.g. before page unload).\r\n */\r\nexport async function flush(): Promise<void> {\r\n if (!initialized || registry == null) return;\r\n const promises: Promise<void>[] = [];\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.flush === 'function') {\r\n const result = listener.flush();\r\n if (result instanceof Promise) promises.push(result);\r\n }\r\n }\r\n await Promise.all(promises);\r\n}\r\n\r\n/**\r\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\r\n */\r\nexport async function reset(): Promise<void> {\r\n if (preInitBuffer) {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n if (enricherState) clearUserId(enricherState);\r\n if (registry) {\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.teardown === 'function') {\r\n try {\r\n const result = listener.teardown();\r\n if (result instanceof Promise) await result;\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, {});\r\n }\r\n }\r\n }\r\n registry.clear();\r\n }\r\n preInitBuffer = null;\r\n enricherState = null;\r\n registry = null;\r\n onError = undefined;\r\n initialized = false;\r\n}\r\n\r\nexport function isInitialized(): boolean {\r\n return initialized;\r\n}\r\n"],"mappings":"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","names":["registry","onError","isBrowser","onError"]}
|
package/dist/index.js
CHANGED
|
@@ -260,10 +260,23 @@ function createClarityListener(config) {
|
|
|
260
260
|
} catch {
|
|
261
261
|
}
|
|
262
262
|
},
|
|
263
|
-
identify(userId,
|
|
263
|
+
identify(userId, traits) {
|
|
264
264
|
try {
|
|
265
265
|
if (!isBrowser() || !ready || typeof window.clarity !== "function") return;
|
|
266
|
-
window.clarity("identify", userId
|
|
266
|
+
window.clarity("identify", userId);
|
|
267
|
+
if (traits) {
|
|
268
|
+
const piiKeys = /* @__PURE__ */ new Set(["email", "name", "displayName", "firstName", "lastName", "username", "phone", "address"]);
|
|
269
|
+
for (const [key, value] of Object.entries(traits)) {
|
|
270
|
+
if (piiKeys.has(key)) continue;
|
|
271
|
+
if (typeof value === "string") {
|
|
272
|
+
window.clarity("set", key, value);
|
|
273
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
274
|
+
window.clarity("set", key, String(value));
|
|
275
|
+
} else if (Array.isArray(value)) {
|
|
276
|
+
window.clarity("set", key, value);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
267
280
|
} catch {
|
|
268
281
|
}
|
|
269
282
|
}
|
|
@@ -505,7 +518,7 @@ function validateConfig(options) {
|
|
|
505
518
|
}
|
|
506
519
|
|
|
507
520
|
// src/version.ts
|
|
508
|
-
var SDK_VERSION = "1.
|
|
521
|
+
var SDK_VERSION = "1.2.0";
|
|
509
522
|
|
|
510
523
|
// src/facade.ts
|
|
511
524
|
var enricherState = null;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts"],"sourcesContent":["import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\r\n\r\nexport interface PreInitBufferOptions {\r\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\r\n mode: 'drop' | 'queue';\r\n maxSize: number;\r\n ttlMs: number;\r\n /** For testing: override browser check so queue works in Node. */\r\n isBrowserOverride?: boolean;\r\n}\r\n\r\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\r\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\r\n\r\n/**\r\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\r\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\r\n */\r\nexport class PreInitBuffer {\r\n private readonly options: PreInitBufferOptions;\r\n private readonly queue: BufferedItem[] = [];\r\n private readonly isBrowser: boolean;\r\n\r\n constructor(options: PreInitBufferOptions) {\r\n this.options = options;\r\n this.isBrowser =\r\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\r\n }\r\n\r\n /**\r\n * Push a track call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedTrack = {\r\n type: 'track',\r\n name,\r\n properties,\r\n timestamp,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n /**\r\n * Push an identify call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedIdentify = {\r\n type: 'identify',\r\n userId,\r\n traits,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n private push(item: BufferedItem): void {\r\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\r\n this.queue.shift(); // drop oldest (FIFO)\r\n }\r\n this.queue.push(item);\r\n }\r\n\r\n /**\r\n * Replay queued items: all identify first (order), then all track (order).\r\n * Items older than ttlMs are dropped. Then clear the queue.\r\n */\r\n flush(\r\n onTrack: FlushTrackHandler,\r\n onIdentify: FlushIdentifyHandler\r\n ): void {\r\n if (!this.isBrowser || this.queue.length === 0) {\r\n this.queue.length = 0;\r\n return;\r\n }\r\n const now = Date.now();\r\n const cutoff = now - this.options.ttlMs;\r\n const identifyItems = this.queue.filter(\r\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\r\n );\r\n const trackItems = this.queue.filter(\r\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\r\n );\r\n for (const item of identifyItems) {\r\n onIdentify(item.userId, item.traits);\r\n }\r\n for (const item of trackItems) {\r\n onTrack({\r\n name: item.name,\r\n properties: item.properties,\r\n timestamp: item.timestamp,\r\n });\r\n }\r\n this.queue.length = 0;\r\n }\r\n\r\n /** Number of items currently queued (for tests). */\r\n get length(): number {\r\n return this.queue.length;\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\n\r\nexport interface RegisteredListener {\r\n id?: string;\r\n listener: AnalyticsListener;\r\n}\r\n\r\n/**\r\n * Holds enabled listener instances. Populated at init.\r\n */\r\nexport class ListenerRegistry {\r\n private readonly listeners: RegisteredListener[] = [];\r\n\r\n add(id: string | undefined, listener: AnalyticsListener): void {\r\n this.listeners.push({ id, listener });\r\n }\r\n\r\n getAll(): RegisteredListener[] {\r\n return [...this.listeners];\r\n }\r\n\r\n clear(): void {\r\n this.listeners.length = 0;\r\n }\r\n}\r\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\r\n\r\n/**\r\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\r\n */\r\nexport function generateId(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID();\r\n }\r\n return fallbackUuidV4();\r\n}\r\n\r\nfunction fallbackUuidV4(): string {\r\n const hex = '0123456789abcdef';\r\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\r\n result = result.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return hex[v];\r\n });\r\n return result;\r\n}\r\n\r\n/**\r\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\r\n */\r\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\r\n if (providedSessionId) return providedSessionId;\r\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\r\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\r\n try {\r\n const existing = sessionStorage.getItem(key);\r\n if (existing) return existing;\r\n const newId = generateId();\r\n sessionStorage.setItem(key, newId);\r\n return newId;\r\n } catch {\r\n return generateId();\r\n }\r\n }\r\n return generateId();\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport { generateId, getOrCreateSessionId } from './session.js';\r\n\r\n/** Raw track payload from app (before enrichment). */\r\nexport interface TrackPayload {\r\n name: string;\r\n properties?: Record<string, unknown>;\r\n timestamp?: string;\r\n}\r\n\r\nexport interface EnricherState {\r\n appId: string;\r\n environment: string;\r\n sdkVersion: string;\r\n sessionId?: string;\r\n userId?: string;\r\n /** Provided at init or generated per session. */\r\n resolvedSessionId?: string;\r\n}\r\n\r\nconst ENVELOPE_VERSION = 1;\r\n\r\n/**\r\n * Creates enricher state from init config. Call once at init.\r\n */\r\nexport function createEnricherState(\r\n appId: string,\r\n environment: string,\r\n sdkVersion: string,\r\n sessionId?: string\r\n): EnricherState {\r\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\r\n return {\r\n appId,\r\n environment,\r\n sdkVersion,\r\n sessionId,\r\n resolvedSessionId,\r\n };\r\n}\r\n\r\n/**\r\n * Enrich a raw track payload into a full EventEnvelope.\r\n */\r\nexport function enrich(\r\n payload: TrackPayload,\r\n state: EnricherState\r\n): EventEnvelope {\r\n const timestamp = payload.timestamp ?? new Date().toISOString();\r\n const envelope: EventEnvelope = {\r\n name: payload.name,\r\n timestamp,\r\n eventId: generateId(),\r\n appId: state.appId,\r\n environment: state.environment,\r\n sdkVersion: state.sdkVersion,\r\n version: ENVELOPE_VERSION,\r\n };\r\n if (payload.properties != null) envelope.properties = payload.properties;\r\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\r\n if (state.userId) envelope.userId = state.userId;\r\n return envelope;\r\n}\r\n\r\n/**\r\n * Set userId (and optional traits) on state after identify. Cleared on reset.\r\n */\r\nexport function setUserId(state: EnricherState, userId: string): void {\r\n state.userId = userId;\r\n}\r\n\r\n/**\r\n * Clear userId from state (e.g. on reset).\r\n */\r\nexport function clearUserId(state: EnricherState): void {\r\n state.userId = undefined;\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport type { OnErrorContext } from '../types/config.js';\r\nimport type { ListenerRegistry } from './registry.js';\r\nimport type { EnricherState } from '../enricher/enricher.js';\r\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\r\n\r\nexport interface DispatcherDeps {\r\n registry: ListenerRegistry;\r\n getEnricherState: () => EnricherState;\r\n onError?: (error: Error, context?: OnErrorContext) => void;\r\n}\r\n\r\n/**\r\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\r\n */\r\nexport function dispatchTrack(\r\n deps: DispatcherDeps,\r\n payload: TrackPayload\r\n): void {\r\n const state = deps.getEnricherState();\r\n const envelope = enrich(payload, state);\r\n dispatchEnvelope(deps, envelope, 'track');\r\n}\r\n\r\n/**\r\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\r\n */\r\nfunction dispatchEnvelope(\r\n deps: DispatcherDeps,\r\n envelope: EventEnvelope,\r\n kind: 'track'\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n try {\r\n listener.track(envelope);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Call identify on all listeners that implement it (try/catch per listener).\r\n */\r\nexport function dispatchIdentify(\r\n deps: DispatcherDeps,\r\n userId: string,\r\n traits?: Record<string, unknown>\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n if (typeof listener.identify !== 'function') continue;\r\n try {\r\n listener.identify(userId, traits);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\n/**\r\n * No-op listener: implements the interface with no side effects.\r\n * Use when analytics is disabled or in tests.\r\n */\r\nexport function createNoopListener(): AnalyticsListener {\r\n return {\r\n init(_config: unknown): void {\r\n // no-op\r\n },\r\n track(_envelope: EventEnvelope): void {\r\n // no-op\r\n },\r\n };\r\n}\r\n\r\n/** Singleton no-op listener instance. */\r\nexport const noopListener: AnalyticsListener = createNoopListener();\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, _traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('identify', userId, undefined, undefined, undefined);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface GAListenerConfig {\r\n measurementId: string;\r\n}\r\n\r\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\r\n\r\ndeclare global {\r\n interface Window {\r\n dataLayer?: unknown[];\r\n gtag?: (...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadGtagScript(measurementId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (window.gtag) {\r\n window.gtag('config', measurementId);\r\n resolve();\r\n return;\r\n }\r\n window.dataLayer = window.dataLayer ?? [];\r\n const gtag = (...args: unknown[]) => {\r\n window.dataLayer!.push(args);\r\n };\r\n window.gtag = gtag;\r\n gtag('js', new Date());\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\r\n script.onload = () => {\r\n gtag('config', measurementId);\r\n resolve();\r\n };\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Google Analytics 4 via gtag.\r\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\r\n const measurementId = config.measurementId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as GAListenerConfig;\r\n const id = c.measurementId;\r\n if (!id || !isBrowser()) return;\r\n await loadGtagScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\r\n const params = envelope.properties ?? {};\r\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\r\n window.gtag('event', envelope.name, params);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || typeof window.gtag !== 'function') return;\r\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface CustomApiListenerConfig {\r\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\r\n endpoint: string;\r\n /** Optional API key or bearer token. */\r\n apiKey?: string;\r\n /** Optional: batch size before sending. Default 1 (no batching). */\r\n batchSize?: number;\r\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\r\n flushIntervalMs?: number;\r\n}\r\n\r\nconst DEFAULT_BATCH_SIZE = 1;\r\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\r\n\r\n/**\r\n * Listener that POSTs events to a configurable endpoint.\r\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\r\n */\r\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\r\n const endpoint = config.endpoint;\r\n const apiKey = config.apiKey;\r\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\r\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\r\n\r\n let batch: EventEnvelope[] = [];\r\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n function scheduleFlush(): void {\r\n if (flushTimer != null) return;\r\n flushTimer = setTimeout(() => {\r\n flushTimer = null;\r\n sendBatch();\r\n }, flushIntervalMs);\r\n }\r\n\r\n function sendBatch(): void {\r\n if (batch.length === 0) return;\r\n const toSend = [...batch];\r\n batch = [];\r\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\r\n if (typeof fetch !== 'undefined') {\r\n fetch(endpoint, {\r\n method: 'POST',\r\n headers,\r\n body,\r\n keepalive: true,\r\n }).catch(() => {});\r\n }\r\n }\r\n\r\n return {\r\n init(_config: unknown): void {\r\n // config was passed at createCustomApiListener\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n batch.push(envelope);\r\n if (batch.length >= batchSize) {\r\n sendBatch();\r\n } else if (flushIntervalMs > 0) {\r\n scheduleFlush();\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n flush(): Promise<void> {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n sendBatch();\r\n return Promise.resolve();\r\n },\r\n teardown(): void {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n batch.length = 0;\r\n },\r\n };\r\n}\r\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\r\nimport type { AnalyticsListener } from '../types/listener.js';\r\nimport { createNoopListener } from './noop.js';\r\nimport { createClarityListener } from './clarity.js';\r\nimport { createGAListener } from './ga.js';\r\nimport { createCustomApiListener } from './custom-api.js';\r\n\r\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\r\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\r\n\r\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\r\n noop: () => createNoopListener(),\r\n clarity: (config) => createClarityListener(config as { projectId: string }),\r\n ga: (config) => createGAListener(config as { measurementId: string }),\r\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\r\n};\r\n\r\n/**\r\n * Returns the built-in listener factory for the given id, or undefined if unknown.\r\n */\r\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\r\n return BUILT_IN_FACTORIES[id];\r\n}\r\n\r\nexport interface BuildListenersFromRemoteConfigOptions {\r\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\r\n factories?: Record<string, ListenerFactory>;\r\n /** Called when a remote listener id has no factory (unknown id). */\r\n onError?: (error: Error, context?: { listenerId?: string }) => void;\r\n}\r\n\r\n/**\r\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\r\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\r\n */\r\nexport function buildListenersFromRemoteConfig(\r\n remote: RemoteAnalyticsConfig,\r\n options?: BuildListenersFromRemoteConfigOptions\r\n): ListenerEntry[] {\r\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\r\n const onError = options?.onError;\r\n const entries: ListenerEntry[] = [];\r\n\r\n if (!Array.isArray(remote.listeners)) {\r\n return entries;\r\n }\r\n\r\n for (const entry of remote.listeners) {\r\n if (entry.enabled === false) continue;\r\n const id = entry.id;\r\n if (id == null || typeof id !== 'string') continue;\r\n\r\n const factory = factories[id];\r\n if (factory == null) {\r\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\r\n continue;\r\n }\r\n\r\n let listener: AnalyticsListener;\r\n try {\r\n listener = factory(entry.config);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n continue;\r\n }\r\n\r\n entries.push({\r\n id,\r\n enabled: entry.enabled,\r\n listener,\r\n config: entry.config,\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n PreInitBehavior,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport type { AnalyticsListener } from './types/listener.js';\r\n\r\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\r\n\r\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\r\n if (entry == null || typeof entry !== 'object') return false;\r\n const o = entry as Record<string, unknown>;\r\n return (\r\n typeof o.id === 'string' &&\r\n o.id.trim() !== '' &&\r\n 'config' in o &&\r\n !('listener' in o && o.listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Validate init config. Throws with clear message on invalid config.\r\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\r\n */\r\nexport function validateConfig(options: AnalyticsInitConfig): void {\r\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\r\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\r\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\r\n }\r\n if (options.listeners != null && !Array.isArray(options.listeners)) {\r\n throw new Error('Analytics init failed: listeners must be an array.');\r\n }\r\n const behavior = options.preInitBehavior ?? 'drop';\r\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\r\n throw new Error(\r\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\r\n );\r\n }\r\n if (behavior === 'queue') {\r\n const maxSize = options.queueMaxSize ?? 100;\r\n const ttlMs = options.queueTtlMs ?? 5000;\r\n if (typeof maxSize !== 'number' || maxSize <= 0) {\r\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\r\n }\r\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\r\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\r\n }\r\n }\r\n if (useRemoteConfig) {\r\n return;\r\n }\r\n const listeners = options.listeners ?? [];\r\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\r\n if (isRemoteShape) {\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as RemoteListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\r\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\r\n }\r\n }\r\n return;\r\n }\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as ListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (entry.listener == null || typeof entry.listener !== 'object') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\r\n }\r\n const listener = entry.listener as AnalyticsListener;\r\n if (typeof listener.track !== 'function') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\r\n }\r\n }\r\n}\r\n","/** SDK version; set at build time or from package.json. */\r\nexport const SDK_VERSION = '1.0.0';\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n OnErrorContext,\r\n RemoteAnalyticsConfig,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\r\nimport { ListenerRegistry } from './dispatcher/registry.js';\r\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\r\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\r\nimport {\r\n createEnricherState,\r\n setUserId,\r\n clearUserId,\r\n type EnricherState,\r\n} from './enricher/enricher.js';\r\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\r\nimport { validateConfig } from './validate-config.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nlet enricherState: EnricherState | null = null;\r\nlet registry: ListenerRegistry | null = null;\r\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\r\nlet preInitBuffer: PreInitBuffer | null = null;\r\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\r\nlet initialized = false;\r\n\r\n/** Default buffer before init: queue mode so events before init can be replayed. */\r\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\r\n if (preInitBuffer == null) {\r\n preInitBuffer = new PreInitBuffer({\r\n mode: 'queue',\r\n maxSize: 100,\r\n ttlMs: 5000,\r\n });\r\n }\r\n return preInitBuffer;\r\n}\r\n\r\nfunction getEnricherState(): EnricherState {\r\n if (enricherState == null) {\r\n throw new Error('Analytics not initialized.');\r\n }\r\n return enricherState;\r\n}\r\n\r\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\r\n if (options.getConfig) {\r\n return options.getConfig();\r\n }\r\n if (options.configUrl) {\r\n const fetchFn = options.fetch ?? globalThis.fetch;\r\n const res = await fetchFn(options.configUrl);\r\n if (!res.ok) {\r\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\r\n }\r\n const json = await res.json();\r\n if (json == null || typeof json !== 'object') {\r\n throw new Error('Analytics config response must be a JSON object.');\r\n }\r\n return json as RemoteAnalyticsConfig;\r\n }\r\n return null;\r\n}\r\n\r\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\r\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\r\n return listeners.every(\r\n (entry): entry is RemoteListenerEntry =>\r\n entry != null &&\r\n typeof entry === 'object' &&\r\n typeof (entry as RemoteListenerEntry).id === 'string' &&\r\n 'config' in entry &&\r\n !('listener' in entry && (entry as ListenerEntry).listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Initialize the SDK. Idempotent; calling again re-applies config.\r\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\r\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\r\n */\r\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\r\n validateConfig(options);\r\n\r\n let appId: string;\r\n let environment: string;\r\n let listeners: ListenerEntry[];\r\n\r\n const remote = await fetchRemoteConfig(options);\r\n if (remote != null) {\r\n appId = (options.appId ?? '').toString().trim();\r\n environment = options.environment ?? 'production';\r\n if (!appId) {\r\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\r\n }\r\n listeners = buildListenersFromRemoteConfig(remote, {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n });\r\n } else {\r\n appId = options.appId.trim();\r\n environment = options.environment ?? 'production';\r\n const rawListeners = options.listeners ?? [];\r\n if (isInlineRemoteShape(rawListeners)) {\r\n listeners = buildListenersFromRemoteConfig(\r\n { listeners: rawListeners },\r\n {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n }\r\n );\r\n } else {\r\n listeners = rawListeners as ListenerEntry[];\r\n }\r\n }\r\n\r\n const preInitBehavior = options.preInitBehavior ?? 'drop';\r\n const queueMaxSize = options.queueMaxSize ?? 100;\r\n const queueTtlMs = options.queueTtlMs ?? 5000;\r\n onError = options.onError;\r\n\r\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\r\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\r\n\r\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\r\n registry = new ListenerRegistry();\r\n\r\n for (const entry of listeners) {\r\n if (entry.enabled === false) continue;\r\n try {\r\n const initResult = entry.listener.init(entry.config);\r\n if (initResult instanceof Promise) {\r\n await initResult;\r\n }\r\n registry.add(entry.id, entry.listener);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: entry.id });\r\n }\r\n }\r\n\r\n const deps: DispatcherDeps = {\r\n registry,\r\n getEnricherState,\r\n onError,\r\n };\r\n\r\n if (preInitBuffer) {\r\n if (preInitBehavior === 'queue') {\r\n preInitBuffer.flush(\r\n (payload) => dispatchTrack(deps, payload),\r\n (userId, traits) => {\r\n setUserId(enricherState!, userId);\r\n dispatchIdentify(deps, userId, traits);\r\n }\r\n );\r\n } else {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n preInitBuffer = null;\r\n }\r\n\r\n initialized = true;\r\n}\r\n\r\n/**\r\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\r\n return;\r\n }\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchTrack(deps, event);\r\n}\r\n\r\n/**\r\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\r\n return;\r\n }\r\n if (enricherState) setUserId(enricherState, userId);\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchIdentify(deps, userId, traits);\r\n}\r\n\r\n/**\r\n * Flush listeners that batch (e.g. before page unload).\r\n */\r\nexport async function flush(): Promise<void> {\r\n if (!initialized || registry == null) return;\r\n const promises: Promise<void>[] = [];\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.flush === 'function') {\r\n const result = listener.flush();\r\n if (result instanceof Promise) promises.push(result);\r\n }\r\n }\r\n await Promise.all(promises);\r\n}\r\n\r\n/**\r\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\r\n */\r\nexport async function reset(): Promise<void> {\r\n if (preInitBuffer) {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n if (enricherState) clearUserId(enricherState);\r\n if (registry) {\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.teardown === 'function') {\r\n try {\r\n const result = listener.teardown();\r\n if (result instanceof Promise) await result;\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, {});\r\n }\r\n }\r\n }\r\n registry.clear();\r\n }\r\n preInitBuffer = null;\r\n enricherState = null;\r\n registry = null;\r\n onError = undefined;\r\n initialized = false;\r\n}\r\n\r\nexport function isInitialized(): boolean {\r\n return initialized;\r\n}\r\n"],"mappings":"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","names":["registry","onError","isBrowser","onError"]}
|
|
1
|
+
{"version":3,"sources":["../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts"],"sourcesContent":["import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\r\n\r\nexport interface PreInitBufferOptions {\r\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\r\n mode: 'drop' | 'queue';\r\n maxSize: number;\r\n ttlMs: number;\r\n /** For testing: override browser check so queue works in Node. */\r\n isBrowserOverride?: boolean;\r\n}\r\n\r\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\r\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\r\n\r\n/**\r\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\r\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\r\n */\r\nexport class PreInitBuffer {\r\n private readonly options: PreInitBufferOptions;\r\n private readonly queue: BufferedItem[] = [];\r\n private readonly isBrowser: boolean;\r\n\r\n constructor(options: PreInitBufferOptions) {\r\n this.options = options;\r\n this.isBrowser =\r\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\r\n }\r\n\r\n /**\r\n * Push a track call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedTrack = {\r\n type: 'track',\r\n name,\r\n properties,\r\n timestamp,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n /**\r\n * Push an identify call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedIdentify = {\r\n type: 'identify',\r\n userId,\r\n traits,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n private push(item: BufferedItem): void {\r\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\r\n this.queue.shift(); // drop oldest (FIFO)\r\n }\r\n this.queue.push(item);\r\n }\r\n\r\n /**\r\n * Replay queued items: all identify first (order), then all track (order).\r\n * Items older than ttlMs are dropped. Then clear the queue.\r\n */\r\n flush(\r\n onTrack: FlushTrackHandler,\r\n onIdentify: FlushIdentifyHandler\r\n ): void {\r\n if (!this.isBrowser || this.queue.length === 0) {\r\n this.queue.length = 0;\r\n return;\r\n }\r\n const now = Date.now();\r\n const cutoff = now - this.options.ttlMs;\r\n const identifyItems = this.queue.filter(\r\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\r\n );\r\n const trackItems = this.queue.filter(\r\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\r\n );\r\n for (const item of identifyItems) {\r\n onIdentify(item.userId, item.traits);\r\n }\r\n for (const item of trackItems) {\r\n onTrack({\r\n name: item.name,\r\n properties: item.properties,\r\n timestamp: item.timestamp,\r\n });\r\n }\r\n this.queue.length = 0;\r\n }\r\n\r\n /** Number of items currently queued (for tests). */\r\n get length(): number {\r\n return this.queue.length;\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\n\r\nexport interface RegisteredListener {\r\n id?: string;\r\n listener: AnalyticsListener;\r\n}\r\n\r\n/**\r\n * Holds enabled listener instances. Populated at init.\r\n */\r\nexport class ListenerRegistry {\r\n private readonly listeners: RegisteredListener[] = [];\r\n\r\n add(id: string | undefined, listener: AnalyticsListener): void {\r\n this.listeners.push({ id, listener });\r\n }\r\n\r\n getAll(): RegisteredListener[] {\r\n return [...this.listeners];\r\n }\r\n\r\n clear(): void {\r\n this.listeners.length = 0;\r\n }\r\n}\r\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\r\n\r\n/**\r\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\r\n */\r\nexport function generateId(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID();\r\n }\r\n return fallbackUuidV4();\r\n}\r\n\r\nfunction fallbackUuidV4(): string {\r\n const hex = '0123456789abcdef';\r\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\r\n result = result.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return hex[v];\r\n });\r\n return result;\r\n}\r\n\r\n/**\r\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\r\n */\r\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\r\n if (providedSessionId) return providedSessionId;\r\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\r\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\r\n try {\r\n const existing = sessionStorage.getItem(key);\r\n if (existing) return existing;\r\n const newId = generateId();\r\n sessionStorage.setItem(key, newId);\r\n return newId;\r\n } catch {\r\n return generateId();\r\n }\r\n }\r\n return generateId();\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport { generateId, getOrCreateSessionId } from './session.js';\r\n\r\n/** Raw track payload from app (before enrichment). */\r\nexport interface TrackPayload {\r\n name: string;\r\n properties?: Record<string, unknown>;\r\n timestamp?: string;\r\n}\r\n\r\nexport interface EnricherState {\r\n appId: string;\r\n environment: string;\r\n sdkVersion: string;\r\n sessionId?: string;\r\n userId?: string;\r\n /** Provided at init or generated per session. */\r\n resolvedSessionId?: string;\r\n}\r\n\r\nconst ENVELOPE_VERSION = 1;\r\n\r\n/**\r\n * Creates enricher state from init config. Call once at init.\r\n */\r\nexport function createEnricherState(\r\n appId: string,\r\n environment: string,\r\n sdkVersion: string,\r\n sessionId?: string\r\n): EnricherState {\r\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\r\n return {\r\n appId,\r\n environment,\r\n sdkVersion,\r\n sessionId,\r\n resolvedSessionId,\r\n };\r\n}\r\n\r\n/**\r\n * Enrich a raw track payload into a full EventEnvelope.\r\n */\r\nexport function enrich(\r\n payload: TrackPayload,\r\n state: EnricherState\r\n): EventEnvelope {\r\n const timestamp = payload.timestamp ?? new Date().toISOString();\r\n const envelope: EventEnvelope = {\r\n name: payload.name,\r\n timestamp,\r\n eventId: generateId(),\r\n appId: state.appId,\r\n environment: state.environment,\r\n sdkVersion: state.sdkVersion,\r\n version: ENVELOPE_VERSION,\r\n };\r\n if (payload.properties != null) envelope.properties = payload.properties;\r\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\r\n if (state.userId) envelope.userId = state.userId;\r\n return envelope;\r\n}\r\n\r\n/**\r\n * Set userId (and optional traits) on state after identify. Cleared on reset.\r\n */\r\nexport function setUserId(state: EnricherState, userId: string): void {\r\n state.userId = userId;\r\n}\r\n\r\n/**\r\n * Clear userId from state (e.g. on reset).\r\n */\r\nexport function clearUserId(state: EnricherState): void {\r\n state.userId = undefined;\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport type { OnErrorContext } from '../types/config.js';\r\nimport type { ListenerRegistry } from './registry.js';\r\nimport type { EnricherState } from '../enricher/enricher.js';\r\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\r\n\r\nexport interface DispatcherDeps {\r\n registry: ListenerRegistry;\r\n getEnricherState: () => EnricherState;\r\n onError?: (error: Error, context?: OnErrorContext) => void;\r\n}\r\n\r\n/**\r\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\r\n */\r\nexport function dispatchTrack(\r\n deps: DispatcherDeps,\r\n payload: TrackPayload\r\n): void {\r\n const state = deps.getEnricherState();\r\n const envelope = enrich(payload, state);\r\n dispatchEnvelope(deps, envelope, 'track');\r\n}\r\n\r\n/**\r\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\r\n */\r\nfunction dispatchEnvelope(\r\n deps: DispatcherDeps,\r\n envelope: EventEnvelope,\r\n kind: 'track'\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n try {\r\n listener.track(envelope);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Call identify on all listeners that implement it (try/catch per listener).\r\n */\r\nexport function dispatchIdentify(\r\n deps: DispatcherDeps,\r\n userId: string,\r\n traits?: Record<string, unknown>\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n if (typeof listener.identify !== 'function') continue;\r\n try {\r\n listener.identify(userId, traits);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\n/**\r\n * No-op listener: implements the interface with no side effects.\r\n * Use when analytics is disabled or in tests.\r\n */\r\nexport function createNoopListener(): AnalyticsListener {\r\n return {\r\n init(_config: unknown): void {\r\n // no-op\r\n },\r\n track(_envelope: EventEnvelope): void {\r\n // no-op\r\n },\r\n };\r\n}\r\n\r\n/** Singleton no-op listener instance. */\r\nexport const noopListener: AnalyticsListener = createNoopListener();\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n\r\n // PII Safety: Only pass the pseudonymous userId to Clarity's identify.\r\n // Do NOT forward PII fields (email, name, displayName) to a third-party service.\r\n // The userId (GUID) can be correlated back to a real user in your own system.\r\n window.clarity('identify', userId);\r\n\r\n // Forward only non-PII traits as Clarity custom tags.\r\n // Keys containing PII are stripped to stay compliant with GDPR / CCPA.\r\n if (traits) {\r\n const piiKeys = new Set(['email', 'name', 'displayName', 'firstName', 'lastName', 'username', 'phone', 'address']);\r\n for (const [key, value] of Object.entries(traits)) {\r\n if (piiKeys.has(key)) continue;\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface GAListenerConfig {\r\n measurementId: string;\r\n}\r\n\r\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\r\n\r\ndeclare global {\r\n interface Window {\r\n dataLayer?: unknown[];\r\n gtag?: (...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadGtagScript(measurementId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (window.gtag) {\r\n window.gtag('config', measurementId);\r\n resolve();\r\n return;\r\n }\r\n window.dataLayer = window.dataLayer ?? [];\r\n const gtag = (...args: unknown[]) => {\r\n window.dataLayer!.push(args);\r\n };\r\n window.gtag = gtag;\r\n gtag('js', new Date());\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\r\n script.onload = () => {\r\n gtag('config', measurementId);\r\n resolve();\r\n };\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Google Analytics 4 via gtag.\r\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\r\n const measurementId = config.measurementId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as GAListenerConfig;\r\n const id = c.measurementId;\r\n if (!id || !isBrowser()) return;\r\n await loadGtagScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\r\n const params = envelope.properties ?? {};\r\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\r\n window.gtag('event', envelope.name, params);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || typeof window.gtag !== 'function') return;\r\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface CustomApiListenerConfig {\r\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\r\n endpoint: string;\r\n /** Optional API key or bearer token. */\r\n apiKey?: string;\r\n /** Optional: batch size before sending. Default 1 (no batching). */\r\n batchSize?: number;\r\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\r\n flushIntervalMs?: number;\r\n}\r\n\r\nconst DEFAULT_BATCH_SIZE = 1;\r\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\r\n\r\n/**\r\n * Listener that POSTs events to a configurable endpoint.\r\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\r\n */\r\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\r\n const endpoint = config.endpoint;\r\n const apiKey = config.apiKey;\r\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\r\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\r\n\r\n let batch: EventEnvelope[] = [];\r\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n function scheduleFlush(): void {\r\n if (flushTimer != null) return;\r\n flushTimer = setTimeout(() => {\r\n flushTimer = null;\r\n sendBatch();\r\n }, flushIntervalMs);\r\n }\r\n\r\n function sendBatch(): void {\r\n if (batch.length === 0) return;\r\n const toSend = [...batch];\r\n batch = [];\r\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\r\n if (typeof fetch !== 'undefined') {\r\n fetch(endpoint, {\r\n method: 'POST',\r\n headers,\r\n body,\r\n keepalive: true,\r\n }).catch(() => {});\r\n }\r\n }\r\n\r\n return {\r\n init(_config: unknown): void {\r\n // config was passed at createCustomApiListener\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n batch.push(envelope);\r\n if (batch.length >= batchSize) {\r\n sendBatch();\r\n } else if (flushIntervalMs > 0) {\r\n scheduleFlush();\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n flush(): Promise<void> {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n sendBatch();\r\n return Promise.resolve();\r\n },\r\n teardown(): void {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n batch.length = 0;\r\n },\r\n };\r\n}\r\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\r\nimport type { AnalyticsListener } from '../types/listener.js';\r\nimport { createNoopListener } from './noop.js';\r\nimport { createClarityListener } from './clarity.js';\r\nimport { createGAListener } from './ga.js';\r\nimport { createCustomApiListener } from './custom-api.js';\r\n\r\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\r\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\r\n\r\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\r\n noop: () => createNoopListener(),\r\n clarity: (config) => createClarityListener(config as { projectId: string }),\r\n ga: (config) => createGAListener(config as { measurementId: string }),\r\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\r\n};\r\n\r\n/**\r\n * Returns the built-in listener factory for the given id, or undefined if unknown.\r\n */\r\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\r\n return BUILT_IN_FACTORIES[id];\r\n}\r\n\r\nexport interface BuildListenersFromRemoteConfigOptions {\r\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\r\n factories?: Record<string, ListenerFactory>;\r\n /** Called when a remote listener id has no factory (unknown id). */\r\n onError?: (error: Error, context?: { listenerId?: string }) => void;\r\n}\r\n\r\n/**\r\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\r\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\r\n */\r\nexport function buildListenersFromRemoteConfig(\r\n remote: RemoteAnalyticsConfig,\r\n options?: BuildListenersFromRemoteConfigOptions\r\n): ListenerEntry[] {\r\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\r\n const onError = options?.onError;\r\n const entries: ListenerEntry[] = [];\r\n\r\n if (!Array.isArray(remote.listeners)) {\r\n return entries;\r\n }\r\n\r\n for (const entry of remote.listeners) {\r\n if (entry.enabled === false) continue;\r\n const id = entry.id;\r\n if (id == null || typeof id !== 'string') continue;\r\n\r\n const factory = factories[id];\r\n if (factory == null) {\r\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\r\n continue;\r\n }\r\n\r\n let listener: AnalyticsListener;\r\n try {\r\n listener = factory(entry.config);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n continue;\r\n }\r\n\r\n entries.push({\r\n id,\r\n enabled: entry.enabled,\r\n listener,\r\n config: entry.config,\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n PreInitBehavior,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport type { AnalyticsListener } from './types/listener.js';\r\n\r\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\r\n\r\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\r\n if (entry == null || typeof entry !== 'object') return false;\r\n const o = entry as Record<string, unknown>;\r\n return (\r\n typeof o.id === 'string' &&\r\n o.id.trim() !== '' &&\r\n 'config' in o &&\r\n !('listener' in o && o.listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Validate init config. Throws with clear message on invalid config.\r\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\r\n */\r\nexport function validateConfig(options: AnalyticsInitConfig): void {\r\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\r\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\r\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\r\n }\r\n if (options.listeners != null && !Array.isArray(options.listeners)) {\r\n throw new Error('Analytics init failed: listeners must be an array.');\r\n }\r\n const behavior = options.preInitBehavior ?? 'drop';\r\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\r\n throw new Error(\r\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\r\n );\r\n }\r\n if (behavior === 'queue') {\r\n const maxSize = options.queueMaxSize ?? 100;\r\n const ttlMs = options.queueTtlMs ?? 5000;\r\n if (typeof maxSize !== 'number' || maxSize <= 0) {\r\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\r\n }\r\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\r\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\r\n }\r\n }\r\n if (useRemoteConfig) {\r\n return;\r\n }\r\n const listeners = options.listeners ?? [];\r\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\r\n if (isRemoteShape) {\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as RemoteListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\r\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\r\n }\r\n }\r\n return;\r\n }\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as ListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (entry.listener == null || typeof entry.listener !== 'object') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\r\n }\r\n const listener = entry.listener as AnalyticsListener;\r\n if (typeof listener.track !== 'function') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\r\n }\r\n }\r\n}\r\n","/** SDK version; set at build time or from package.json. */\r\nexport const SDK_VERSION = '1.2.0';\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n OnErrorContext,\r\n RemoteAnalyticsConfig,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\r\nimport { ListenerRegistry } from './dispatcher/registry.js';\r\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\r\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\r\nimport {\r\n createEnricherState,\r\n setUserId,\r\n clearUserId,\r\n type EnricherState,\r\n} from './enricher/enricher.js';\r\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\r\nimport { validateConfig } from './validate-config.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nlet enricherState: EnricherState | null = null;\r\nlet registry: ListenerRegistry | null = null;\r\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\r\nlet preInitBuffer: PreInitBuffer | null = null;\r\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\r\nlet initialized = false;\r\n\r\n/** Default buffer before init: queue mode so events before init can be replayed. */\r\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\r\n if (preInitBuffer == null) {\r\n preInitBuffer = new PreInitBuffer({\r\n mode: 'queue',\r\n maxSize: 100,\r\n ttlMs: 5000,\r\n });\r\n }\r\n return preInitBuffer;\r\n}\r\n\r\nfunction getEnricherState(): EnricherState {\r\n if (enricherState == null) {\r\n throw new Error('Analytics not initialized.');\r\n }\r\n return enricherState;\r\n}\r\n\r\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\r\n if (options.getConfig) {\r\n return options.getConfig();\r\n }\r\n if (options.configUrl) {\r\n const fetchFn = options.fetch ?? globalThis.fetch;\r\n const res = await fetchFn(options.configUrl);\r\n if (!res.ok) {\r\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\r\n }\r\n const json = await res.json();\r\n if (json == null || typeof json !== 'object') {\r\n throw new Error('Analytics config response must be a JSON object.');\r\n }\r\n return json as RemoteAnalyticsConfig;\r\n }\r\n return null;\r\n}\r\n\r\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\r\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\r\n return listeners.every(\r\n (entry): entry is RemoteListenerEntry =>\r\n entry != null &&\r\n typeof entry === 'object' &&\r\n typeof (entry as RemoteListenerEntry).id === 'string' &&\r\n 'config' in entry &&\r\n !('listener' in entry && (entry as ListenerEntry).listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Initialize the SDK. Idempotent; calling again re-applies config.\r\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\r\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\r\n */\r\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\r\n validateConfig(options);\r\n\r\n let appId: string;\r\n let environment: string;\r\n let listeners: ListenerEntry[];\r\n\r\n const remote = await fetchRemoteConfig(options);\r\n if (remote != null) {\r\n appId = (options.appId ?? '').toString().trim();\r\n environment = options.environment ?? 'production';\r\n if (!appId) {\r\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\r\n }\r\n listeners = buildListenersFromRemoteConfig(remote, {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n });\r\n } else {\r\n appId = options.appId.trim();\r\n environment = options.environment ?? 'production';\r\n const rawListeners = options.listeners ?? [];\r\n if (isInlineRemoteShape(rawListeners)) {\r\n listeners = buildListenersFromRemoteConfig(\r\n { listeners: rawListeners },\r\n {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n }\r\n );\r\n } else {\r\n listeners = rawListeners as ListenerEntry[];\r\n }\r\n }\r\n\r\n const preInitBehavior = options.preInitBehavior ?? 'drop';\r\n const queueMaxSize = options.queueMaxSize ?? 100;\r\n const queueTtlMs = options.queueTtlMs ?? 5000;\r\n onError = options.onError;\r\n\r\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\r\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\r\n\r\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\r\n registry = new ListenerRegistry();\r\n\r\n for (const entry of listeners) {\r\n if (entry.enabled === false) continue;\r\n try {\r\n const initResult = entry.listener.init(entry.config);\r\n if (initResult instanceof Promise) {\r\n await initResult;\r\n }\r\n registry.add(entry.id, entry.listener);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: entry.id });\r\n }\r\n }\r\n\r\n const deps: DispatcherDeps = {\r\n registry,\r\n getEnricherState,\r\n onError,\r\n };\r\n\r\n if (preInitBuffer) {\r\n if (preInitBehavior === 'queue') {\r\n preInitBuffer.flush(\r\n (payload) => dispatchTrack(deps, payload),\r\n (userId, traits) => {\r\n setUserId(enricherState!, userId);\r\n dispatchIdentify(deps, userId, traits);\r\n }\r\n );\r\n } else {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n preInitBuffer = null;\r\n }\r\n\r\n initialized = true;\r\n}\r\n\r\n/**\r\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\r\n return;\r\n }\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchTrack(deps, event);\r\n}\r\n\r\n/**\r\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\r\n return;\r\n }\r\n if (enricherState) setUserId(enricherState, userId);\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchIdentify(deps, userId, traits);\r\n}\r\n\r\n/**\r\n * Flush listeners that batch (e.g. before page unload).\r\n */\r\nexport async function flush(): Promise<void> {\r\n if (!initialized || registry == null) return;\r\n const promises: Promise<void>[] = [];\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.flush === 'function') {\r\n const result = listener.flush();\r\n if (result instanceof Promise) promises.push(result);\r\n }\r\n }\r\n await Promise.all(promises);\r\n}\r\n\r\n/**\r\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\r\n */\r\nexport async function reset(): Promise<void> {\r\n if (preInitBuffer) {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n if (enricherState) clearUserId(enricherState);\r\n if (registry) {\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.teardown === 'function') {\r\n try {\r\n const result = listener.teardown();\r\n if (result instanceof Promise) await result;\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, {});\r\n }\r\n }\r\n }\r\n registry.clear();\r\n }\r\n preInitBuffer = null;\r\n enricherState = null;\r\n registry = null;\r\n onError = undefined;\r\n initialized = false;\r\n}\r\n\r\nexport function isInitialized(): boolean {\r\n return initialized;\r\n}\r\n"],"mappings":";AAkBO,IAAM,gBAAN,MAAoB;AAAA,EAKzB,YAAY,SAA+B;AAH3C,SAAiB,QAAwB,CAAC;AAIxC,SAAK,UAAU;AACf,SAAK,YACH,QAAQ,sBAAsB,SAAY,QAAQ,oBAAoB,OAAO,WAAW;AAAA,EAC5F;AAAA;AAAA;AAAA;AAAA,EAKA,UAAU,MAAc,YAAsC,WAA0B;AACtF,QAAI,KAAK,QAAQ,SAAS,UAAU,CAAC,KAAK,UAAW;AACrD,UAAM,OAAsB;AAAA,MAC1B,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,IACtB;AACA,SAAK,KAAK,IAAI;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,QAAgB,QAAwC;AACnE,QAAI,KAAK,QAAQ,SAAS,UAAU,CAAC,KAAK,UAAW;AACrD,UAAM,OAAyB;AAAA,MAC7B,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA,WAAW,KAAK,IAAI;AAAA,IACtB;AACA,SAAK,KAAK,IAAI;AAAA,EAChB;AAAA,EAEQ,KAAK,MAA0B;AACrC,WAAO,KAAK,MAAM,UAAU,KAAK,QAAQ,WAAW,KAAK,MAAM,SAAS,GAAG;AACzE,WAAK,MAAM,MAAM;AAAA,IACnB;AACA,SAAK,MAAM,KAAK,IAAI;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MACE,SACA,YACM;AACN,QAAI,CAAC,KAAK,aAAa,KAAK,MAAM,WAAW,GAAG;AAC9C,WAAK,MAAM,SAAS;AACpB;AAAA,IACF;AACA,UAAM,MAAM,KAAK,IAAI;AACrB,UAAM,SAAS,MAAM,KAAK,QAAQ;AAClC,UAAM,gBAAgB,KAAK,MAAM;AAAA,MAC/B,CAAC,SAAmC,KAAK,SAAS,cAAc,KAAK,aAAa;AAAA,IACpF;AACA,UAAM,aAAa,KAAK,MAAM;AAAA,MAC5B,CAAC,SAAgC,KAAK,SAAS,WAAW,KAAK,aAAa;AAAA,IAC9E;AACA,eAAW,QAAQ,eAAe;AAChC,iBAAW,KAAK,QAAQ,KAAK,MAAM;AAAA,IACrC;AACA,eAAW,QAAQ,YAAY;AAC7B,cAAQ;AAAA,QACN,MAAM,KAAK;AAAA,QACX,YAAY,KAAK;AAAA,QACjB,WAAW,KAAK;AAAA,MAClB,CAAC;AAAA,IACH;AACA,SAAK,MAAM,SAAS;AAAA,EACtB;AAAA;AAAA,EAGA,IAAI,SAAiB;AACnB,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;;;AC5FO,IAAM,mBAAN,MAAuB;AAAA,EAAvB;AACL,SAAiB,YAAkC,CAAC;AAAA;AAAA,EAEpD,IAAI,IAAwB,UAAmC;AAC7D,SAAK,UAAU,KAAK,EAAE,IAAI,SAAS,CAAC;AAAA,EACtC;AAAA,EAEA,SAA+B;AAC7B,WAAO,CAAC,GAAG,KAAK,SAAS;AAAA,EAC3B;AAAA,EAEA,QAAc;AACZ,SAAK,UAAU,SAAS;AAAA,EAC1B;AACF;;;ACxBA,IAAM,qBAAqB;AAKpB,SAAS,aAAqB;AACnC,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AACA,SAAO,eAAe;AACxB;AAEA,SAAS,iBAAyB;AAChC,QAAM,MAAM;AACZ,MAAI,SAAS;AACb,WAAS,OAAO,QAAQ,SAAS,CAAC,MAAM;AACtC,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,MAAM,MAAM,IAAK,IAAI,IAAO;AACtC,WAAO,IAAI,CAAC;AAAA,EACd,CAAC;AACD,SAAO;AACT;AAKO,SAAS,qBAAqB,OAAe,mBAAoC;AACtF,MAAI,kBAAmB,QAAO;AAC9B,QAAM,MAAM,GAAG,kBAAkB,GAAG,KAAK;AACzC,MAAI,OAAO,WAAW,eAAe,OAAO,mBAAmB,aAAa;AAC1E,QAAI;AACF,YAAM,WAAW,eAAe,QAAQ,GAAG;AAC3C,UAAI,SAAU,QAAO;AACrB,YAAM,QAAQ,WAAW;AACzB,qBAAe,QAAQ,KAAK,KAAK;AACjC,aAAO;AAAA,IACT,QAAQ;AACN,aAAO,WAAW;AAAA,IACpB;AAAA,EACF;AACA,SAAO,WAAW;AACpB;;;ACrBA,IAAM,mBAAmB;AAKlB,SAAS,oBACd,OACA,aACA,YACA,WACe;AACf,QAAM,oBAAoB,qBAAqB,OAAO,SAAS;AAC/D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKO,SAAS,OACd,SACA,OACe;AACf,QAAM,YAAY,QAAQ,cAAa,oBAAI,KAAK,GAAE,YAAY;AAC9D,QAAM,WAA0B;AAAA,IAC9B,MAAM,QAAQ;AAAA,IACd;AAAA,IACA,SAAS,WAAW;AAAA,IACpB,OAAO,MAAM;AAAA,IACb,aAAa,MAAM;AAAA,IACnB,YAAY,MAAM;AAAA,IAClB,SAAS;AAAA,EACX;AACA,MAAI,QAAQ,cAAc,KAAM,UAAS,aAAa,QAAQ;AAC9D,MAAI,MAAM,kBAAmB,UAAS,YAAY,MAAM;AACxD,MAAI,MAAM,OAAQ,UAAS,SAAS,MAAM;AAC1C,SAAO;AACT;AAKO,SAAS,UAAU,OAAsB,QAAsB;AACpE,QAAM,SAAS;AACjB;AAKO,SAAS,YAAY,OAA4B;AACtD,QAAM,SAAS;AACjB;;;AC7DO,SAAS,cACd,MACA,SACM;AACN,QAAM,QAAQ,KAAK,iBAAiB;AACpC,QAAM,WAAW,OAAO,SAAS,KAAK;AACtC,mBAAiB,MAAM,UAAU,OAAO;AAC1C;AAKA,SAAS,iBACP,MACA,UACA,MACM;AACN,QAAM,EAAE,UAAAA,WAAU,SAAAC,SAAQ,IAAI;AAC9B,aAAW,EAAE,IAAI,SAAS,KAAKD,UAAS,OAAO,GAAG;AAChD,QAAI;AACF,eAAS,MAAM,QAAQ;AAAA,IACzB,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,MAAAC,WAAU,OAAO,EAAE,YAAY,GAAG,CAAC;AAAA,IACrC;AAAA,EACF;AACF;AAKO,SAAS,iBACd,MACA,QACA,QACM;AACN,QAAM,EAAE,UAAAD,WAAU,SAAAC,SAAQ,IAAI;AAC9B,aAAW,EAAE,IAAI,SAAS,KAAKD,UAAS,OAAO,GAAG;AAChD,QAAI,OAAO,SAAS,aAAa,WAAY;AAC7C,QAAI;AACF,eAAS,SAAS,QAAQ,MAAM;AAAA,IAClC,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,MAAAC,WAAU,OAAO,EAAE,YAAY,GAAG,CAAC;AAAA,IACrC;AAAA,EACF;AACF;;;ACtDO,SAAS,qBAAwC;AACtD,SAAO;AAAA,IACL,KAAK,SAAwB;AAAA,IAE7B;AAAA,IACA,MAAM,WAAgC;AAAA,IAEtC;AAAA,EACF;AACF;AAGO,IAAM,eAAkC,mBAAmB;;;ACZlE,IAAM,sBAAsB;AAQ5B,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,kBAAkB,WAAkC;AAC3D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAAC,UAAU,GAAG;AAChB,cAAQ;AACR;AAAA,IACF;AACA,QAAI,OAAO,OAAO,YAAY,YAAY;AACxC,cAAQ;AACR;AAAA,IACF;AAMA,UAAM,IAAI;AACV,MAAE,SAAS,IAAI,EAAE,SAAS,KAAK,YAAa,MAAiB;AAC3D,YAAM,KAAK,EAAE,SAAS;AACtB,OAAC,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,IAAI;AAAA,IAC/B;AACA,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,MAAM,GAAG,mBAAmB,GAAG,SAAS;AAC/C,WAAO,SAAS,MAAM,QAAQ;AAC9B,WAAO,UAAU,MAAM,QAAQ;AAC/B,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAOO,SAAS,sBAAsB,QAAkD;AACtF,QAAM,YAAY,OAAO;AACzB,MAAI,QAAQ;AAEZ,SAAO;AAAA,IACL,MAAM,KAAK,KAA6B;AACtC,UAAI;AACF,cAAM,IAAK,OAAO;AAClB,cAAM,KAAK,EAAE;AACb,YAAI,CAAC,MAAM,CAAC,UAAU,EAAG;AACzB,cAAM,kBAAkB,EAAE;AAC1B,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAAC,UAAU,KAAK,CAAC,SAAS,OAAO,OAAO,YAAY,WAAY;AACpE,eAAO,QAAQ,SAAS,SAAS,IAAI;AACrC,YAAI,SAAS,cAAc,OAAO,OAAO,YAAY,YAAY;AAC/D,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,UAAU,GAAG;AAG9D,gBAAI,OAAO,UAAU,UAAU;AAC7B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAClE,qBAAO,QAAQ,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,YAC1C,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,SAAS,QAAgB,QAAwC;AAC/D,UAAI;AACF,YAAI,CAAC,UAAU,KAAK,CAAC,SAAS,OAAO,OAAO,YAAY,WAAY;AAKpE,eAAO,QAAQ,YAAY,MAAM;AAIjC,YAAI,QAAQ;AACV,gBAAM,UAAU,oBAAI,IAAI,CAAC,SAAS,QAAQ,eAAe,aAAa,YAAY,YAAY,SAAS,SAAS,CAAC;AACjH,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,gBAAI,QAAQ,IAAI,GAAG,EAAG;AACtB,gBAAI,OAAO,UAAU,UAAU;AAC7B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAClE,qBAAO,QAAQ,OAAO,KAAK,OAAO,KAAK,CAAC;AAAA,YAC1C,WAAW,MAAM,QAAQ,KAAK,GAAG;AAC/B,qBAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,YAClC;AAAA,UACF;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;AChHA,IAAM,kBAAkB;AASxB,SAASC,aAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,eAAe,eAAsC;AAC5D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAACA,WAAU,GAAG;AAChB,cAAQ;AACR;AAAA,IACF;AACA,QAAI,OAAO,MAAM;AACf,aAAO,KAAK,UAAU,aAAa;AACnC,cAAQ;AACR;AAAA,IACF;AACA,WAAO,YAAY,OAAO,aAAa,CAAC;AACxC,UAAM,OAAO,IAAI,SAAoB;AACnC,aAAO,UAAW,KAAK,IAAI;AAAA,IAC7B;AACA,WAAO,OAAO;AACd,SAAK,MAAM,oBAAI,KAAK,CAAC;AACrB,UAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,WAAO,QAAQ;AACf,WAAO,MAAM,GAAG,eAAe,OAAO,mBAAmB,aAAa,CAAC;AACvE,WAAO,SAAS,MAAM;AACpB,WAAK,UAAU,aAAa;AAC5B,cAAQ;AAAA,IACV;AACA,WAAO,UAAU,MAAM,QAAQ;AAC/B,aAAS,KAAK,YAAY,MAAM;AAAA,EAClC,CAAC;AACH;AAOO,SAAS,iBAAiB,QAA6C;AAC5E,QAAM,gBAAgB,OAAO;AAC7B,MAAI,QAAQ;AAEZ,SAAO;AAAA,IACL,MAAM,KAAK,KAA6B;AACtC,UAAI;AACF,cAAM,IAAK,OAAO;AAClB,cAAM,KAAK,EAAE;AACb,YAAI,CAAC,MAAM,CAACA,WAAU,EAAG;AACzB,cAAM,eAAe,EAAE;AACvB,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAACA,WAAU,KAAK,CAAC,SAAS,OAAO,OAAO,SAAS,WAAY;AACjE,cAAM,SAAS,SAAS,cAAc,CAAC;AACvC,YAAI,SAAS,OAAQ,CAAC,OAAmC,SAAS,IAAI,SAAS;AAC/E,eAAO,KAAK,SAAS,SAAS,MAAM,MAAM;AAAA,MAC5C,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,SAAS,QAAgB,QAAwC;AAC/D,UAAI;AACF,YAAI,CAACA,WAAU,KAAK,OAAO,OAAO,SAAS,WAAY;AACvD,eAAO,KAAK,OAAO,mBAAmB,EAAE,SAAS,QAAQ,GAAG,OAAO,CAAC;AAAA,MACtE,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;AC3EA,IAAM,qBAAqB;AAC3B,IAAM,4BAA4B;AAM3B,SAAS,wBAAwB,QAAoD;AAC1F,QAAM,WAAW,OAAO;AACxB,QAAM,SAAS,OAAO;AACtB,QAAM,YAAY,OAAO,aAAa;AACtC,QAAM,kBAAkB,OAAO,mBAAmB;AAElD,MAAI,QAAyB,CAAC;AAC9B,MAAI,aAAmD;AAEvD,WAAS,gBAAsB;AAC7B,QAAI,cAAc,KAAM;AACxB,iBAAa,WAAW,MAAM;AAC5B,mBAAa;AACb,gBAAU;AAAA,IACZ,GAAG,eAAe;AAAA,EACpB;AAEA,WAAS,YAAkB;AACzB,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,SAAS,CAAC,GAAG,KAAK;AACxB,YAAQ,CAAC;AACT,UAAM,OAAO,KAAK,UAAU,OAAO,WAAW,IAAI,OAAO,CAAC,IAAI,EAAE,QAAQ,OAAO,CAAC;AAChF,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,IAClB;AACA,QAAI,OAAQ,SAAQ,eAAe,IAAI,UAAU,MAAM;AACvD,QAAI,OAAO,UAAU,aAAa;AAChC,YAAM,UAAU;AAAA,QACd,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA,WAAW;AAAA,MACb,CAAC,EAAE,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,KAAK,SAAwB;AAAA,IAE7B;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,cAAM,KAAK,QAAQ;AACnB,YAAI,MAAM,UAAU,WAAW;AAC7B,oBAAU;AAAA,QACZ,WAAW,kBAAkB,GAAG;AAC9B,wBAAc;AAAA,QAChB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,QAAuB;AACrB,UAAI,cAAc,MAAM;AACtB,qBAAa,UAAU;AACvB,qBAAa;AAAA,MACf;AACA,gBAAU;AACV,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAAA,IACA,WAAiB;AACf,UAAI,cAAc,MAAM;AACtB,qBAAa,UAAU;AACvB,qBAAa;AAAA,MACf;AACA,YAAM,SAAS;AAAA,IACjB;AAAA,EACF;AACF;;;AC/EA,IAAM,qBAAsD;AAAA,EAC1D,MAAM,MAAM,mBAAmB;AAAA,EAC/B,SAAS,CAAC,WAAW,sBAAsB,MAA+B;AAAA,EAC1E,IAAI,CAAC,WAAW,iBAAiB,MAAmC;AAAA,EACpE,cAAc,CAAC,WAAW,wBAAwB,MAAuD;AAC3G;AAoBO,SAAS,+BACd,QACA,SACiB;AACjB,QAAM,YAAY,SAAS,aAAa;AACxC,QAAMC,WAAU,SAAS;AACzB,QAAM,UAA2B,CAAC;AAElC,MAAI,CAAC,MAAM,QAAQ,OAAO,SAAS,GAAG;AACpC,WAAO;AAAA,EACT;AAEA,aAAW,SAAS,OAAO,WAAW;AACpC,QAAI,MAAM,YAAY,MAAO;AAC7B,UAAM,KAAK,MAAM;AACjB,QAAI,MAAM,QAAQ,OAAO,OAAO,SAAU;AAE1C,UAAM,UAAU,UAAU,EAAE;AAC5B,QAAI,WAAW,MAAM;AACnB,MAAAA,WAAU,IAAI,MAAM,wBAAwB,EAAE,EAAE,GAAG,EAAE,YAAY,GAAG,CAAC;AACrE;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,iBAAW,QAAQ,MAAM,MAAM;AAAA,IACjC,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,MAAAA,WAAU,OAAO,EAAE,YAAY,GAAG,CAAC;AACnC;AAAA,IACF;AAEA,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,SAAS,MAAM;AAAA,MACf;AAAA,MACA,QAAQ,MAAM;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACpEA,IAAM,2BAA8C,CAAC,QAAQ,OAAO;AAEpE,SAAS,sBAAsB,OAA8C;AAC3E,MAAI,SAAS,QAAQ,OAAO,UAAU,SAAU,QAAO;AACvD,QAAM,IAAI;AACV,SACE,OAAO,EAAE,OAAO,YAChB,EAAE,GAAG,KAAK,MAAM,MAChB,YAAY,KACZ,EAAE,cAAc,KAAK,EAAE,YAAY;AAEvC;AAMO,SAAS,eAAe,SAAoC;AACjE,QAAM,kBAAkB,QAAQ,QAAQ,aAAa,QAAQ,SAAS;AACtE,MAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,MAAM,KAAK,MAAM,IAAI;AACpE,UAAM,IAAI,MAAM,4GAA4G;AAAA,EAC9H;AACA,MAAI,QAAQ,aAAa,QAAQ,CAAC,MAAM,QAAQ,QAAQ,SAAS,GAAG;AAClE,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,QAAM,WAAW,QAAQ,mBAAmB;AAC5C,MAAI,CAAC,yBAAyB,SAAS,QAAQ,GAAG;AAChD,UAAM,IAAI;AAAA,MACR,0EAA0E,QAAQ;AAAA,IACpF;AAAA,EACF;AACA,MAAI,aAAa,SAAS;AACxB,UAAM,UAAU,QAAQ,gBAAgB;AACxC,UAAM,QAAQ,QAAQ,cAAc;AACpC,QAAI,OAAO,YAAY,YAAY,WAAW,GAAG;AAC/C,YAAM,IAAI,MAAM,gEAAgE;AAAA,IAClF;AACA,QAAI,OAAO,UAAU,YAAY,SAAS,GAAG;AAC3C,YAAM,IAAI,MAAM,8DAA8D;AAAA,IAChF;AAAA,EACF;AACA,MAAI,iBAAiB;AACnB;AAAA,EACF;AACA,QAAM,YAAY,QAAQ,aAAa,CAAC;AACxC,QAAM,gBAAgB,UAAU,SAAS,KAAK,UAAU,MAAM,qBAAqB;AACnF,MAAI,eAAe;AACjB,aAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,YAAM,QAAQ,UAAU,CAAC;AACzB,UAAI,MAAM,YAAY,MAAO;AAC7B,UAAI,OAAO,MAAM,OAAO,YAAY,MAAM,GAAG,KAAK,MAAM,IAAI;AAC1D,cAAM,IAAI,MAAM,oCAAoC,CAAC,kDAAkD;AAAA,MACzG;AAAA,IACF;AACA;AAAA,EACF;AACA,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,QAAQ,UAAU,CAAC;AACzB,QAAI,MAAM,YAAY,MAAO;AAC7B,QAAI,MAAM,YAAY,QAAQ,OAAO,MAAM,aAAa,UAAU;AAChE,YAAM,IAAI,MAAM,oCAAoC,CAAC,yBAAyB;AAAA,IAChF;AACA,UAAM,WAAW,MAAM;AACvB,QAAI,OAAO,SAAS,UAAU,YAAY;AACxC,YAAM,IAAI,MAAM,oCAAoC,CAAC,sCAAsC;AAAA,IAC7F;AAAA,EACF;AACF;;;AC1EO,IAAM,cAAc;;;ACoB3B,IAAI,gBAAsC;AAC1C,IAAI,WAAoC;AAExC,IAAI,gBAAsC;AAC1C,IAAI;AACJ,IAAI,cAAc;AAGlB,SAAS,2BAA0C;AACjD,MAAI,iBAAiB,MAAM;AACzB,oBAAgB,IAAI,cAAc;AAAA,MAChC,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAEA,SAAS,mBAAkC;AACzC,MAAI,iBAAiB,MAAM;AACzB,UAAM,IAAI,MAAM,4BAA4B;AAAA,EAC9C;AACA,SAAO;AACT;AAEA,eAAe,kBAAkB,SAAqE;AACpG,MAAI,QAAQ,WAAW;AACrB,WAAO,QAAQ,UAAU;AAAA,EAC3B;AACA,MAAI,QAAQ,WAAW;AACrB,UAAM,UAAU,QAAQ,SAAS,WAAW;AAC5C,UAAM,MAAM,MAAM,QAAQ,QAAQ,SAAS;AAC3C,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,MAAM,kCAAkC,IAAI,MAAM,IAAI,IAAI,UAAU,EAAE;AAAA,IAClF;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,QAAQ,QAAQ,OAAO,SAAS,UAAU;AAC5C,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,WAAwD;AACnF,MAAI,CAAC,MAAM,QAAQ,SAAS,KAAK,UAAU,WAAW,EAAG,QAAO;AAChE,SAAO,UAAU;AAAA,IACf,CAAC,UACC,SAAS,QACT,OAAO,UAAU,YACjB,OAAQ,MAA8B,OAAO,YAC7C,YAAY,SACZ,EAAE,cAAc,SAAU,MAAwB,YAAY;AAAA,EAClE;AACF;AAOA,eAAsB,KAAK,SAA6C;AACtE,iBAAe,OAAO;AAEtB,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,QAAM,SAAS,MAAM,kBAAkB,OAAO;AAC9C,MAAI,UAAU,MAAM;AAClB,aAAS,QAAQ,SAAS,IAAI,SAAS,EAAE,KAAK;AAC9C,kBAAc,QAAQ,eAAe;AACrC,QAAI,CAAC,OAAO;AACV,YAAM,IAAI,MAAM,kGAAkG;AAAA,IACpH;AACA,gBAAY,+BAA+B,QAAQ;AAAA,MACjD,WAAW,QAAQ;AAAA,MACnB,SAAS,QAAQ;AAAA,IACnB,CAAC;AAAA,EACH,OAAO;AACL,YAAQ,QAAQ,MAAM,KAAK;AAC3B,kBAAc,QAAQ,eAAe;AACrC,UAAM,eAAe,QAAQ,aAAa,CAAC;AAC3C,QAAI,oBAAoB,YAAY,GAAG;AACrC,kBAAY;AAAA,QACV,EAAE,WAAW,aAAa;AAAA,QAC1B;AAAA,UACE,WAAW,QAAQ;AAAA,UACnB,SAAS,QAAQ;AAAA,QACnB;AAAA,MACF;AAAA,IACF,OAAO;AACL,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,aAAa,QAAQ,cAAc;AACzC,YAAU,QAAQ;AAElB,QAAM,OAAO,oBAAoB,UAAU,UAAU;AACrD,kBAAgB,IAAI,cAAc,EAAE,MAAM,SAAS,cAAc,OAAO,WAAW,CAAC;AAEpF,kBAAgB,oBAAoB,OAAO,aAAa,aAAa,QAAQ,SAAS;AACtF,aAAW,IAAI,iBAAiB;AAEhC,aAAW,SAAS,WAAW;AAC7B,QAAI,MAAM,YAAY,MAAO;AAC7B,QAAI;AACF,YAAM,aAAa,MAAM,SAAS,KAAK,MAAM,MAAM;AACnD,UAAI,sBAAsB,SAAS;AACjC,cAAM;AAAA,MACR;AACA,eAAS,IAAI,MAAM,IAAI,MAAM,QAAQ;AAAA,IACvC,SAAS,KAAK;AACZ,YAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,gBAAU,OAAO,EAAE,YAAY,MAAM,GAAG,CAAC;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,OAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,eAAe;AACjB,QAAI,oBAAoB,SAAS;AAC/B,oBAAc;AAAA,QACZ,CAAC,YAAY,cAAc,MAAM,OAAO;AAAA,QACxC,CAAC,QAAQ,WAAW;AAClB,oBAAU,eAAgB,MAAM;AAChC,2BAAiB,MAAM,QAAQ,MAAM;AAAA,QACvC;AAAA,MACF;AAAA,IACF,OAAO;AACL,oBAAc,MAAM,MAAM;AAAA,MAAC,GAAG,MAAM;AAAA,MAAC,CAAC;AAAA,IACxC;AACA,oBAAgB;AAAA,EAClB;AAEA,gBAAc;AAChB;AAKO,SAAS,MAAM,OAAyF;AAC7G,MAAI,CAAC,aAAa;AAChB,6BAAyB,EAAE,UAAU,MAAM,MAAM,MAAM,YAAY,MAAM,SAAS;AAClF;AAAA,EACF;AACA,QAAM,OAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,gBAAc,MAAM,KAAK;AAC3B;AAKO,SAAS,SAAS,QAAgB,QAAwC;AAC/E,MAAI,CAAC,aAAa;AAChB,6BAAyB,EAAE,aAAa,QAAQ,MAAM;AACtD;AAAA,EACF;AACA,MAAI,cAAe,WAAU,eAAe,MAAM;AAClD,QAAM,OAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,mBAAiB,MAAM,QAAQ,MAAM;AACvC;AAKA,eAAsB,QAAuB;AAC3C,MAAI,CAAC,eAAe,YAAY,KAAM;AACtC,QAAM,WAA4B,CAAC;AACnC,aAAW,EAAE,SAAS,KAAK,SAAS,OAAO,GAAG;AAC5C,QAAI,OAAO,SAAS,UAAU,YAAY;AACxC,YAAM,SAAS,SAAS,MAAM;AAC9B,UAAI,kBAAkB,QAAS,UAAS,KAAK,MAAM;AAAA,IACrD;AAAA,EACF;AACA,QAAM,QAAQ,IAAI,QAAQ;AAC5B;AAKA,eAAsB,QAAuB;AAC3C,MAAI,eAAe;AACjB,kBAAc,MAAM,MAAM;AAAA,IAAC,GAAG,MAAM;AAAA,IAAC,CAAC;AAAA,EACxC;AACA,MAAI,cAAe,aAAY,aAAa;AAC5C,MAAI,UAAU;AACZ,eAAW,EAAE,SAAS,KAAK,SAAS,OAAO,GAAG;AAC5C,UAAI,OAAO,SAAS,aAAa,YAAY;AAC3C,YAAI;AACF,gBAAM,SAAS,SAAS,SAAS;AACjC,cAAI,kBAAkB,QAAS,OAAM;AAAA,QACvC,SAAS,KAAK;AACZ,gBAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,oBAAU,OAAO,CAAC,CAAC;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AACA,aAAS,MAAM;AAAA,EACjB;AACA,kBAAgB;AAChB,kBAAgB;AAChB,aAAW;AACX,YAAU;AACV,gBAAc;AAChB;AAEO,SAAS,gBAAyB;AACvC,SAAO;AACT;","names":["registry","onError","isBrowser","onError"]}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var UsageAnalytics=(()=>{var h=class{constructor(t){this.queue=[];this.options=t,this.isBrowser=t.isBrowserOverride!==void 0?t.isBrowserOverride:typeof window<"u"}pushTrack(t,r,n){if(this.options.mode==="drop"||!this.isBrowser)return;let o={type:"track",name:t,properties:r,timestamp:n,createdAt:Date.now()};this.push(o)}pushIdentify(t,r){if(this.options.mode==="drop"||!this.isBrowser)return;let n={type:"identify",userId:t,traits:r,createdAt:Date.now()};this.push(n)}push(t){for(;this.queue.length>=this.options.maxSize&&this.queue.length>0;)this.queue.shift();this.queue.push(t)}flush(t,r){if(!this.isBrowser||this.queue.length===0){this.queue.length=0;return}let o=Date.now()-this.options.ttlMs,i=this.queue.filter(a=>a.type==="identify"&&a.createdAt>=o),s=this.queue.filter(a=>a.type==="track"&&a.createdAt>=o);for(let a of i)r(a.userId,a.traits);for(let a of s)t({name:a.name,properties:a.properties,timestamp:a.timestamp});this.queue.length=0}get length(){return this.queue.length}};var E=class{constructor(){this.listeners=[]}add(t,r){this.listeners.push({id:t,listener:r})}getAll(){return[...this.listeners]}clear(){this.listeners.length=0}};var G="snovasys_usage_analytics_session_";function w(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():H()}function H(){let e="0123456789abcdef",t="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";return t=t.replace(/[xy]/g,r=>{let n=Math.random()*16|0,o=r==="x"?n:n&3|8;return e[o]}),t}function b(e,t){if(t)return t;let r=`${G}${e}`;if(typeof window<"u"&&typeof sessionStorage<"u")try{let n=sessionStorage.getItem(r);if(n)return n;let o=w();return sessionStorage.setItem(r,o),o}catch{return w()}return w()}var K=1;function T(e,t,r,n){let o=b(e,n);return{appId:e,environment:t,sdkVersion:r,sessionId:n,resolvedSessionId:o}}function _(e,t){let r=e.timestamp??new Date().toISOString(),n={name:e.name,timestamp:r,eventId:w(),appId:t.appId,environment:t.environment,sdkVersion:t.sdkVersion,version:K};return e.properties!=null&&(n.properties=e.properties),t.resolvedSessionId&&(n.sessionId=t.resolvedSessionId),t.userId&&(n.userId=t.userId),n}function A(e,t){e.userId=t}function B(e){e.userId=void 0}function x(e,t){let r=e.getEnricherState(),n=_(t,r);Y(e,n,"track")}function Y(e,t,r){let{registry:n,onError:o}=e;for(let{id:i,listener:s}of n.getAll())try{s.track(t)}catch(a){let l=a instanceof Error?a:new Error(String(a));o?.(l,{listenerId:i})}}function L(e,t,r){let{registry:n,onError:o}=e;for(let{id:i,listener:s}of n.getAll())if(typeof s.identify=="function")try{s.identify(t,r)}catch(a){let l=a instanceof Error?a:new Error(String(a));o?.(l,{listenerId:i})}}function S(){return{init(e){},track(e){}}}var ye=S();var J="https://www.clarity.ms/tag/";function I(){return typeof window<"u"}function W(e){return new Promise(t=>{if(!I()){t();return}if(typeof window.clarity=="function"){t();return}let r=window;r.clarity=r.clarity||function(...o){let i=r.clarity;(i.q=i.q||[]).push(o)};let n=document.createElement("script");n.async=!0,n.src=`${J}${e}`,n.onload=()=>t(),n.onerror=()=>t(),document.head.appendChild(n)})}function j(e){let t=e.projectId,r=!1;return{async init(n){try{let i=(n??e).projectId;if(!i||!I())return;await W(i),r=!0}catch{}},track(n){try{if(!I()||!r||typeof window.clarity!="function")return;if(window.clarity("event",n.name),n.properties&&typeof window.clarity=="function")for(let[o,i]of Object.entries(n.properties))typeof i=="string"?window.clarity("set",o,i):typeof i=="number"||typeof i=="boolean"?window.clarity("set",o,String(i)):Array.isArray(i)&&window.clarity("set",o,i)}catch{}},identify(n,o){try{if(!I()||!r||typeof window.clarity!="function")return;window.clarity("identify",n,void 0,void 0,void 0)}catch{}}}}var Z="https://www.googletagmanager.com/gtag/js";function v(){return typeof window<"u"}function X(e){return new Promise(t=>{if(!v()){t();return}if(window.gtag){window.gtag("config",e),t();return}window.dataLayer=window.dataLayer??[];let r=(...o)=>{window.dataLayer.push(o)};window.gtag=r,r("js",new Date);let n=document.createElement("script");n.async=!0,n.src=`${Z}?id=${encodeURIComponent(e)}`,n.onload=()=>{r("config",e),t()},n.onerror=()=>t(),document.head.appendChild(n)})}function P(e){let t=e.measurementId,r=!1;return{async init(n){try{let i=(n??e).measurementId;if(!i||!v())return;await X(i),r=!0}catch{}},track(n){try{if(!v()||!r||typeof window.gtag!="function")return;let o=n.properties??{};n.userId&&(o.user_id=n.userId),window.gtag("event",n.name,o)}catch{}},identify(n,o){try{if(!v()||typeof window.gtag!="function")return;window.gtag("set","user_properties",{user_id:n,...o})}catch{}}}}function q(e){let t=e.endpoint,r=e.apiKey,n=e.batchSize??1,o=e.flushIntervalMs??5e3,i=[],s=null;function a(){s==null&&(s=setTimeout(()=>{s=null,l()},o))}function l(){if(i.length===0)return;let f=[...i];i=[];let c=JSON.stringify(f.length===1?f[0]:{events:f}),u={"Content-Type":"application/json"};r&&(u.Authorization=`Bearer ${r}`),typeof fetch<"u"&&fetch(t,{method:"POST",headers:u,body:c,keepalive:!0}).catch(()=>{})}return{init(f){},track(f){try{i.push(f),i.length>=n?l():o>0&&a()}catch{}},flush(){return s!=null&&(clearTimeout(s),s=null),l(),Promise.resolve()},teardown(){s!=null&&(clearTimeout(s),s=null),i.length=0}}}var Q={noop:()=>S(),clarity:e=>j(e),ga:e=>P(e),"custom-api":e=>q(e)};function k(e,t){let r=t?.factories??Q,n=t?.onError,o=[];if(!Array.isArray(e.listeners))return o;for(let i of e.listeners){if(i.enabled===!1)continue;let s=i.id;if(s==null||typeof s!="string")continue;let a=r[s];if(a==null){n?.(new Error(`Unknown listener id: ${s}`),{listenerId:s});continue}let l;try{l=a(i.config)}catch(f){let c=f instanceof Error?f:new Error(String(f));n?.(c,{listenerId:s});continue}o.push({id:s,enabled:i.enabled,listener:l,config:i.config})}return o}var ee=["drop","queue"];function te(e){if(e==null||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&t.id.trim()!==""&&"config"in t&&!("listener"in t&&t.listener!=null)}function D(e){let t=!!(e.configUrl??e.getConfig);if(typeof e.appId!="string"||e.appId.trim()==="")throw new Error("Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).");if(e.listeners!=null&&!Array.isArray(e.listeners))throw new Error("Analytics init failed: listeners must be an array.");let r=e.preInitBehavior??"drop";if(!ee.includes(r))throw new Error(`Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${r}'.`);if(r==="queue"){let i=e.queueMaxSize??100,s=e.queueTtlMs??5e3;if(typeof i!="number"||i<=0)throw new Error("Analytics init failed: queueMaxSize must be a positive number.");if(typeof s!="number"||s<=0)throw new Error("Analytics init failed: queueTtlMs must be a positive number.")}if(t)return;let n=e.listeners??[];if(n.length>0&&n.every(te)){for(let i=0;i<n.length;i++){let s=n[i];if(s.enabled!==!1&&(typeof s.id!="string"||s.id.trim()===""))throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`)}return}for(let i=0;i<n.length;i++){let s=n[i];if(s.enabled===!1)continue;if(s.listener==null||typeof s.listener!="object")throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);if(typeof s.listener.track!="function")throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`)}}var O="1.0.0";var y=null,p=null,d=null,m,g=!1;function F(){return d==null&&(d=new h({mode:"queue",maxSize:100,ttlMs:5e3})),d}function C(){if(y==null)throw new Error("Analytics not initialized.");return y}async function ne(e){if(e.getConfig)return e.getConfig();if(e.configUrl){let r=await(e.fetch??globalThis.fetch)(e.configUrl);if(!r.ok)throw new Error(`Analytics config fetch failed: ${r.status} ${r.statusText}`);let n=await r.json();if(n==null||typeof n!="object")throw new Error("Analytics config response must be a JSON object.");return n}return null}function re(e){return!Array.isArray(e)||e.length===0?!1:e.every(t=>t!=null&&typeof t=="object"&&typeof t.id=="string"&&"config"in t&&!("listener"in t&&t.listener!=null))}async function R(e){D(e);let t,r,n,o=await ne(e);if(o!=null){if(t=(e.appId??"").toString().trim(),r=e.environment??"production",!t)throw new Error("Analytics init failed: appId is required (set in init options, e.g. from environment variables).");n=k(o,{factories:e.listenerFactories,onError:e.onError})}else{t=e.appId.trim(),r=e.environment??"production";let c=e.listeners??[];re(c)?n=k({listeners:c},{factories:e.listenerFactories,onError:e.onError}):n=c}let i=e.preInitBehavior??"drop",s=e.queueMaxSize??100,a=e.queueTtlMs??5e3;m=e.onError;let l=i==="queue"?"queue":"drop";d=new h({mode:l,maxSize:s,ttlMs:a}),y=T(t,r,O,e.sessionId),p=new E;for(let c of n)if(c.enabled!==!1)try{let u=c.listener.init(c.config);u instanceof Promise&&await u,p.add(c.id,c.listener)}catch(u){let V=u instanceof Error?u:new Error(String(u));m?.(V,{listenerId:c.id})}let f={registry:p,getEnricherState:C,onError:m};d&&(i==="queue"?d.flush(c=>x(f,c),(c,u)=>{A(y,c),L(f,c,u)}):d.flush(()=>{},()=>{}),d=null),g=!0}function U(e){if(!g){F().pushTrack(e.name,e.properties,e.timestamp);return}x({registry:p,getEnricherState:C,onError:m},e)}function M(e,t){if(!g){F().pushIdentify(e,t);return}y&&A(y,e),L({registry:p,getEnricherState:C,onError:m},e,t)}async function z(){if(!g||p==null)return;let e=[];for(let{listener:t}of p.getAll())if(typeof t.flush=="function"){let r=t.flush();r instanceof Promise&&e.push(r)}await Promise.all(e)}async function N(){if(d&&d.flush(()=>{},()=>{}),y&&B(y),p){for(let{listener:e}of p.getAll())if(typeof e.teardown=="function")try{let t=e.teardown();t instanceof Promise&&await t}catch(t){let r=t instanceof Error?t:new Error(String(t));m?.(r,{})}p.clear()}d=null,y=null,p=null,m=void 0,g=!1}function $(){return g}var ie={init:R,track:U,identify:M,flush:z,reset:N,isInitialized:$};typeof globalThis<"u"&&(globalThis.UsageAnalytics=ie);function oe(){if(typeof document>"u")return null;let e=document.currentScript;if(!e)return null;let t=e.getAttribute("data-config");if(!t||typeof t!="string")return null;try{let r=JSON.parse(t);if(r&&typeof r.appId=="string"&&r.appId.trim()!=="")return r}catch{}return null}function se(){if(typeof window>"u")return;let e=oe()??window.__ANALYTICS_CONFIG__;e&&R(e).catch(()=>{})}se();})();
|
|
1
|
+
"use strict";var UsageAnalytics=(()=>{var h=class{constructor(t){this.queue=[];this.options=t,this.isBrowser=t.isBrowserOverride!==void 0?t.isBrowserOverride:typeof window<"u"}pushTrack(t,r,n){if(this.options.mode==="drop"||!this.isBrowser)return;let o={type:"track",name:t,properties:r,timestamp:n,createdAt:Date.now()};this.push(o)}pushIdentify(t,r){if(this.options.mode==="drop"||!this.isBrowser)return;let n={type:"identify",userId:t,traits:r,createdAt:Date.now()};this.push(n)}push(t){for(;this.queue.length>=this.options.maxSize&&this.queue.length>0;)this.queue.shift();this.queue.push(t)}flush(t,r){if(!this.isBrowser||this.queue.length===0){this.queue.length=0;return}let o=Date.now()-this.options.ttlMs,i=this.queue.filter(a=>a.type==="identify"&&a.createdAt>=o),s=this.queue.filter(a=>a.type==="track"&&a.createdAt>=o);for(let a of i)r(a.userId,a.traits);for(let a of s)t({name:a.name,properties:a.properties,timestamp:a.timestamp});this.queue.length=0}get length(){return this.queue.length}};var E=class{constructor(){this.listeners=[]}add(t,r){this.listeners.push({id:t,listener:r})}getAll(){return[...this.listeners]}clear(){this.listeners.length=0}};var G="snovasys_usage_analytics_session_";function w(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():H()}function H(){let e="0123456789abcdef",t="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";return t=t.replace(/[xy]/g,r=>{let n=Math.random()*16|0,o=r==="x"?n:n&3|8;return e[o]}),t}function b(e,t){if(t)return t;let r=`${G}${e}`;if(typeof window<"u"&&typeof sessionStorage<"u")try{let n=sessionStorage.getItem(r);if(n)return n;let o=w();return sessionStorage.setItem(r,o),o}catch{return w()}return w()}var K=1;function T(e,t,r,n){let o=b(e,n);return{appId:e,environment:t,sdkVersion:r,sessionId:n,resolvedSessionId:o}}function _(e,t){let r=e.timestamp??new Date().toISOString(),n={name:e.name,timestamp:r,eventId:w(),appId:t.appId,environment:t.environment,sdkVersion:t.sdkVersion,version:K};return e.properties!=null&&(n.properties=e.properties),t.resolvedSessionId&&(n.sessionId=t.resolvedSessionId),t.userId&&(n.userId=t.userId),n}function A(e,t){e.userId=t}function B(e){e.userId=void 0}function x(e,t){let r=e.getEnricherState(),n=_(t,r);Y(e,n,"track")}function Y(e,t,r){let{registry:n,onError:o}=e;for(let{id:i,listener:s}of n.getAll())try{s.track(t)}catch(a){let l=a instanceof Error?a:new Error(String(a));o?.(l,{listenerId:i})}}function L(e,t,r){let{registry:n,onError:o}=e;for(let{id:i,listener:s}of n.getAll())if(typeof s.identify=="function")try{s.identify(t,r)}catch(a){let l=a instanceof Error?a:new Error(String(a));o?.(l,{listenerId:i})}}function S(){return{init(e){},track(e){}}}var ye=S();var J="https://www.clarity.ms/tag/";function I(){return typeof window<"u"}function W(e){return new Promise(t=>{if(!I()){t();return}if(typeof window.clarity=="function"){t();return}let r=window;r.clarity=r.clarity||function(...o){let i=r.clarity;(i.q=i.q||[]).push(o)};let n=document.createElement("script");n.async=!0,n.src=`${J}${e}`,n.onload=()=>t(),n.onerror=()=>t(),document.head.appendChild(n)})}function j(e){let t=e.projectId,r=!1;return{async init(n){try{let i=(n??e).projectId;if(!i||!I())return;await W(i),r=!0}catch{}},track(n){try{if(!I()||!r||typeof window.clarity!="function")return;if(window.clarity("event",n.name),n.properties&&typeof window.clarity=="function")for(let[o,i]of Object.entries(n.properties))typeof i=="string"?window.clarity("set",o,i):typeof i=="number"||typeof i=="boolean"?window.clarity("set",o,String(i)):Array.isArray(i)&&window.clarity("set",o,i)}catch{}},identify(n,o){try{if(!I()||!r||typeof window.clarity!="function")return;if(window.clarity("identify",n),o){let i=new Set(["email","name","displayName","firstName","lastName","username","phone","address"]);for(let[s,a]of Object.entries(o))i.has(s)||(typeof a=="string"?window.clarity("set",s,a):typeof a=="number"||typeof a=="boolean"?window.clarity("set",s,String(a)):Array.isArray(a)&&window.clarity("set",s,a))}}catch{}}}}var Z="https://www.googletagmanager.com/gtag/js";function v(){return typeof window<"u"}function X(e){return new Promise(t=>{if(!v()){t();return}if(window.gtag){window.gtag("config",e),t();return}window.dataLayer=window.dataLayer??[];let r=(...o)=>{window.dataLayer.push(o)};window.gtag=r,r("js",new Date);let n=document.createElement("script");n.async=!0,n.src=`${Z}?id=${encodeURIComponent(e)}`,n.onload=()=>{r("config",e),t()},n.onerror=()=>t(),document.head.appendChild(n)})}function P(e){let t=e.measurementId,r=!1;return{async init(n){try{let i=(n??e).measurementId;if(!i||!v())return;await X(i),r=!0}catch{}},track(n){try{if(!v()||!r||typeof window.gtag!="function")return;let o=n.properties??{};n.userId&&(o.user_id=n.userId),window.gtag("event",n.name,o)}catch{}},identify(n,o){try{if(!v()||typeof window.gtag!="function")return;window.gtag("set","user_properties",{user_id:n,...o})}catch{}}}}function q(e){let t=e.endpoint,r=e.apiKey,n=e.batchSize??1,o=e.flushIntervalMs??5e3,i=[],s=null;function a(){s==null&&(s=setTimeout(()=>{s=null,l()},o))}function l(){if(i.length===0)return;let f=[...i];i=[];let c=JSON.stringify(f.length===1?f[0]:{events:f}),u={"Content-Type":"application/json"};r&&(u.Authorization=`Bearer ${r}`),typeof fetch<"u"&&fetch(t,{method:"POST",headers:u,body:c,keepalive:!0}).catch(()=>{})}return{init(f){},track(f){try{i.push(f),i.length>=n?l():o>0&&a()}catch{}},flush(){return s!=null&&(clearTimeout(s),s=null),l(),Promise.resolve()},teardown(){s!=null&&(clearTimeout(s),s=null),i.length=0}}}var Q={noop:()=>S(),clarity:e=>j(e),ga:e=>P(e),"custom-api":e=>q(e)};function k(e,t){let r=t?.factories??Q,n=t?.onError,o=[];if(!Array.isArray(e.listeners))return o;for(let i of e.listeners){if(i.enabled===!1)continue;let s=i.id;if(s==null||typeof s!="string")continue;let a=r[s];if(a==null){n?.(new Error(`Unknown listener id: ${s}`),{listenerId:s});continue}let l;try{l=a(i.config)}catch(f){let c=f instanceof Error?f:new Error(String(f));n?.(c,{listenerId:s});continue}o.push({id:s,enabled:i.enabled,listener:l,config:i.config})}return o}var ee=["drop","queue"];function te(e){if(e==null||typeof e!="object")return!1;let t=e;return typeof t.id=="string"&&t.id.trim()!==""&&"config"in t&&!("listener"in t&&t.listener!=null)}function D(e){let t=!!(e.configUrl??e.getConfig);if(typeof e.appId!="string"||e.appId.trim()==="")throw new Error("Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).");if(e.listeners!=null&&!Array.isArray(e.listeners))throw new Error("Analytics init failed: listeners must be an array.");let r=e.preInitBehavior??"drop";if(!ee.includes(r))throw new Error(`Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${r}'.`);if(r==="queue"){let i=e.queueMaxSize??100,s=e.queueTtlMs??5e3;if(typeof i!="number"||i<=0)throw new Error("Analytics init failed: queueMaxSize must be a positive number.");if(typeof s!="number"||s<=0)throw new Error("Analytics init failed: queueTtlMs must be a positive number.")}if(t)return;let n=e.listeners??[];if(n.length>0&&n.every(te)){for(let i=0;i<n.length;i++){let s=n[i];if(s.enabled!==!1&&(typeof s.id!="string"||s.id.trim()===""))throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`)}return}for(let i=0;i<n.length;i++){let s=n[i];if(s.enabled===!1)continue;if(s.listener==null||typeof s.listener!="object")throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);if(typeof s.listener.track!="function")throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`)}}var O="1.2.0";var y=null,p=null,d=null,m,g=!1;function F(){return d==null&&(d=new h({mode:"queue",maxSize:100,ttlMs:5e3})),d}function C(){if(y==null)throw new Error("Analytics not initialized.");return y}async function ne(e){if(e.getConfig)return e.getConfig();if(e.configUrl){let r=await(e.fetch??globalThis.fetch)(e.configUrl);if(!r.ok)throw new Error(`Analytics config fetch failed: ${r.status} ${r.statusText}`);let n=await r.json();if(n==null||typeof n!="object")throw new Error("Analytics config response must be a JSON object.");return n}return null}function re(e){return!Array.isArray(e)||e.length===0?!1:e.every(t=>t!=null&&typeof t=="object"&&typeof t.id=="string"&&"config"in t&&!("listener"in t&&t.listener!=null))}async function R(e){D(e);let t,r,n,o=await ne(e);if(o!=null){if(t=(e.appId??"").toString().trim(),r=e.environment??"production",!t)throw new Error("Analytics init failed: appId is required (set in init options, e.g. from environment variables).");n=k(o,{factories:e.listenerFactories,onError:e.onError})}else{t=e.appId.trim(),r=e.environment??"production";let c=e.listeners??[];re(c)?n=k({listeners:c},{factories:e.listenerFactories,onError:e.onError}):n=c}let i=e.preInitBehavior??"drop",s=e.queueMaxSize??100,a=e.queueTtlMs??5e3;m=e.onError;let l=i==="queue"?"queue":"drop";d=new h({mode:l,maxSize:s,ttlMs:a}),y=T(t,r,O,e.sessionId),p=new E;for(let c of n)if(c.enabled!==!1)try{let u=c.listener.init(c.config);u instanceof Promise&&await u,p.add(c.id,c.listener)}catch(u){let V=u instanceof Error?u:new Error(String(u));m?.(V,{listenerId:c.id})}let f={registry:p,getEnricherState:C,onError:m};d&&(i==="queue"?d.flush(c=>x(f,c),(c,u)=>{A(y,c),L(f,c,u)}):d.flush(()=>{},()=>{}),d=null),g=!0}function U(e){if(!g){F().pushTrack(e.name,e.properties,e.timestamp);return}x({registry:p,getEnricherState:C,onError:m},e)}function N(e,t){if(!g){F().pushIdentify(e,t);return}y&&A(y,e),L({registry:p,getEnricherState:C,onError:m},e,t)}async function M(){if(!g||p==null)return;let e=[];for(let{listener:t}of p.getAll())if(typeof t.flush=="function"){let r=t.flush();r instanceof Promise&&e.push(r)}await Promise.all(e)}async function z(){if(d&&d.flush(()=>{},()=>{}),y&&B(y),p){for(let{listener:e}of p.getAll())if(typeof e.teardown=="function")try{let t=e.teardown();t instanceof Promise&&await t}catch(t){let r=t instanceof Error?t:new Error(String(t));m?.(r,{})}p.clear()}d=null,y=null,p=null,m=void 0,g=!1}function $(){return g}var ie={init:R,track:U,identify:N,flush:M,reset:z,isInitialized:$};typeof globalThis<"u"&&(globalThis.UsageAnalytics=ie);function oe(){if(typeof document>"u")return null;let e=document.currentScript;if(!e)return null;let t=e.getAttribute("data-config");if(!t||typeof t!="string")return null;try{let r=JSON.parse(t);if(r&&typeof r.appId=="string"&&r.appId.trim()!=="")return r}catch{}return null}function se(){if(typeof window>"u")return;let e=oe()??window.__ANALYTICS_CONFIG__;e&&R(e).catch(()=>{})}se();})();
|
|
2
2
|
//# sourceMappingURL=usage-analytics.min.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts","../src/script-entry.ts"],"sourcesContent":["import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\r\n\r\nexport interface PreInitBufferOptions {\r\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\r\n mode: 'drop' | 'queue';\r\n maxSize: number;\r\n ttlMs: number;\r\n /** For testing: override browser check so queue works in Node. */\r\n isBrowserOverride?: boolean;\r\n}\r\n\r\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\r\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\r\n\r\n/**\r\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\r\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\r\n */\r\nexport class PreInitBuffer {\r\n private readonly options: PreInitBufferOptions;\r\n private readonly queue: BufferedItem[] = [];\r\n private readonly isBrowser: boolean;\r\n\r\n constructor(options: PreInitBufferOptions) {\r\n this.options = options;\r\n this.isBrowser =\r\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\r\n }\r\n\r\n /**\r\n * Push a track call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedTrack = {\r\n type: 'track',\r\n name,\r\n properties,\r\n timestamp,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n /**\r\n * Push an identify call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedIdentify = {\r\n type: 'identify',\r\n userId,\r\n traits,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n private push(item: BufferedItem): void {\r\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\r\n this.queue.shift(); // drop oldest (FIFO)\r\n }\r\n this.queue.push(item);\r\n }\r\n\r\n /**\r\n * Replay queued items: all identify first (order), then all track (order).\r\n * Items older than ttlMs are dropped. Then clear the queue.\r\n */\r\n flush(\r\n onTrack: FlushTrackHandler,\r\n onIdentify: FlushIdentifyHandler\r\n ): void {\r\n if (!this.isBrowser || this.queue.length === 0) {\r\n this.queue.length = 0;\r\n return;\r\n }\r\n const now = Date.now();\r\n const cutoff = now - this.options.ttlMs;\r\n const identifyItems = this.queue.filter(\r\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\r\n );\r\n const trackItems = this.queue.filter(\r\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\r\n );\r\n for (const item of identifyItems) {\r\n onIdentify(item.userId, item.traits);\r\n }\r\n for (const item of trackItems) {\r\n onTrack({\r\n name: item.name,\r\n properties: item.properties,\r\n timestamp: item.timestamp,\r\n });\r\n }\r\n this.queue.length = 0;\r\n }\r\n\r\n /** Number of items currently queued (for tests). */\r\n get length(): number {\r\n return this.queue.length;\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\n\r\nexport interface RegisteredListener {\r\n id?: string;\r\n listener: AnalyticsListener;\r\n}\r\n\r\n/**\r\n * Holds enabled listener instances. Populated at init.\r\n */\r\nexport class ListenerRegistry {\r\n private readonly listeners: RegisteredListener[] = [];\r\n\r\n add(id: string | undefined, listener: AnalyticsListener): void {\r\n this.listeners.push({ id, listener });\r\n }\r\n\r\n getAll(): RegisteredListener[] {\r\n return [...this.listeners];\r\n }\r\n\r\n clear(): void {\r\n this.listeners.length = 0;\r\n }\r\n}\r\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\r\n\r\n/**\r\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\r\n */\r\nexport function generateId(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID();\r\n }\r\n return fallbackUuidV4();\r\n}\r\n\r\nfunction fallbackUuidV4(): string {\r\n const hex = '0123456789abcdef';\r\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\r\n result = result.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return hex[v];\r\n });\r\n return result;\r\n}\r\n\r\n/**\r\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\r\n */\r\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\r\n if (providedSessionId) return providedSessionId;\r\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\r\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\r\n try {\r\n const existing = sessionStorage.getItem(key);\r\n if (existing) return existing;\r\n const newId = generateId();\r\n sessionStorage.setItem(key, newId);\r\n return newId;\r\n } catch {\r\n return generateId();\r\n }\r\n }\r\n return generateId();\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport { generateId, getOrCreateSessionId } from './session.js';\r\n\r\n/** Raw track payload from app (before enrichment). */\r\nexport interface TrackPayload {\r\n name: string;\r\n properties?: Record<string, unknown>;\r\n timestamp?: string;\r\n}\r\n\r\nexport interface EnricherState {\r\n appId: string;\r\n environment: string;\r\n sdkVersion: string;\r\n sessionId?: string;\r\n userId?: string;\r\n /** Provided at init or generated per session. */\r\n resolvedSessionId?: string;\r\n}\r\n\r\nconst ENVELOPE_VERSION = 1;\r\n\r\n/**\r\n * Creates enricher state from init config. Call once at init.\r\n */\r\nexport function createEnricherState(\r\n appId: string,\r\n environment: string,\r\n sdkVersion: string,\r\n sessionId?: string\r\n): EnricherState {\r\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\r\n return {\r\n appId,\r\n environment,\r\n sdkVersion,\r\n sessionId,\r\n resolvedSessionId,\r\n };\r\n}\r\n\r\n/**\r\n * Enrich a raw track payload into a full EventEnvelope.\r\n */\r\nexport function enrich(\r\n payload: TrackPayload,\r\n state: EnricherState\r\n): EventEnvelope {\r\n const timestamp = payload.timestamp ?? new Date().toISOString();\r\n const envelope: EventEnvelope = {\r\n name: payload.name,\r\n timestamp,\r\n eventId: generateId(),\r\n appId: state.appId,\r\n environment: state.environment,\r\n sdkVersion: state.sdkVersion,\r\n version: ENVELOPE_VERSION,\r\n };\r\n if (payload.properties != null) envelope.properties = payload.properties;\r\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\r\n if (state.userId) envelope.userId = state.userId;\r\n return envelope;\r\n}\r\n\r\n/**\r\n * Set userId (and optional traits) on state after identify. Cleared on reset.\r\n */\r\nexport function setUserId(state: EnricherState, userId: string): void {\r\n state.userId = userId;\r\n}\r\n\r\n/**\r\n * Clear userId from state (e.g. on reset).\r\n */\r\nexport function clearUserId(state: EnricherState): void {\r\n state.userId = undefined;\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport type { OnErrorContext } from '../types/config.js';\r\nimport type { ListenerRegistry } from './registry.js';\r\nimport type { EnricherState } from '../enricher/enricher.js';\r\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\r\n\r\nexport interface DispatcherDeps {\r\n registry: ListenerRegistry;\r\n getEnricherState: () => EnricherState;\r\n onError?: (error: Error, context?: OnErrorContext) => void;\r\n}\r\n\r\n/**\r\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\r\n */\r\nexport function dispatchTrack(\r\n deps: DispatcherDeps,\r\n payload: TrackPayload\r\n): void {\r\n const state = deps.getEnricherState();\r\n const envelope = enrich(payload, state);\r\n dispatchEnvelope(deps, envelope, 'track');\r\n}\r\n\r\n/**\r\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\r\n */\r\nfunction dispatchEnvelope(\r\n deps: DispatcherDeps,\r\n envelope: EventEnvelope,\r\n kind: 'track'\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n try {\r\n listener.track(envelope);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Call identify on all listeners that implement it (try/catch per listener).\r\n */\r\nexport function dispatchIdentify(\r\n deps: DispatcherDeps,\r\n userId: string,\r\n traits?: Record<string, unknown>\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n if (typeof listener.identify !== 'function') continue;\r\n try {\r\n listener.identify(userId, traits);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\n/**\r\n * No-op listener: implements the interface with no side effects.\r\n * Use when analytics is disabled or in tests.\r\n */\r\nexport function createNoopListener(): AnalyticsListener {\r\n return {\r\n init(_config: unknown): void {\r\n // no-op\r\n },\r\n track(_envelope: EventEnvelope): void {\r\n // no-op\r\n },\r\n };\r\n}\r\n\r\n/** Singleton no-op listener instance. */\r\nexport const noopListener: AnalyticsListener = createNoopListener();\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, _traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('identify', userId, undefined, undefined, undefined);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface GAListenerConfig {\r\n measurementId: string;\r\n}\r\n\r\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\r\n\r\ndeclare global {\r\n interface Window {\r\n dataLayer?: unknown[];\r\n gtag?: (...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadGtagScript(measurementId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (window.gtag) {\r\n window.gtag('config', measurementId);\r\n resolve();\r\n return;\r\n }\r\n window.dataLayer = window.dataLayer ?? [];\r\n const gtag = (...args: unknown[]) => {\r\n window.dataLayer!.push(args);\r\n };\r\n window.gtag = gtag;\r\n gtag('js', new Date());\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\r\n script.onload = () => {\r\n gtag('config', measurementId);\r\n resolve();\r\n };\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Google Analytics 4 via gtag.\r\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\r\n const measurementId = config.measurementId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as GAListenerConfig;\r\n const id = c.measurementId;\r\n if (!id || !isBrowser()) return;\r\n await loadGtagScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\r\n const params = envelope.properties ?? {};\r\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\r\n window.gtag('event', envelope.name, params);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || typeof window.gtag !== 'function') return;\r\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface CustomApiListenerConfig {\r\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\r\n endpoint: string;\r\n /** Optional API key or bearer token. */\r\n apiKey?: string;\r\n /** Optional: batch size before sending. Default 1 (no batching). */\r\n batchSize?: number;\r\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\r\n flushIntervalMs?: number;\r\n}\r\n\r\nconst DEFAULT_BATCH_SIZE = 1;\r\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\r\n\r\n/**\r\n * Listener that POSTs events to a configurable endpoint.\r\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\r\n */\r\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\r\n const endpoint = config.endpoint;\r\n const apiKey = config.apiKey;\r\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\r\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\r\n\r\n let batch: EventEnvelope[] = [];\r\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n function scheduleFlush(): void {\r\n if (flushTimer != null) return;\r\n flushTimer = setTimeout(() => {\r\n flushTimer = null;\r\n sendBatch();\r\n }, flushIntervalMs);\r\n }\r\n\r\n function sendBatch(): void {\r\n if (batch.length === 0) return;\r\n const toSend = [...batch];\r\n batch = [];\r\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\r\n if (typeof fetch !== 'undefined') {\r\n fetch(endpoint, {\r\n method: 'POST',\r\n headers,\r\n body,\r\n keepalive: true,\r\n }).catch(() => {});\r\n }\r\n }\r\n\r\n return {\r\n init(_config: unknown): void {\r\n // config was passed at createCustomApiListener\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n batch.push(envelope);\r\n if (batch.length >= batchSize) {\r\n sendBatch();\r\n } else if (flushIntervalMs > 0) {\r\n scheduleFlush();\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n flush(): Promise<void> {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n sendBatch();\r\n return Promise.resolve();\r\n },\r\n teardown(): void {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n batch.length = 0;\r\n },\r\n };\r\n}\r\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\r\nimport type { AnalyticsListener } from '../types/listener.js';\r\nimport { createNoopListener } from './noop.js';\r\nimport { createClarityListener } from './clarity.js';\r\nimport { createGAListener } from './ga.js';\r\nimport { createCustomApiListener } from './custom-api.js';\r\n\r\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\r\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\r\n\r\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\r\n noop: () => createNoopListener(),\r\n clarity: (config) => createClarityListener(config as { projectId: string }),\r\n ga: (config) => createGAListener(config as { measurementId: string }),\r\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\r\n};\r\n\r\n/**\r\n * Returns the built-in listener factory for the given id, or undefined if unknown.\r\n */\r\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\r\n return BUILT_IN_FACTORIES[id];\r\n}\r\n\r\nexport interface BuildListenersFromRemoteConfigOptions {\r\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\r\n factories?: Record<string, ListenerFactory>;\r\n /** Called when a remote listener id has no factory (unknown id). */\r\n onError?: (error: Error, context?: { listenerId?: string }) => void;\r\n}\r\n\r\n/**\r\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\r\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\r\n */\r\nexport function buildListenersFromRemoteConfig(\r\n remote: RemoteAnalyticsConfig,\r\n options?: BuildListenersFromRemoteConfigOptions\r\n): ListenerEntry[] {\r\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\r\n const onError = options?.onError;\r\n const entries: ListenerEntry[] = [];\r\n\r\n if (!Array.isArray(remote.listeners)) {\r\n return entries;\r\n }\r\n\r\n for (const entry of remote.listeners) {\r\n if (entry.enabled === false) continue;\r\n const id = entry.id;\r\n if (id == null || typeof id !== 'string') continue;\r\n\r\n const factory = factories[id];\r\n if (factory == null) {\r\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\r\n continue;\r\n }\r\n\r\n let listener: AnalyticsListener;\r\n try {\r\n listener = factory(entry.config);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n continue;\r\n }\r\n\r\n entries.push({\r\n id,\r\n enabled: entry.enabled,\r\n listener,\r\n config: entry.config,\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n PreInitBehavior,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport type { AnalyticsListener } from './types/listener.js';\r\n\r\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\r\n\r\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\r\n if (entry == null || typeof entry !== 'object') return false;\r\n const o = entry as Record<string, unknown>;\r\n return (\r\n typeof o.id === 'string' &&\r\n o.id.trim() !== '' &&\r\n 'config' in o &&\r\n !('listener' in o && o.listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Validate init config. Throws with clear message on invalid config.\r\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\r\n */\r\nexport function validateConfig(options: AnalyticsInitConfig): void {\r\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\r\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\r\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\r\n }\r\n if (options.listeners != null && !Array.isArray(options.listeners)) {\r\n throw new Error('Analytics init failed: listeners must be an array.');\r\n }\r\n const behavior = options.preInitBehavior ?? 'drop';\r\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\r\n throw new Error(\r\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\r\n );\r\n }\r\n if (behavior === 'queue') {\r\n const maxSize = options.queueMaxSize ?? 100;\r\n const ttlMs = options.queueTtlMs ?? 5000;\r\n if (typeof maxSize !== 'number' || maxSize <= 0) {\r\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\r\n }\r\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\r\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\r\n }\r\n }\r\n if (useRemoteConfig) {\r\n return;\r\n }\r\n const listeners = options.listeners ?? [];\r\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\r\n if (isRemoteShape) {\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as RemoteListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\r\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\r\n }\r\n }\r\n return;\r\n }\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as ListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (entry.listener == null || typeof entry.listener !== 'object') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\r\n }\r\n const listener = entry.listener as AnalyticsListener;\r\n if (typeof listener.track !== 'function') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\r\n }\r\n }\r\n}\r\n","/** SDK version; set at build time or from package.json. */\r\nexport const SDK_VERSION = '1.0.0';\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n OnErrorContext,\r\n RemoteAnalyticsConfig,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\r\nimport { ListenerRegistry } from './dispatcher/registry.js';\r\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\r\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\r\nimport {\r\n createEnricherState,\r\n setUserId,\r\n clearUserId,\r\n type EnricherState,\r\n} from './enricher/enricher.js';\r\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\r\nimport { validateConfig } from './validate-config.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nlet enricherState: EnricherState | null = null;\r\nlet registry: ListenerRegistry | null = null;\r\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\r\nlet preInitBuffer: PreInitBuffer | null = null;\r\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\r\nlet initialized = false;\r\n\r\n/** Default buffer before init: queue mode so events before init can be replayed. */\r\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\r\n if (preInitBuffer == null) {\r\n preInitBuffer = new PreInitBuffer({\r\n mode: 'queue',\r\n maxSize: 100,\r\n ttlMs: 5000,\r\n });\r\n }\r\n return preInitBuffer;\r\n}\r\n\r\nfunction getEnricherState(): EnricherState {\r\n if (enricherState == null) {\r\n throw new Error('Analytics not initialized.');\r\n }\r\n return enricherState;\r\n}\r\n\r\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\r\n if (options.getConfig) {\r\n return options.getConfig();\r\n }\r\n if (options.configUrl) {\r\n const fetchFn = options.fetch ?? globalThis.fetch;\r\n const res = await fetchFn(options.configUrl);\r\n if (!res.ok) {\r\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\r\n }\r\n const json = await res.json();\r\n if (json == null || typeof json !== 'object') {\r\n throw new Error('Analytics config response must be a JSON object.');\r\n }\r\n return json as RemoteAnalyticsConfig;\r\n }\r\n return null;\r\n}\r\n\r\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\r\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\r\n return listeners.every(\r\n (entry): entry is RemoteListenerEntry =>\r\n entry != null &&\r\n typeof entry === 'object' &&\r\n typeof (entry as RemoteListenerEntry).id === 'string' &&\r\n 'config' in entry &&\r\n !('listener' in entry && (entry as ListenerEntry).listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Initialize the SDK. Idempotent; calling again re-applies config.\r\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\r\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\r\n */\r\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\r\n validateConfig(options);\r\n\r\n let appId: string;\r\n let environment: string;\r\n let listeners: ListenerEntry[];\r\n\r\n const remote = await fetchRemoteConfig(options);\r\n if (remote != null) {\r\n appId = (options.appId ?? '').toString().trim();\r\n environment = options.environment ?? 'production';\r\n if (!appId) {\r\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\r\n }\r\n listeners = buildListenersFromRemoteConfig(remote, {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n });\r\n } else {\r\n appId = options.appId.trim();\r\n environment = options.environment ?? 'production';\r\n const rawListeners = options.listeners ?? [];\r\n if (isInlineRemoteShape(rawListeners)) {\r\n listeners = buildListenersFromRemoteConfig(\r\n { listeners: rawListeners },\r\n {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n }\r\n );\r\n } else {\r\n listeners = rawListeners as ListenerEntry[];\r\n }\r\n }\r\n\r\n const preInitBehavior = options.preInitBehavior ?? 'drop';\r\n const queueMaxSize = options.queueMaxSize ?? 100;\r\n const queueTtlMs = options.queueTtlMs ?? 5000;\r\n onError = options.onError;\r\n\r\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\r\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\r\n\r\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\r\n registry = new ListenerRegistry();\r\n\r\n for (const entry of listeners) {\r\n if (entry.enabled === false) continue;\r\n try {\r\n const initResult = entry.listener.init(entry.config);\r\n if (initResult instanceof Promise) {\r\n await initResult;\r\n }\r\n registry.add(entry.id, entry.listener);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: entry.id });\r\n }\r\n }\r\n\r\n const deps: DispatcherDeps = {\r\n registry,\r\n getEnricherState,\r\n onError,\r\n };\r\n\r\n if (preInitBuffer) {\r\n if (preInitBehavior === 'queue') {\r\n preInitBuffer.flush(\r\n (payload) => dispatchTrack(deps, payload),\r\n (userId, traits) => {\r\n setUserId(enricherState!, userId);\r\n dispatchIdentify(deps, userId, traits);\r\n }\r\n );\r\n } else {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n preInitBuffer = null;\r\n }\r\n\r\n initialized = true;\r\n}\r\n\r\n/**\r\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\r\n return;\r\n }\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchTrack(deps, event);\r\n}\r\n\r\n/**\r\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\r\n return;\r\n }\r\n if (enricherState) setUserId(enricherState, userId);\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchIdentify(deps, userId, traits);\r\n}\r\n\r\n/**\r\n * Flush listeners that batch (e.g. before page unload).\r\n */\r\nexport async function flush(): Promise<void> {\r\n if (!initialized || registry == null) return;\r\n const promises: Promise<void>[] = [];\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.flush === 'function') {\r\n const result = listener.flush();\r\n if (result instanceof Promise) promises.push(result);\r\n }\r\n }\r\n await Promise.all(promises);\r\n}\r\n\r\n/**\r\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\r\n */\r\nexport async function reset(): Promise<void> {\r\n if (preInitBuffer) {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n if (enricherState) clearUserId(enricherState);\r\n if (registry) {\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.teardown === 'function') {\r\n try {\r\n const result = listener.teardown();\r\n if (result instanceof Promise) await result;\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, {});\r\n }\r\n }\r\n }\r\n registry.clear();\r\n }\r\n preInitBuffer = null;\r\n enricherState = null;\r\n registry = null;\r\n onError = undefined;\r\n initialized = false;\r\n}\r\n\r\nexport function isInitialized(): boolean {\r\n return initialized;\r\n}\r\n","/**\r\n * Script entry for browser <script src=\"...\"> usage.\r\n * Exposes UsageAnalytics on globalThis (window). Auto-inits from config in the script tag (data-config) or window.__ANALYTICS_CONFIG__.\r\n */\r\nimport { init, track, identify, flush, reset, isInitialized } from './facade.js';\r\nimport type { AnalyticsInitConfig } from './types/config.js';\r\n\r\ndeclare global {\r\n interface Window {\r\n UsageAnalytics: {\r\n init: typeof init;\r\n track: typeof track;\r\n identify: typeof identify;\r\n flush: typeof flush;\r\n reset: typeof reset;\r\n isInitialized: typeof isInitialized;\r\n };\r\n __ANALYTICS_CONFIG__?: AnalyticsInitConfig;\r\n }\r\n}\r\n\r\nconst api = {\r\n init,\r\n track,\r\n identify,\r\n flush,\r\n reset,\r\n isInitialized,\r\n};\r\n\r\nif (typeof globalThis !== 'undefined') {\r\n (globalThis as unknown as { UsageAnalytics: typeof api }).UsageAnalytics = api;\r\n}\r\n\r\nfunction getConfigFromScriptTag(): AnalyticsInitConfig | null {\r\n if (typeof document === 'undefined') return null;\r\n const script = document.currentScript as HTMLScriptElement | null;\r\n if (!script) return null;\r\n const raw = script.getAttribute('data-config');\r\n if (!raw || typeof raw !== 'string') return null;\r\n try {\r\n const parsed = JSON.parse(raw) as AnalyticsInitConfig;\r\n if (parsed && typeof parsed.appId === 'string' && parsed.appId.trim() !== '') {\r\n return parsed;\r\n }\r\n } catch {\r\n // Invalid JSON\r\n }\r\n return null;\r\n}\r\n\r\nfunction autoInit(): void {\r\n if (typeof window === 'undefined') return;\r\n const config = getConfigFromScriptTag() ?? window.__ANALYTICS_CONFIG__;\r\n if (config) {\r\n init(config).catch(() => {\r\n // Silent fail for auto-init; app can call init() manually with onError if needed\r\n });\r\n }\r\n}\r\n\r\nautoInit();\r\n"],"mappings":"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","names":["PreInitBuffer","options","name","properties","timestamp","item","userId","traits","onTrack","onIdentify","cutoff","identifyItems","trackItems","ListenerRegistry","id","listener","STORAGE_KEY_PREFIX","generateId","fallbackUuidV4","hex","result","c","r","v","getOrCreateSessionId","appId","providedSessionId","key","existing","newId","ENVELOPE_VERSION","createEnricherState","appId","environment","sdkVersion","sessionId","resolvedSessionId","getOrCreateSessionId","enrich","payload","state","timestamp","envelope","generateId","setUserId","userId","clearUserId","dispatchTrack","deps","payload","state","envelope","enrich","dispatchEnvelope","kind","registry","onError","id","listener","err","error","dispatchIdentify","userId","traits","createNoopListener","_config","_envelope","noopListener","CLARITY_SCRIPT_BASE","isBrowser","loadClarityScript","projectId","resolve","w","args","fn","script","createClarityListener","config","ready","cfg","id","envelope","key","value","userId","_traits","GTAG_SCRIPT_URL","isBrowser","loadGtagScript","measurementId","resolve","gtag","args","script","createGAListener","config","ready","cfg","id","envelope","params","userId","traits","createCustomApiListener","config","endpoint","apiKey","batchSize","flushIntervalMs","batch","flushTimer","scheduleFlush","sendBatch","toSend","body","headers","_config","envelope","BUILT_IN_FACTORIES","createNoopListener","config","createClarityListener","createGAListener","createCustomApiListener","buildListenersFromRemoteConfig","remote","options","factories","BUILT_IN_FACTORIES","onError","entries","entry","id","factory","listener","err","error","VALID_PRE_INIT_BEHAVIORS","isRemoteListenerEntry","entry","o","validateConfig","options","useRemoteConfig","behavior","maxSize","ttlMs","listeners","SDK_VERSION","enricherState","registry","preInitBuffer","onError","initialized","getOrCreatePreInitBuffer","PreInitBuffer","getEnricherState","fetchRemoteConfig","options","res","json","isInlineRemoteShape","listeners","entry","init","validateConfig","appId","environment","remote","buildListenersFromRemoteConfig","rawListeners","preInitBehavior","queueMaxSize","queueTtlMs","mode","createEnricherState","SDK_VERSION","ListenerRegistry","initResult","err","error","deps","payload","dispatchTrack","userId","traits","setUserId","dispatchIdentify","track","event","identify","flush","promises","listener","result","reset","clearUserId","isInitialized","api","init","track","identify","flush","reset","isInitialized","getConfigFromScriptTag","script","raw","parsed","autoInit","config"]}
|
|
1
|
+
{"version":3,"sources":["../src/buffer/pre-init-buffer.ts","../src/dispatcher/registry.ts","../src/enricher/session.ts","../src/enricher/enricher.ts","../src/dispatcher/dispatcher.ts","../src/listeners/noop.ts","../src/listeners/clarity.ts","../src/listeners/ga.ts","../src/listeners/custom-api.ts","../src/listeners/registry.ts","../src/validate-config.ts","../src/version.ts","../src/facade.ts","../src/script-entry.ts"],"sourcesContent":["import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\r\n\r\nexport interface PreInitBufferOptions {\r\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\r\n mode: 'drop' | 'queue';\r\n maxSize: number;\r\n ttlMs: number;\r\n /** For testing: override browser check so queue works in Node. */\r\n isBrowserOverride?: boolean;\r\n}\r\n\r\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\r\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\r\n\r\n/**\r\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\r\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\r\n */\r\nexport class PreInitBuffer {\r\n private readonly options: PreInitBufferOptions;\r\n private readonly queue: BufferedItem[] = [];\r\n private readonly isBrowser: boolean;\r\n\r\n constructor(options: PreInitBufferOptions) {\r\n this.options = options;\r\n this.isBrowser =\r\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\r\n }\r\n\r\n /**\r\n * Push a track call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedTrack = {\r\n type: 'track',\r\n name,\r\n properties,\r\n timestamp,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n /**\r\n * Push an identify call. No-op if mode is drop or not browser (SSR).\r\n */\r\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\r\n if (this.options.mode === 'drop' || !this.isBrowser) return;\r\n const item: BufferedIdentify = {\r\n type: 'identify',\r\n userId,\r\n traits,\r\n createdAt: Date.now(),\r\n };\r\n this.push(item);\r\n }\r\n\r\n private push(item: BufferedItem): void {\r\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\r\n this.queue.shift(); // drop oldest (FIFO)\r\n }\r\n this.queue.push(item);\r\n }\r\n\r\n /**\r\n * Replay queued items: all identify first (order), then all track (order).\r\n * Items older than ttlMs are dropped. Then clear the queue.\r\n */\r\n flush(\r\n onTrack: FlushTrackHandler,\r\n onIdentify: FlushIdentifyHandler\r\n ): void {\r\n if (!this.isBrowser || this.queue.length === 0) {\r\n this.queue.length = 0;\r\n return;\r\n }\r\n const now = Date.now();\r\n const cutoff = now - this.options.ttlMs;\r\n const identifyItems = this.queue.filter(\r\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\r\n );\r\n const trackItems = this.queue.filter(\r\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\r\n );\r\n for (const item of identifyItems) {\r\n onIdentify(item.userId, item.traits);\r\n }\r\n for (const item of trackItems) {\r\n onTrack({\r\n name: item.name,\r\n properties: item.properties,\r\n timestamp: item.timestamp,\r\n });\r\n }\r\n this.queue.length = 0;\r\n }\r\n\r\n /** Number of items currently queued (for tests). */\r\n get length(): number {\r\n return this.queue.length;\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\n\r\nexport interface RegisteredListener {\r\n id?: string;\r\n listener: AnalyticsListener;\r\n}\r\n\r\n/**\r\n * Holds enabled listener instances. Populated at init.\r\n */\r\nexport class ListenerRegistry {\r\n private readonly listeners: RegisteredListener[] = [];\r\n\r\n add(id: string | undefined, listener: AnalyticsListener): void {\r\n this.listeners.push({ id, listener });\r\n }\r\n\r\n getAll(): RegisteredListener[] {\r\n return [...this.listeners];\r\n }\r\n\r\n clear(): void {\r\n this.listeners.length = 0;\r\n }\r\n}\r\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\r\n\r\n/**\r\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\r\n */\r\nexport function generateId(): string {\r\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\r\n return crypto.randomUUID();\r\n }\r\n return fallbackUuidV4();\r\n}\r\n\r\nfunction fallbackUuidV4(): string {\r\n const hex = '0123456789abcdef';\r\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\r\n result = result.replace(/[xy]/g, (c) => {\r\n const r = (Math.random() * 16) | 0;\r\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\r\n return hex[v];\r\n });\r\n return result;\r\n}\r\n\r\n/**\r\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\r\n */\r\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\r\n if (providedSessionId) return providedSessionId;\r\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\r\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\r\n try {\r\n const existing = sessionStorage.getItem(key);\r\n if (existing) return existing;\r\n const newId = generateId();\r\n sessionStorage.setItem(key, newId);\r\n return newId;\r\n } catch {\r\n return generateId();\r\n }\r\n }\r\n return generateId();\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport { generateId, getOrCreateSessionId } from './session.js';\r\n\r\n/** Raw track payload from app (before enrichment). */\r\nexport interface TrackPayload {\r\n name: string;\r\n properties?: Record<string, unknown>;\r\n timestamp?: string;\r\n}\r\n\r\nexport interface EnricherState {\r\n appId: string;\r\n environment: string;\r\n sdkVersion: string;\r\n sessionId?: string;\r\n userId?: string;\r\n /** Provided at init or generated per session. */\r\n resolvedSessionId?: string;\r\n}\r\n\r\nconst ENVELOPE_VERSION = 1;\r\n\r\n/**\r\n * Creates enricher state from init config. Call once at init.\r\n */\r\nexport function createEnricherState(\r\n appId: string,\r\n environment: string,\r\n sdkVersion: string,\r\n sessionId?: string\r\n): EnricherState {\r\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\r\n return {\r\n appId,\r\n environment,\r\n sdkVersion,\r\n sessionId,\r\n resolvedSessionId,\r\n };\r\n}\r\n\r\n/**\r\n * Enrich a raw track payload into a full EventEnvelope.\r\n */\r\nexport function enrich(\r\n payload: TrackPayload,\r\n state: EnricherState\r\n): EventEnvelope {\r\n const timestamp = payload.timestamp ?? new Date().toISOString();\r\n const envelope: EventEnvelope = {\r\n name: payload.name,\r\n timestamp,\r\n eventId: generateId(),\r\n appId: state.appId,\r\n environment: state.environment,\r\n sdkVersion: state.sdkVersion,\r\n version: ENVELOPE_VERSION,\r\n };\r\n if (payload.properties != null) envelope.properties = payload.properties;\r\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\r\n if (state.userId) envelope.userId = state.userId;\r\n return envelope;\r\n}\r\n\r\n/**\r\n * Set userId (and optional traits) on state after identify. Cleared on reset.\r\n */\r\nexport function setUserId(state: EnricherState, userId: string): void {\r\n state.userId = userId;\r\n}\r\n\r\n/**\r\n * Clear userId from state (e.g. on reset).\r\n */\r\nexport function clearUserId(state: EnricherState): void {\r\n state.userId = undefined;\r\n}\r\n","import type { EventEnvelope } from '../types/envelope.js';\r\nimport type { OnErrorContext } from '../types/config.js';\r\nimport type { ListenerRegistry } from './registry.js';\r\nimport type { EnricherState } from '../enricher/enricher.js';\r\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\r\n\r\nexport interface DispatcherDeps {\r\n registry: ListenerRegistry;\r\n getEnricherState: () => EnricherState;\r\n onError?: (error: Error, context?: OnErrorContext) => void;\r\n}\r\n\r\n/**\r\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\r\n */\r\nexport function dispatchTrack(\r\n deps: DispatcherDeps,\r\n payload: TrackPayload\r\n): void {\r\n const state = deps.getEnricherState();\r\n const envelope = enrich(payload, state);\r\n dispatchEnvelope(deps, envelope, 'track');\r\n}\r\n\r\n/**\r\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\r\n */\r\nfunction dispatchEnvelope(\r\n deps: DispatcherDeps,\r\n envelope: EventEnvelope,\r\n kind: 'track'\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n try {\r\n listener.track(envelope);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Call identify on all listeners that implement it (try/catch per listener).\r\n */\r\nexport function dispatchIdentify(\r\n deps: DispatcherDeps,\r\n userId: string,\r\n traits?: Record<string, unknown>\r\n): void {\r\n const { registry, onError } = deps;\r\n for (const { id, listener } of registry.getAll()) {\r\n if (typeof listener.identify !== 'function') continue;\r\n try {\r\n listener.identify(userId, traits);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n }\r\n }\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\n/**\r\n * No-op listener: implements the interface with no side effects.\r\n * Use when analytics is disabled or in tests.\r\n */\r\nexport function createNoopListener(): AnalyticsListener {\r\n return {\r\n init(_config: unknown): void {\r\n // no-op\r\n },\r\n track(_envelope: EventEnvelope): void {\r\n // no-op\r\n },\r\n };\r\n}\r\n\r\n/** Singleton no-op listener instance. */\r\nexport const noopListener: AnalyticsListener = createNoopListener();\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface ClarityListenerConfig {\r\n projectId: string;\r\n}\r\n\r\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\r\n\r\ndeclare global {\r\n interface Window {\r\n clarity?: (cmd: string, ...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadClarityScript(projectId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (typeof window.clarity === 'function') {\r\n resolve();\r\n return;\r\n }\r\n // Set up the Clarity queue stub BEFORE loading the tag script.\r\n // The tag script at clarity.ms/tag/{id} expects window.clarity to exist\r\n // as a queue function so it can process early calls and bootstrap properly.\r\n // This mirrors the standard Clarity snippet:\r\n // window.clarity = window.clarity || function(){ (window.clarity.q = window.clarity.q || []).push(arguments) };\r\n const w = window as unknown as Record<string, unknown>;\r\n w['clarity'] = w['clarity'] || function (...args: unknown[]) {\r\n const fn = w['clarity'] as unknown as { q?: unknown[][] };\r\n (fn.q = fn.q || []).push(args);\r\n };\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;\r\n script.onload = () => resolve();\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Microsoft Clarity via script injection.\r\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\r\n const projectId = config.projectId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as ClarityListenerConfig;\r\n const id = c.projectId;\r\n if (!id || !isBrowser()) return;\r\n await loadClarityScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n window.clarity('event', envelope.name);\r\n if (envelope.properties && typeof window.clarity === 'function') {\r\n for (const [key, value] of Object.entries(envelope.properties)) {\r\n // clarity('set', key, value) accepts string values.\r\n // Convert numbers and booleans to strings so they are not silently dropped.\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\r\n\r\n // PII Safety: Only pass the pseudonymous userId to Clarity's identify.\r\n // Do NOT forward PII fields (email, name, displayName) to a third-party service.\r\n // The userId (GUID) can be correlated back to a real user in your own system.\r\n window.clarity('identify', userId);\r\n\r\n // Forward only non-PII traits as Clarity custom tags.\r\n // Keys containing PII are stripped to stay compliant with GDPR / CCPA.\r\n if (traits) {\r\n const piiKeys = new Set(['email', 'name', 'displayName', 'firstName', 'lastName', 'username', 'phone', 'address']);\r\n for (const [key, value] of Object.entries(traits)) {\r\n if (piiKeys.has(key)) continue;\r\n if (typeof value === 'string') {\r\n window.clarity('set', key, value);\r\n } else if (typeof value === 'number' || typeof value === 'boolean') {\r\n window.clarity('set', key, String(value));\r\n } else if (Array.isArray(value)) {\r\n window.clarity('set', key, value);\r\n }\r\n }\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface GAListenerConfig {\r\n measurementId: string;\r\n}\r\n\r\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\r\n\r\ndeclare global {\r\n interface Window {\r\n dataLayer?: unknown[];\r\n gtag?: (...args: unknown[]) => void;\r\n }\r\n}\r\n\r\nfunction isBrowser(): boolean {\r\n return typeof window !== 'undefined';\r\n}\r\n\r\nfunction loadGtagScript(measurementId: string): Promise<void> {\r\n return new Promise((resolve) => {\r\n if (!isBrowser()) {\r\n resolve();\r\n return;\r\n }\r\n if (window.gtag) {\r\n window.gtag('config', measurementId);\r\n resolve();\r\n return;\r\n }\r\n window.dataLayer = window.dataLayer ?? [];\r\n const gtag = (...args: unknown[]) => {\r\n window.dataLayer!.push(args);\r\n };\r\n window.gtag = gtag;\r\n gtag('js', new Date());\r\n const script = document.createElement('script');\r\n script.async = true;\r\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\r\n script.onload = () => {\r\n gtag('config', measurementId);\r\n resolve();\r\n };\r\n script.onerror = () => resolve();\r\n document.head.appendChild(script);\r\n });\r\n}\r\n\r\n/**\r\n * Listener that sends events to Google Analytics 4 via gtag.\r\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\r\n * No-op when typeof window === 'undefined' (SSR).\r\n */\r\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\r\n const measurementId = config.measurementId;\r\n let ready = false;\r\n\r\n return {\r\n async init(cfg: unknown): Promise<void> {\r\n try {\r\n const c = (cfg ?? config) as GAListenerConfig;\r\n const id = c.measurementId;\r\n if (!id || !isBrowser()) return;\r\n await loadGtagScript(id);\r\n ready = true;\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\r\n const params = envelope.properties ?? {};\r\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\r\n window.gtag('event', envelope.name, params);\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n identify(userId: string, traits?: Record<string, unknown>): void {\r\n try {\r\n if (!isBrowser() || typeof window.gtag !== 'function') return;\r\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n };\r\n}\r\n","import type { AnalyticsListener } from '../types/listener.js';\r\nimport type { EventEnvelope } from '../types/envelope.js';\r\n\r\nexport interface CustomApiListenerConfig {\r\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\r\n endpoint: string;\r\n /** Optional API key or bearer token. */\r\n apiKey?: string;\r\n /** Optional: batch size before sending. Default 1 (no batching). */\r\n batchSize?: number;\r\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\r\n flushIntervalMs?: number;\r\n}\r\n\r\nconst DEFAULT_BATCH_SIZE = 1;\r\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\r\n\r\n/**\r\n * Listener that POSTs events to a configurable endpoint.\r\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\r\n */\r\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\r\n const endpoint = config.endpoint;\r\n const apiKey = config.apiKey;\r\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\r\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\r\n\r\n let batch: EventEnvelope[] = [];\r\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\r\n\r\n function scheduleFlush(): void {\r\n if (flushTimer != null) return;\r\n flushTimer = setTimeout(() => {\r\n flushTimer = null;\r\n sendBatch();\r\n }, flushIntervalMs);\r\n }\r\n\r\n function sendBatch(): void {\r\n if (batch.length === 0) return;\r\n const toSend = [...batch];\r\n batch = [];\r\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\r\n const headers: Record<string, string> = {\r\n 'Content-Type': 'application/json',\r\n };\r\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\r\n if (typeof fetch !== 'undefined') {\r\n fetch(endpoint, {\r\n method: 'POST',\r\n headers,\r\n body,\r\n keepalive: true,\r\n }).catch(() => {});\r\n }\r\n }\r\n\r\n return {\r\n init(_config: unknown): void {\r\n // config was passed at createCustomApiListener\r\n },\r\n track(envelope: EventEnvelope): void {\r\n try {\r\n batch.push(envelope);\r\n if (batch.length >= batchSize) {\r\n sendBatch();\r\n } else if (flushIntervalMs > 0) {\r\n scheduleFlush();\r\n }\r\n } catch {\r\n // swallow\r\n }\r\n },\r\n flush(): Promise<void> {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n sendBatch();\r\n return Promise.resolve();\r\n },\r\n teardown(): void {\r\n if (flushTimer != null) {\r\n clearTimeout(flushTimer);\r\n flushTimer = null;\r\n }\r\n batch.length = 0;\r\n },\r\n };\r\n}\r\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\r\nimport type { AnalyticsListener } from '../types/listener.js';\r\nimport { createNoopListener } from './noop.js';\r\nimport { createClarityListener } from './clarity.js';\r\nimport { createGAListener } from './ga.js';\r\nimport { createCustomApiListener } from './custom-api.js';\r\n\r\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\r\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\r\n\r\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\r\n noop: () => createNoopListener(),\r\n clarity: (config) => createClarityListener(config as { projectId: string }),\r\n ga: (config) => createGAListener(config as { measurementId: string }),\r\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\r\n};\r\n\r\n/**\r\n * Returns the built-in listener factory for the given id, or undefined if unknown.\r\n */\r\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\r\n return BUILT_IN_FACTORIES[id];\r\n}\r\n\r\nexport interface BuildListenersFromRemoteConfigOptions {\r\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\r\n factories?: Record<string, ListenerFactory>;\r\n /** Called when a remote listener id has no factory (unknown id). */\r\n onError?: (error: Error, context?: { listenerId?: string }) => void;\r\n}\r\n\r\n/**\r\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\r\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\r\n */\r\nexport function buildListenersFromRemoteConfig(\r\n remote: RemoteAnalyticsConfig,\r\n options?: BuildListenersFromRemoteConfigOptions\r\n): ListenerEntry[] {\r\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\r\n const onError = options?.onError;\r\n const entries: ListenerEntry[] = [];\r\n\r\n if (!Array.isArray(remote.listeners)) {\r\n return entries;\r\n }\r\n\r\n for (const entry of remote.listeners) {\r\n if (entry.enabled === false) continue;\r\n const id = entry.id;\r\n if (id == null || typeof id !== 'string') continue;\r\n\r\n const factory = factories[id];\r\n if (factory == null) {\r\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\r\n continue;\r\n }\r\n\r\n let listener: AnalyticsListener;\r\n try {\r\n listener = factory(entry.config);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: id });\r\n continue;\r\n }\r\n\r\n entries.push({\r\n id,\r\n enabled: entry.enabled,\r\n listener,\r\n config: entry.config,\r\n });\r\n }\r\n\r\n return entries;\r\n}\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n PreInitBehavior,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport type { AnalyticsListener } from './types/listener.js';\r\n\r\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\r\n\r\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\r\n if (entry == null || typeof entry !== 'object') return false;\r\n const o = entry as Record<string, unknown>;\r\n return (\r\n typeof o.id === 'string' &&\r\n o.id.trim() !== '' &&\r\n 'config' in o &&\r\n !('listener' in o && o.listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Validate init config. Throws with clear message on invalid config.\r\n * appId and environment are always taken from init options (e.g. from env files); when configUrl/getConfig is set, only listener details come from the remote source.\r\n */\r\nexport function validateConfig(options: AnalyticsInitConfig): void {\r\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\r\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\r\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\r\n }\r\n if (options.listeners != null && !Array.isArray(options.listeners)) {\r\n throw new Error('Analytics init failed: listeners must be an array.');\r\n }\r\n const behavior = options.preInitBehavior ?? 'drop';\r\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\r\n throw new Error(\r\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\r\n );\r\n }\r\n if (behavior === 'queue') {\r\n const maxSize = options.queueMaxSize ?? 100;\r\n const ttlMs = options.queueTtlMs ?? 5000;\r\n if (typeof maxSize !== 'number' || maxSize <= 0) {\r\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\r\n }\r\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\r\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\r\n }\r\n }\r\n if (useRemoteConfig) {\r\n return;\r\n }\r\n const listeners = options.listeners ?? [];\r\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\r\n if (isRemoteShape) {\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as RemoteListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\r\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\r\n }\r\n }\r\n return;\r\n }\r\n for (let i = 0; i < listeners.length; i++) {\r\n const entry = listeners[i] as ListenerEntry;\r\n if (entry.enabled === false) continue;\r\n if (entry.listener == null || typeof entry.listener !== 'object') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\r\n }\r\n const listener = entry.listener as AnalyticsListener;\r\n if (typeof listener.track !== 'function') {\r\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\r\n }\r\n }\r\n}\r\n","/** SDK version; set at build time or from package.json. */\r\nexport const SDK_VERSION = '1.2.0';\r\n","import type {\r\n AnalyticsInitConfig,\r\n ListenerEntry,\r\n OnErrorContext,\r\n RemoteAnalyticsConfig,\r\n RemoteListenerEntry,\r\n} from './types/config.js';\r\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\r\nimport { ListenerRegistry } from './dispatcher/registry.js';\r\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\r\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\r\nimport {\r\n createEnricherState,\r\n setUserId,\r\n clearUserId,\r\n type EnricherState,\r\n} from './enricher/enricher.js';\r\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\r\nimport { validateConfig } from './validate-config.js';\r\nimport { SDK_VERSION } from './version.js';\r\n\r\nlet enricherState: EnricherState | null = null;\r\nlet registry: ListenerRegistry | null = null;\r\n/** Pre-init buffer. Lazy-created on first track/identify (queue mode, defaults); replaced at init() with user options, then flushed and set null after init. */\r\nlet preInitBuffer: PreInitBuffer | null = null;\r\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\r\nlet initialized = false;\r\n\r\n/** Default buffer before init: queue mode so events before init can be replayed. */\r\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\r\n if (preInitBuffer == null) {\r\n preInitBuffer = new PreInitBuffer({\r\n mode: 'queue',\r\n maxSize: 100,\r\n ttlMs: 5000,\r\n });\r\n }\r\n return preInitBuffer;\r\n}\r\n\r\nfunction getEnricherState(): EnricherState {\r\n if (enricherState == null) {\r\n throw new Error('Analytics not initialized.');\r\n }\r\n return enricherState;\r\n}\r\n\r\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\r\n if (options.getConfig) {\r\n return options.getConfig();\r\n }\r\n if (options.configUrl) {\r\n const fetchFn = options.fetch ?? globalThis.fetch;\r\n const res = await fetchFn(options.configUrl);\r\n if (!res.ok) {\r\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\r\n }\r\n const json = await res.json();\r\n if (json == null || typeof json !== 'object') {\r\n throw new Error('Analytics config response must be a JSON object.');\r\n }\r\n return json as RemoteAnalyticsConfig;\r\n }\r\n return null;\r\n}\r\n\r\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\r\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\r\n return listeners.every(\r\n (entry): entry is RemoteListenerEntry =>\r\n entry != null &&\r\n typeof entry === 'object' &&\r\n typeof (entry as RemoteListenerEntry).id === 'string' &&\r\n 'config' in entry &&\r\n !('listener' in entry && (entry as ListenerEntry).listener != null)\r\n );\r\n}\r\n\r\n/**\r\n * Initialize the SDK. Idempotent; calling again re-applies config.\r\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\r\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\r\n */\r\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\r\n validateConfig(options);\r\n\r\n let appId: string;\r\n let environment: string;\r\n let listeners: ListenerEntry[];\r\n\r\n const remote = await fetchRemoteConfig(options);\r\n if (remote != null) {\r\n appId = (options.appId ?? '').toString().trim();\r\n environment = options.environment ?? 'production';\r\n if (!appId) {\r\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\r\n }\r\n listeners = buildListenersFromRemoteConfig(remote, {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n });\r\n } else {\r\n appId = options.appId.trim();\r\n environment = options.environment ?? 'production';\r\n const rawListeners = options.listeners ?? [];\r\n if (isInlineRemoteShape(rawListeners)) {\r\n listeners = buildListenersFromRemoteConfig(\r\n { listeners: rawListeners },\r\n {\r\n factories: options.listenerFactories,\r\n onError: options.onError,\r\n }\r\n );\r\n } else {\r\n listeners = rawListeners as ListenerEntry[];\r\n }\r\n }\r\n\r\n const preInitBehavior = options.preInitBehavior ?? 'drop';\r\n const queueMaxSize = options.queueMaxSize ?? 100;\r\n const queueTtlMs = options.queueTtlMs ?? 5000;\r\n onError = options.onError;\r\n\r\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\r\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\r\n\r\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\r\n registry = new ListenerRegistry();\r\n\r\n for (const entry of listeners) {\r\n if (entry.enabled === false) continue;\r\n try {\r\n const initResult = entry.listener.init(entry.config);\r\n if (initResult instanceof Promise) {\r\n await initResult;\r\n }\r\n registry.add(entry.id, entry.listener);\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, { listenerId: entry.id });\r\n }\r\n }\r\n\r\n const deps: DispatcherDeps = {\r\n registry,\r\n getEnricherState,\r\n onError,\r\n };\r\n\r\n if (preInitBuffer) {\r\n if (preInitBehavior === 'queue') {\r\n preInitBuffer.flush(\r\n (payload) => dispatchTrack(deps, payload),\r\n (userId, traits) => {\r\n setUserId(enricherState!, userId);\r\n dispatchIdentify(deps, userId, traits);\r\n }\r\n );\r\n } else {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n preInitBuffer = null;\r\n }\r\n\r\n initialized = true;\r\n}\r\n\r\n/**\r\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\r\n return;\r\n }\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchTrack(deps, event);\r\n}\r\n\r\n/**\r\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\r\n */\r\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\r\n if (!initialized) {\r\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\r\n return;\r\n }\r\n if (enricherState) setUserId(enricherState, userId);\r\n const deps: DispatcherDeps = {\r\n registry: registry!,\r\n getEnricherState,\r\n onError,\r\n };\r\n dispatchIdentify(deps, userId, traits);\r\n}\r\n\r\n/**\r\n * Flush listeners that batch (e.g. before page unload).\r\n */\r\nexport async function flush(): Promise<void> {\r\n if (!initialized || registry == null) return;\r\n const promises: Promise<void>[] = [];\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.flush === 'function') {\r\n const result = listener.flush();\r\n if (result instanceof Promise) promises.push(result);\r\n }\r\n }\r\n await Promise.all(promises);\r\n}\r\n\r\n/**\r\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\r\n */\r\nexport async function reset(): Promise<void> {\r\n if (preInitBuffer) {\r\n preInitBuffer.flush(() => {}, () => {});\r\n }\r\n if (enricherState) clearUserId(enricherState);\r\n if (registry) {\r\n for (const { listener } of registry.getAll()) {\r\n if (typeof listener.teardown === 'function') {\r\n try {\r\n const result = listener.teardown();\r\n if (result instanceof Promise) await result;\r\n } catch (err) {\r\n const error = err instanceof Error ? err : new Error(String(err));\r\n onError?.(error, {});\r\n }\r\n }\r\n }\r\n registry.clear();\r\n }\r\n preInitBuffer = null;\r\n enricherState = null;\r\n registry = null;\r\n onError = undefined;\r\n initialized = false;\r\n}\r\n\r\nexport function isInitialized(): boolean {\r\n return initialized;\r\n}\r\n","/**\r\n * Script entry for browser <script src=\"...\"> usage.\r\n * Exposes UsageAnalytics on globalThis (window). Auto-inits from config in the script tag (data-config) or window.__ANALYTICS_CONFIG__.\r\n */\r\nimport { init, track, identify, flush, reset, isInitialized } from './facade.js';\r\nimport type { AnalyticsInitConfig } from './types/config.js';\r\n\r\ndeclare global {\r\n interface Window {\r\n UsageAnalytics: {\r\n init: typeof init;\r\n track: typeof track;\r\n identify: typeof identify;\r\n flush: typeof flush;\r\n reset: typeof reset;\r\n isInitialized: typeof isInitialized;\r\n };\r\n __ANALYTICS_CONFIG__?: AnalyticsInitConfig;\r\n }\r\n}\r\n\r\nconst api = {\r\n init,\r\n track,\r\n identify,\r\n flush,\r\n reset,\r\n isInitialized,\r\n};\r\n\r\nif (typeof globalThis !== 'undefined') {\r\n (globalThis as unknown as { UsageAnalytics: typeof api }).UsageAnalytics = api;\r\n}\r\n\r\nfunction getConfigFromScriptTag(): AnalyticsInitConfig | null {\r\n if (typeof document === 'undefined') return null;\r\n const script = document.currentScript as HTMLScriptElement | null;\r\n if (!script) return null;\r\n const raw = script.getAttribute('data-config');\r\n if (!raw || typeof raw !== 'string') return null;\r\n try {\r\n const parsed = JSON.parse(raw) as AnalyticsInitConfig;\r\n if (parsed && typeof parsed.appId === 'string' && parsed.appId.trim() !== '') {\r\n return parsed;\r\n }\r\n } catch {\r\n // Invalid JSON\r\n }\r\n return null;\r\n}\r\n\r\nfunction autoInit(): void {\r\n if (typeof window === 'undefined') return;\r\n const config = getConfigFromScriptTag() ?? window.__ANALYTICS_CONFIG__;\r\n if (config) {\r\n init(config).catch(() => {\r\n // Silent fail for auto-init; app can call init() manually with onError if needed\r\n });\r\n }\r\n}\r\n\r\nautoInit();\r\n"],"mappings":"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","names":["PreInitBuffer","options","name","properties","timestamp","item","userId","traits","onTrack","onIdentify","cutoff","identifyItems","trackItems","ListenerRegistry","id","listener","STORAGE_KEY_PREFIX","generateId","fallbackUuidV4","hex","result","c","r","v","getOrCreateSessionId","appId","providedSessionId","key","existing","newId","ENVELOPE_VERSION","createEnricherState","appId","environment","sdkVersion","sessionId","resolvedSessionId","getOrCreateSessionId","enrich","payload","state","timestamp","envelope","generateId","setUserId","userId","clearUserId","dispatchTrack","deps","payload","state","envelope","enrich","dispatchEnvelope","kind","registry","onError","id","listener","err","error","dispatchIdentify","userId","traits","createNoopListener","_config","_envelope","noopListener","CLARITY_SCRIPT_BASE","isBrowser","loadClarityScript","projectId","resolve","w","args","fn","script","createClarityListener","config","ready","cfg","id","envelope","key","value","userId","traits","piiKeys","GTAG_SCRIPT_URL","isBrowser","loadGtagScript","measurementId","resolve","gtag","args","script","createGAListener","config","ready","cfg","id","envelope","params","userId","traits","createCustomApiListener","config","endpoint","apiKey","batchSize","flushIntervalMs","batch","flushTimer","scheduleFlush","sendBatch","toSend","body","headers","_config","envelope","BUILT_IN_FACTORIES","createNoopListener","config","createClarityListener","createGAListener","createCustomApiListener","buildListenersFromRemoteConfig","remote","options","factories","BUILT_IN_FACTORIES","onError","entries","entry","id","factory","listener","err","error","VALID_PRE_INIT_BEHAVIORS","isRemoteListenerEntry","entry","o","validateConfig","options","useRemoteConfig","behavior","maxSize","ttlMs","listeners","SDK_VERSION","enricherState","registry","preInitBuffer","onError","initialized","getOrCreatePreInitBuffer","PreInitBuffer","getEnricherState","fetchRemoteConfig","options","res","json","isInlineRemoteShape","listeners","entry","init","validateConfig","appId","environment","remote","buildListenersFromRemoteConfig","rawListeners","preInitBehavior","queueMaxSize","queueTtlMs","mode","createEnricherState","SDK_VERSION","ListenerRegistry","initResult","err","error","deps","payload","dispatchTrack","userId","traits","setUserId","dispatchIdentify","track","event","identify","flush","promises","listener","result","reset","clearUserId","isInitialized","api","init","track","identify","flush","reset","isInitialized","getConfigFromScriptTag","script","raw","parsed","autoInit","config"]}
|