@snovasys/usage-analytics-sdk 1.0.0 → 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 +26 -4
- package/dist/clarity.cjs.map +1 -1
- package/dist/clarity.js +26 -4
- package/dist/clarity.js.map +1 -1
- package/dist/custom-api.cjs.map +1 -1
- package/dist/custom-api.js.map +1 -1
- package/dist/ga.cjs.map +1 -1
- package/dist/ga.js.map +1 -1
- package/dist/index.cjs +27 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +27 -5
- package/dist/index.js.map +1 -1
- package/dist/noop.cjs.map +1 -1
- package/dist/noop.js.map +1 -1
- package/dist/usage-analytics.min.js +1 -1
- package/dist/usage-analytics.min.js.map +1 -1
- package/package.json +59 -59
- package/README.md +0 -638
package/dist/clarity.cjs
CHANGED
|
@@ -39,9 +39,14 @@ function loadClarityScript(projectId) {
|
|
|
39
39
|
resolve();
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
+
const w = window;
|
|
43
|
+
w["clarity"] = w["clarity"] || function(...args) {
|
|
44
|
+
const fn = w["clarity"];
|
|
45
|
+
(fn.q = fn.q || []).push(args);
|
|
46
|
+
};
|
|
42
47
|
const script = document.createElement("script");
|
|
43
48
|
script.async = true;
|
|
44
|
-
script.src = `${CLARITY_SCRIPT_BASE}${
|
|
49
|
+
script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;
|
|
45
50
|
script.onload = () => resolve();
|
|
46
51
|
script.onerror = () => resolve();
|
|
47
52
|
document.head.appendChild(script);
|
|
@@ -67,7 +72,11 @@ function createClarityListener(config) {
|
|
|
67
72
|
window.clarity("event", envelope.name);
|
|
68
73
|
if (envelope.properties && typeof window.clarity === "function") {
|
|
69
74
|
for (const [key, value] of Object.entries(envelope.properties)) {
|
|
70
|
-
if (typeof value === "string"
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
window.clarity("set", key, value);
|
|
77
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
78
|
+
window.clarity("set", key, String(value));
|
|
79
|
+
} else if (Array.isArray(value)) {
|
|
71
80
|
window.clarity("set", key, value);
|
|
72
81
|
}
|
|
73
82
|
}
|
|
@@ -75,10 +84,23 @@ function createClarityListener(config) {
|
|
|
75
84
|
} catch {
|
|
76
85
|
}
|
|
77
86
|
},
|
|
78
|
-
identify(userId,
|
|
87
|
+
identify(userId, traits) {
|
|
79
88
|
try {
|
|
80
89
|
if (!isBrowser() || !ready || typeof window.clarity !== "function") return;
|
|
81
|
-
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
|
+
}
|
|
82
104
|
} catch {
|
|
83
105
|
}
|
|
84
106
|
}
|
package/dist/clarity.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../clarity.ts","../src/listeners/clarity.ts"],"sourcesContent":["/**\n * Entry point: @snovasys/usage-analytics-sdk/clarity\n * Export Clarity listener factory for manual wiring or use via built-in registry (id: clarity).\n */\nexport { createClarityListener } from './src/listeners/clarity.js';\nexport type { ClarityListenerConfig } from './src/listeners/clarity.js';\nexport type { AnalyticsListener, EventEnvelope } from './src/types/index.js';\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface ClarityListenerConfig {\n projectId: string;\n}\n\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\n\ndeclare global {\n interface Window {\n clarity?: (cmd: string, ...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadClarityScript(projectId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (typeof window.clarity === 'function') {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.async = true;\n script.src = `${CLARITY_SCRIPT_BASE}${
|
|
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
|
@@ -13,9 +13,14 @@ function loadClarityScript(projectId) {
|
|
|
13
13
|
resolve();
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
|
+
const w = window;
|
|
17
|
+
w["clarity"] = w["clarity"] || function(...args) {
|
|
18
|
+
const fn = w["clarity"];
|
|
19
|
+
(fn.q = fn.q || []).push(args);
|
|
20
|
+
};
|
|
16
21
|
const script = document.createElement("script");
|
|
17
22
|
script.async = true;
|
|
18
|
-
script.src = `${CLARITY_SCRIPT_BASE}${
|
|
23
|
+
script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;
|
|
19
24
|
script.onload = () => resolve();
|
|
20
25
|
script.onerror = () => resolve();
|
|
21
26
|
document.head.appendChild(script);
|
|
@@ -41,7 +46,11 @@ function createClarityListener(config) {
|
|
|
41
46
|
window.clarity("event", envelope.name);
|
|
42
47
|
if (envelope.properties && typeof window.clarity === "function") {
|
|
43
48
|
for (const [key, value] of Object.entries(envelope.properties)) {
|
|
44
|
-
if (typeof value === "string"
|
|
49
|
+
if (typeof value === "string") {
|
|
50
|
+
window.clarity("set", key, value);
|
|
51
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
52
|
+
window.clarity("set", key, String(value));
|
|
53
|
+
} else if (Array.isArray(value)) {
|
|
45
54
|
window.clarity("set", key, value);
|
|
46
55
|
}
|
|
47
56
|
}
|
|
@@ -49,10 +58,23 @@ function createClarityListener(config) {
|
|
|
49
58
|
} catch {
|
|
50
59
|
}
|
|
51
60
|
},
|
|
52
|
-
identify(userId,
|
|
61
|
+
identify(userId, traits) {
|
|
53
62
|
try {
|
|
54
63
|
if (!isBrowser() || !ready || typeof window.clarity !== "function") return;
|
|
55
|
-
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
|
+
}
|
|
56
78
|
} catch {
|
|
57
79
|
}
|
|
58
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';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface ClarityListenerConfig {\n projectId: string;\n}\n\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\n\ndeclare global {\n interface Window {\n clarity?: (cmd: string, ...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadClarityScript(projectId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (typeof window.clarity === 'function') {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.async = true;\n script.src = `${CLARITY_SCRIPT_BASE}${
|
|
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/custom-api.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../custom-api.ts","../src/listeners/custom-api.ts"],"sourcesContent":["/**\n * Entry point: @snovasys/usage-analytics-sdk/custom-api\n * Export custom API listener factory for apps that send events to their backend.\n */\nexport { createCustomApiListener } from './src/listeners/custom-api.js';\nexport type { CustomApiListenerConfig } from './src/listeners/custom-api.js';\nexport type { AnalyticsListener, EventEnvelope } from './src/types/index.js';\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface CustomApiListenerConfig {\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\n endpoint: string;\n /** Optional API key or bearer token. */\n apiKey?: string;\n /** Optional: batch size before sending. Default 1 (no batching). */\n batchSize?: number;\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\n flushIntervalMs?: number;\n}\n\nconst DEFAULT_BATCH_SIZE = 1;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\n\n/**\n * Listener that POSTs events to a configurable endpoint.\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\n */\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\n const endpoint = config.endpoint;\n const apiKey = config.apiKey;\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\n let batch: EventEnvelope[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleFlush(): void {\n if (flushTimer != null) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n sendBatch();\n }, flushIntervalMs);\n }\n\n function sendBatch(): void {\n if (batch.length === 0) return;\n const toSend = [...batch];\n batch = [];\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\n if (typeof fetch !== 'undefined') {\n fetch(endpoint, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n }).catch(() => {});\n }\n }\n\n return {\n init(_config: unknown): void {\n // config was passed at createCustomApiListener\n },\n track(envelope: EventEnvelope): void {\n try {\n batch.push(envelope);\n if (batch.length >= batchSize) {\n sendBatch();\n } else if (flushIntervalMs > 0) {\n scheduleFlush();\n }\n } catch {\n // swallow\n }\n },\n flush(): Promise<void> {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n sendBatch();\n return Promise.resolve();\n },\n teardown(): void {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n batch.length = 0;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcA,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;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../custom-api.ts","../src/listeners/custom-api.ts"],"sourcesContent":["/**\r\n * Entry point: @snovasys/usage-analytics-sdk/custom-api\r\n * Export custom API listener factory for apps that send events to their backend.\r\n */\r\nexport { createCustomApiListener } from './src/listeners/custom-api.js';\r\nexport type { CustomApiListenerConfig } from './src/listeners/custom-api.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 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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACcA,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;","names":[]}
|
package/dist/custom-api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/listeners/custom-api.ts"],"sourcesContent":["import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface CustomApiListenerConfig {\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\n endpoint: string;\n /** Optional API key or bearer token. */\n apiKey?: string;\n /** Optional: batch size before sending. Default 1 (no batching). */\n batchSize?: number;\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\n flushIntervalMs?: number;\n}\n\nconst DEFAULT_BATCH_SIZE = 1;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\n\n/**\n * Listener that POSTs events to a configurable endpoint.\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\n */\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\n const endpoint = config.endpoint;\n const apiKey = config.apiKey;\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\n let batch: EventEnvelope[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleFlush(): void {\n if (flushTimer != null) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n sendBatch();\n }, flushIntervalMs);\n }\n\n function sendBatch(): void {\n if (batch.length === 0) return;\n const toSend = [...batch];\n batch = [];\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\n if (typeof fetch !== 'undefined') {\n fetch(endpoint, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n }).catch(() => {});\n }\n }\n\n return {\n init(_config: unknown): void {\n // config was passed at createCustomApiListener\n },\n track(envelope: EventEnvelope): void {\n try {\n batch.push(envelope);\n if (batch.length >= batchSize) {\n sendBatch();\n } else if (flushIntervalMs > 0) {\n scheduleFlush();\n }\n } catch {\n // swallow\n }\n },\n flush(): Promise<void> {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n sendBatch();\n return Promise.resolve();\n },\n teardown(): void {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n batch.length = 0;\n },\n };\n}\n"],"mappings":";AAcA,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;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/listeners/custom-api.ts"],"sourcesContent":["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"],"mappings":";AAcA,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;","names":[]}
|
package/dist/ga.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../ga.ts","../src/listeners/ga.ts"],"sourcesContent":["/**\n * Entry point: @snovasys/usage-analytics-sdk/ga\n * Export GA listener factory for manual wiring or use via built-in registry (id: ga).\n */\nexport { createGAListener } from './src/listeners/ga.js';\nexport type { GAListenerConfig } from './src/listeners/ga.js';\nexport type { AnalyticsListener, EventEnvelope } from './src/types/index.js';\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface GAListenerConfig {\n measurementId: string;\n}\n\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[];\n gtag?: (...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadGtagScript(measurementId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (window.gtag) {\n window.gtag('config', measurementId);\n resolve();\n return;\n }\n window.dataLayer = window.dataLayer ?? [];\n const gtag = (...args: unknown[]) => {\n window.dataLayer!.push(args);\n };\n window.gtag = gtag;\n gtag('js', new Date());\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\n script.onload = () => {\n gtag('config', measurementId);\n resolve();\n };\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Google Analytics 4 via gtag.\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\n const measurementId = config.measurementId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as GAListenerConfig;\n const id = c.measurementId;\n if (!id || !isBrowser()) return;\n await loadGtagScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\n const params = envelope.properties ?? {};\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\n window.gtag('event', envelope.name, params);\n } catch {\n // swallow\n }\n },\n identify(userId: string, traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || typeof window.gtag !== 'function') return;\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\n } catch {\n // swallow\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOA,IAAM,kBAAkB;AASxB,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,eAAe,eAAsC;AAC5D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAAC,UAAU,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,CAAC,UAAU,EAAG;AACzB,cAAM,eAAe,EAAE;AACvB,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAAC,UAAU,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,CAAC,UAAU,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;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../ga.ts","../src/listeners/ga.ts"],"sourcesContent":["/**\r\n * Entry point: @snovasys/usage-analytics-sdk/ga\r\n * Export GA listener factory for manual wiring or use via built-in registry (id: ga).\r\n */\r\nexport { createGAListener } from './src/listeners/ga.js';\r\nexport type { GAListenerConfig } from './src/listeners/ga.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 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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOA,IAAM,kBAAkB;AASxB,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,eAAe,eAAsC;AAC5D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAAC,UAAU,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,CAAC,UAAU,EAAG;AACzB,cAAM,eAAe,EAAE;AACvB,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAAC,UAAU,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,CAAC,UAAU,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;","names":[]}
|
package/dist/ga.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/listeners/ga.ts"],"sourcesContent":["import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface GAListenerConfig {\n measurementId: string;\n}\n\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[];\n gtag?: (...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadGtagScript(measurementId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (window.gtag) {\n window.gtag('config', measurementId);\n resolve();\n return;\n }\n window.dataLayer = window.dataLayer ?? [];\n const gtag = (...args: unknown[]) => {\n window.dataLayer!.push(args);\n };\n window.gtag = gtag;\n gtag('js', new Date());\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\n script.onload = () => {\n gtag('config', measurementId);\n resolve();\n };\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Google Analytics 4 via gtag.\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\n const measurementId = config.measurementId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as GAListenerConfig;\n const id = c.measurementId;\n if (!id || !isBrowser()) return;\n await loadGtagScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\n const params = envelope.properties ?? {};\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\n window.gtag('event', envelope.name, params);\n } catch {\n // swallow\n }\n },\n identify(userId: string, traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || typeof window.gtag !== 'function') return;\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\n } catch {\n // swallow\n }\n },\n };\n}\n"],"mappings":";AAOA,IAAM,kBAAkB;AASxB,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,eAAe,eAAsC;AAC5D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAAC,UAAU,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,CAAC,UAAU,EAAG;AACzB,cAAM,eAAe,EAAE;AACvB,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAAC,UAAU,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,CAAC,UAAU,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;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/listeners/ga.ts"],"sourcesContent":["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"],"mappings":";AAOA,IAAM,kBAAkB;AASxB,SAAS,YAAqB;AAC5B,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,eAAe,eAAsC;AAC5D,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,CAAC,UAAU,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,CAAC,UAAU,EAAG;AACzB,cAAM,eAAe,EAAE;AACvB,gBAAQ;AAAA,MACV,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,MAAM,UAA+B;AACnC,UAAI;AACF,YAAI,CAAC,UAAU,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,CAAC,UAAU,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;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -246,9 +246,14 @@ function loadClarityScript(projectId) {
|
|
|
246
246
|
resolve();
|
|
247
247
|
return;
|
|
248
248
|
}
|
|
249
|
+
const w = window;
|
|
250
|
+
w["clarity"] = w["clarity"] || function(...args) {
|
|
251
|
+
const fn = w["clarity"];
|
|
252
|
+
(fn.q = fn.q || []).push(args);
|
|
253
|
+
};
|
|
249
254
|
const script = document.createElement("script");
|
|
250
255
|
script.async = true;
|
|
251
|
-
script.src = `${CLARITY_SCRIPT_BASE}${
|
|
256
|
+
script.src = `${CLARITY_SCRIPT_BASE}${projectId}`;
|
|
252
257
|
script.onload = () => resolve();
|
|
253
258
|
script.onerror = () => resolve();
|
|
254
259
|
document.head.appendChild(script);
|
|
@@ -274,7 +279,11 @@ function createClarityListener(config) {
|
|
|
274
279
|
window.clarity("event", envelope.name);
|
|
275
280
|
if (envelope.properties && typeof window.clarity === "function") {
|
|
276
281
|
for (const [key, value] of Object.entries(envelope.properties)) {
|
|
277
|
-
if (typeof value === "string"
|
|
282
|
+
if (typeof value === "string") {
|
|
283
|
+
window.clarity("set", key, value);
|
|
284
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
285
|
+
window.clarity("set", key, String(value));
|
|
286
|
+
} else if (Array.isArray(value)) {
|
|
278
287
|
window.clarity("set", key, value);
|
|
279
288
|
}
|
|
280
289
|
}
|
|
@@ -282,10 +291,23 @@ function createClarityListener(config) {
|
|
|
282
291
|
} catch {
|
|
283
292
|
}
|
|
284
293
|
},
|
|
285
|
-
identify(userId,
|
|
294
|
+
identify(userId, traits) {
|
|
286
295
|
try {
|
|
287
296
|
if (!isBrowser() || !ready || typeof window.clarity !== "function") return;
|
|
288
|
-
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
|
+
}
|
|
289
311
|
} catch {
|
|
290
312
|
}
|
|
291
313
|
}
|
|
@@ -527,7 +549,7 @@ function validateConfig(options) {
|
|
|
527
549
|
}
|
|
528
550
|
|
|
529
551
|
// src/version.ts
|
|
530
|
-
var SDK_VERSION = "1.
|
|
552
|
+
var SDK_VERSION = "1.2.0";
|
|
531
553
|
|
|
532
554
|
// src/facade.ts
|
|
533
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';\nexport type { AnalyticsInitConfig, AnalyticsListener, EventEnvelope, ListenerEntry, OnErrorContext, PreInitBehavior } from './types/index.js';\n","import type { BufferedItem, BufferedTrack, BufferedIdentify } from './types.js';\n\nexport interface PreInitBufferOptions {\n /** 'drop' = no-op; 'queue' = FIFO queue. In SSR (no window) always drop. */\n mode: 'drop' | 'queue';\n maxSize: number;\n ttlMs: number;\n /** For testing: override browser check so queue works in Node. */\n isBrowserOverride?: boolean;\n}\n\nexport type FlushTrackHandler = (payload: { name: string; properties?: Record<string, unknown>; timestamp?: string }) => void;\nexport type FlushIdentifyHandler = (userId: string, traits?: Record<string, unknown>) => void;\n\n/**\n * Pre-init buffer: either drops events or queues them (FIFO) until flush.\n * In Node/SSR (typeof window === 'undefined') always behaves as drop.\n */\nexport class PreInitBuffer {\n private readonly options: PreInitBufferOptions;\n private readonly queue: BufferedItem[] = [];\n private readonly isBrowser: boolean;\n\n constructor(options: PreInitBufferOptions) {\n this.options = options;\n this.isBrowser =\n options.isBrowserOverride !== undefined ? options.isBrowserOverride : typeof window !== 'undefined';\n }\n\n /**\n * Push a track call. No-op if mode is drop or not browser (SSR).\n */\n pushTrack(name: string, properties?: Record<string, unknown>, timestamp?: string): void {\n if (this.options.mode === 'drop' || !this.isBrowser) return;\n const item: BufferedTrack = {\n type: 'track',\n name,\n properties,\n timestamp,\n createdAt: Date.now(),\n };\n this.push(item);\n }\n\n /**\n * Push an identify call. No-op if mode is drop or not browser (SSR).\n */\n pushIdentify(userId: string, traits?: Record<string, unknown>): void {\n if (this.options.mode === 'drop' || !this.isBrowser) return;\n const item: BufferedIdentify = {\n type: 'identify',\n userId,\n traits,\n createdAt: Date.now(),\n };\n this.push(item);\n }\n\n private push(item: BufferedItem): void {\n while (this.queue.length >= this.options.maxSize && this.queue.length > 0) {\n this.queue.shift(); // drop oldest (FIFO)\n }\n this.queue.push(item);\n }\n\n /**\n * Replay queued items: all identify first (order), then all track (order).\n * Items older than ttlMs are dropped. Then clear the queue.\n */\n flush(\n onTrack: FlushTrackHandler,\n onIdentify: FlushIdentifyHandler\n ): void {\n if (!this.isBrowser || this.queue.length === 0) {\n this.queue.length = 0;\n return;\n }\n const now = Date.now();\n const cutoff = now - this.options.ttlMs;\n const identifyItems = this.queue.filter(\n (item): item is BufferedIdentify => item.type === 'identify' && item.createdAt >= cutoff\n );\n const trackItems = this.queue.filter(\n (item): item is BufferedTrack => item.type === 'track' && item.createdAt >= cutoff\n );\n for (const item of identifyItems) {\n onIdentify(item.userId, item.traits);\n }\n for (const item of trackItems) {\n onTrack({\n name: item.name,\n properties: item.properties,\n timestamp: item.timestamp,\n });\n }\n this.queue.length = 0;\n }\n\n /** Number of items currently queued (for tests). */\n get length(): number {\n return this.queue.length;\n }\n}\n","import type { AnalyticsListener } from '../types/listener.js';\n\nexport interface RegisteredListener {\n id?: string;\n listener: AnalyticsListener;\n}\n\n/**\n * Holds enabled listener instances. Populated at init.\n */\nexport class ListenerRegistry {\n private readonly listeners: RegisteredListener[] = [];\n\n add(id: string | undefined, listener: AnalyticsListener): void {\n this.listeners.push({ id, listener });\n }\n\n getAll(): RegisteredListener[] {\n return [...this.listeners];\n }\n\n clear(): void {\n this.listeners.length = 0;\n }\n}\n","const STORAGE_KEY_PREFIX = 'snovasys_usage_analytics_session_';\n\n/**\n * Generate a UUID v4 (uses crypto.randomUUID when available, else fallback).\n */\nexport function generateId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n return fallbackUuidV4();\n}\n\nfunction fallbackUuidV4(): string {\n const hex = '0123456789abcdef';\n let result = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';\n result = result.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return hex[v];\n });\n return result;\n}\n\n/**\n * Get or create session id. Optionally persist in sessionStorage keyed by appId.\n */\nexport function getOrCreateSessionId(appId: string, providedSessionId?: string): string {\n if (providedSessionId) return providedSessionId;\n const key = `${STORAGE_KEY_PREFIX}${appId}`;\n if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {\n try {\n const existing = sessionStorage.getItem(key);\n if (existing) return existing;\n const newId = generateId();\n sessionStorage.setItem(key, newId);\n return newId;\n } catch {\n return generateId();\n }\n }\n return generateId();\n}\n","import type { EventEnvelope } from '../types/envelope.js';\nimport { generateId, getOrCreateSessionId } from './session.js';\n\n/** Raw track payload from app (before enrichment). */\nexport interface TrackPayload {\n name: string;\n properties?: Record<string, unknown>;\n timestamp?: string;\n}\n\nexport interface EnricherState {\n appId: string;\n environment: string;\n sdkVersion: string;\n sessionId?: string;\n userId?: string;\n /** Provided at init or generated per session. */\n resolvedSessionId?: string;\n}\n\nconst ENVELOPE_VERSION = 1;\n\n/**\n * Creates enricher state from init config. Call once at init.\n */\nexport function createEnricherState(\n appId: string,\n environment: string,\n sdkVersion: string,\n sessionId?: string\n): EnricherState {\n const resolvedSessionId = getOrCreateSessionId(appId, sessionId);\n return {\n appId,\n environment,\n sdkVersion,\n sessionId,\n resolvedSessionId,\n };\n}\n\n/**\n * Enrich a raw track payload into a full EventEnvelope.\n */\nexport function enrich(\n payload: TrackPayload,\n state: EnricherState\n): EventEnvelope {\n const timestamp = payload.timestamp ?? new Date().toISOString();\n const envelope: EventEnvelope = {\n name: payload.name,\n timestamp,\n eventId: generateId(),\n appId: state.appId,\n environment: state.environment,\n sdkVersion: state.sdkVersion,\n version: ENVELOPE_VERSION,\n };\n if (payload.properties != null) envelope.properties = payload.properties;\n if (state.resolvedSessionId) envelope.sessionId = state.resolvedSessionId;\n if (state.userId) envelope.userId = state.userId;\n return envelope;\n}\n\n/**\n * Set userId (and optional traits) on state after identify. Cleared on reset.\n */\nexport function setUserId(state: EnricherState, userId: string): void {\n state.userId = userId;\n}\n\n/**\n * Clear userId from state (e.g. on reset).\n */\nexport function clearUserId(state: EnricherState): void {\n state.userId = undefined;\n}\n","import type { EventEnvelope } from '../types/envelope.js';\nimport type { OnErrorContext } from '../types/config.js';\nimport type { ListenerRegistry } from './registry.js';\nimport type { EnricherState } from '../enricher/enricher.js';\nimport { enrich, type TrackPayload } from '../enricher/enricher.js';\n\nexport interface DispatcherDeps {\n registry: ListenerRegistry;\n getEnricherState: () => EnricherState;\n onError?: (error: Error, context?: OnErrorContext) => void;\n}\n\n/**\n * Normalize payload → enrich → dispatch to all listeners (try/catch per listener).\n */\nexport function dispatchTrack(\n deps: DispatcherDeps,\n payload: TrackPayload\n): void {\n const state = deps.getEnricherState();\n const envelope = enrich(payload, state);\n dispatchEnvelope(deps, envelope, 'track');\n}\n\n/**\n * Dispatch a pre-built envelope to all listeners (e.g. after enrich).\n */\nfunction dispatchEnvelope(\n deps: DispatcherDeps,\n envelope: EventEnvelope,\n kind: 'track'\n): void {\n const { registry, onError } = deps;\n for (const { id, listener } of registry.getAll()) {\n try {\n listener.track(envelope);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n }\n }\n}\n\n/**\n * Call identify on all listeners that implement it (try/catch per listener).\n */\nexport function dispatchIdentify(\n deps: DispatcherDeps,\n userId: string,\n traits?: Record<string, unknown>\n): void {\n const { registry, onError } = deps;\n for (const { id, listener } of registry.getAll()) {\n if (typeof listener.identify !== 'function') continue;\n try {\n listener.identify(userId, traits);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n }\n }\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\n/**\n * No-op listener: implements the interface with no side effects.\n * Use when analytics is disabled or in tests.\n */\nexport function createNoopListener(): AnalyticsListener {\n return {\n init(_config: unknown): void {\n // no-op\n },\n track(_envelope: EventEnvelope): void {\n // no-op\n },\n };\n}\n\n/** Singleton no-op listener instance. */\nexport const noopListener: AnalyticsListener = createNoopListener();\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface ClarityListenerConfig {\n projectId: string;\n}\n\nconst CLARITY_SCRIPT_BASE = 'https://www.clarity.ms/tag/';\n\ndeclare global {\n interface Window {\n clarity?: (cmd: string, ...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadClarityScript(projectId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (typeof window.clarity === 'function') {\n resolve();\n return;\n }\n const script = document.createElement('script');\n script.async = true;\n script.src = `${CLARITY_SCRIPT_BASE}${encodeURIComponent(projectId)}`;\n script.onload = () => resolve();\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Microsoft Clarity via script injection.\n * Loads Clarity script with projectId; track → clarity(\"event\", name); identify → clarity(\"identify\", userId, ...).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createClarityListener(config: ClarityListenerConfig): AnalyticsListener {\n const projectId = config.projectId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as ClarityListenerConfig;\n const id = c.projectId;\n if (!id || !isBrowser()) return;\n await loadClarityScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\n window.clarity('event', envelope.name);\n if (envelope.properties && typeof window.clarity === 'function') {\n for (const [key, value] of Object.entries(envelope.properties)) {\n if (typeof value === 'string' || Array.isArray(value)) {\n window.clarity('set', key, value);\n }\n }\n }\n } catch {\n // swallow\n }\n },\n identify(userId: string, _traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || !ready || typeof window.clarity !== 'function') return;\n window.clarity('identify', userId, undefined, undefined, undefined);\n } catch {\n // swallow\n }\n },\n };\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface GAListenerConfig {\n measurementId: string;\n}\n\nconst GTAG_SCRIPT_URL = 'https://www.googletagmanager.com/gtag/js';\n\ndeclare global {\n interface Window {\n dataLayer?: unknown[];\n gtag?: (...args: unknown[]) => void;\n }\n}\n\nfunction isBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nfunction loadGtagScript(measurementId: string): Promise<void> {\n return new Promise((resolve) => {\n if (!isBrowser()) {\n resolve();\n return;\n }\n if (window.gtag) {\n window.gtag('config', measurementId);\n resolve();\n return;\n }\n window.dataLayer = window.dataLayer ?? [];\n const gtag = (...args: unknown[]) => {\n window.dataLayer!.push(args);\n };\n window.gtag = gtag;\n gtag('js', new Date());\n const script = document.createElement('script');\n script.async = true;\n script.src = `${GTAG_SCRIPT_URL}?id=${encodeURIComponent(measurementId)}`;\n script.onload = () => {\n gtag('config', measurementId);\n resolve();\n };\n script.onerror = () => resolve();\n document.head.appendChild(script);\n });\n}\n\n/**\n * Listener that sends events to Google Analytics 4 via gtag.\n * Loads gtag script with measurementId; track → gtag('event', name, properties).\n * No-op when typeof window === 'undefined' (SSR).\n */\nexport function createGAListener(config: GAListenerConfig): AnalyticsListener {\n const measurementId = config.measurementId;\n let ready = false;\n\n return {\n async init(cfg: unknown): Promise<void> {\n try {\n const c = (cfg ?? config) as GAListenerConfig;\n const id = c.measurementId;\n if (!id || !isBrowser()) return;\n await loadGtagScript(id);\n ready = true;\n } catch {\n // swallow\n }\n },\n track(envelope: EventEnvelope): void {\n try {\n if (!isBrowser() || !ready || typeof window.gtag !== 'function') return;\n const params = envelope.properties ?? {};\n if (envelope.userId) (params as Record<string, unknown>)['user_id'] = envelope.userId;\n window.gtag('event', envelope.name, params);\n } catch {\n // swallow\n }\n },\n identify(userId: string, traits?: Record<string, unknown>): void {\n try {\n if (!isBrowser() || typeof window.gtag !== 'function') return;\n window.gtag('set', 'user_properties', { user_id: userId, ...traits });\n } catch {\n // swallow\n }\n },\n };\n}\n","import type { AnalyticsListener } from '../types/listener.js';\nimport type { EventEnvelope } from '../types/envelope.js';\n\nexport interface CustomApiListenerConfig {\n /** Endpoint URL for POST (e.g. https://api.example.com/analytics/events). */\n endpoint: string;\n /** Optional API key or bearer token. */\n apiKey?: string;\n /** Optional: batch size before sending. Default 1 (no batching). */\n batchSize?: number;\n /** Optional: max ms to wait before sending a partial batch. Default 5000. */\n flushIntervalMs?: number;\n}\n\nconst DEFAULT_BATCH_SIZE = 1;\nconst DEFAULT_FLUSH_INTERVAL_MS = 5000;\n\n/**\n * Listener that POSTs events to a configurable endpoint.\n * Optional batching: accumulates events and sends when batchSize or flushIntervalMs is reached.\n */\nexport function createCustomApiListener(config: CustomApiListenerConfig): AnalyticsListener {\n const endpoint = config.endpoint;\n const apiKey = config.apiKey;\n const batchSize = config.batchSize ?? DEFAULT_BATCH_SIZE;\n const flushIntervalMs = config.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\n let batch: EventEnvelope[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n function scheduleFlush(): void {\n if (flushTimer != null) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n sendBatch();\n }, flushIntervalMs);\n }\n\n function sendBatch(): void {\n if (batch.length === 0) return;\n const toSend = [...batch];\n batch = [];\n const body = JSON.stringify(toSend.length === 1 ? toSend[0] : { events: toSend });\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;\n if (typeof fetch !== 'undefined') {\n fetch(endpoint, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n }).catch(() => {});\n }\n }\n\n return {\n init(_config: unknown): void {\n // config was passed at createCustomApiListener\n },\n track(envelope: EventEnvelope): void {\n try {\n batch.push(envelope);\n if (batch.length >= batchSize) {\n sendBatch();\n } else if (flushIntervalMs > 0) {\n scheduleFlush();\n }\n } catch {\n // swallow\n }\n },\n flush(): Promise<void> {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n sendBatch();\n return Promise.resolve();\n },\n teardown(): void {\n if (flushTimer != null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n batch.length = 0;\n },\n };\n}\n","import type { ListenerEntry, ListenerFactory, RemoteAnalyticsConfig } from '../types/config.js';\nimport type { AnalyticsListener } from '../types/listener.js';\nimport { createNoopListener } from './noop.js';\nimport { createClarityListener } from './clarity.js';\nimport { createGAListener } from './ga.js';\nimport { createCustomApiListener } from './custom-api.js';\n\n/** Built-in listener IDs supported by the SDK (noop, clarity, ga, custom-api). */\nexport const BUILT_IN_LISTENER_IDS = ['noop', 'clarity', 'ga', 'custom-api'] as const;\n\nconst BUILT_IN_FACTORIES: Record<string, ListenerFactory> = {\n noop: () => createNoopListener(),\n clarity: (config) => createClarityListener(config as { projectId: string }),\n ga: (config) => createGAListener(config as { measurementId: string }),\n 'custom-api': (config) => createCustomApiListener(config as Parameters<typeof createCustomApiListener>[0]),\n};\n\n/**\n * Returns the built-in listener factory for the given id, or undefined if unknown.\n */\nexport function getBuiltInListenerFactory(id: string): ListenerFactory | undefined {\n return BUILT_IN_FACTORIES[id];\n}\n\nexport interface BuildListenersFromRemoteConfigOptions {\n /** App override: map listener id to factory. When omitted, built-in registry is used. */\n factories?: Record<string, ListenerFactory>;\n /** Called when a remote listener id has no factory (unknown id). */\n onError?: (error: Error, context?: { listenerId?: string }) => void;\n}\n\n/**\n * Builds ListenerEntry[] from remote config. Uses options.factories when provided,\n * otherwise the built-in registry. Skips entries with enabled === false or unknown id.\n */\nexport function buildListenersFromRemoteConfig(\n remote: RemoteAnalyticsConfig,\n options?: BuildListenersFromRemoteConfigOptions\n): ListenerEntry[] {\n const factories = options?.factories ?? BUILT_IN_FACTORIES;\n const onError = options?.onError;\n const entries: ListenerEntry[] = [];\n\n if (!Array.isArray(remote.listeners)) {\n return entries;\n }\n\n for (const entry of remote.listeners) {\n if (entry.enabled === false) continue;\n const id = entry.id;\n if (id == null || typeof id !== 'string') continue;\n\n const factory = factories[id];\n if (factory == null) {\n onError?.(new Error(`Unknown listener id: ${id}`), { listenerId: id });\n continue;\n }\n\n let listener: AnalyticsListener;\n try {\n listener = factory(entry.config);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: id });\n continue;\n }\n\n entries.push({\n id,\n enabled: entry.enabled,\n listener,\n config: entry.config,\n });\n }\n\n return entries;\n}\n","import type {\n AnalyticsInitConfig,\n ListenerEntry,\n PreInitBehavior,\n RemoteListenerEntry,\n} from './types/config.js';\nimport type { AnalyticsListener } from './types/listener.js';\n\nconst VALID_PRE_INIT_BEHAVIORS: PreInitBehavior[] = ['drop', 'queue'];\n\nfunction isRemoteListenerEntry(entry: unknown): entry is RemoteListenerEntry {\n if (entry == null || typeof entry !== 'object') return false;\n const o = entry as Record<string, unknown>;\n return (\n typeof o.id === 'string' &&\n o.id.trim() !== '' &&\n 'config' in o &&\n !('listener' in o && o.listener != null)\n );\n}\n\n/**\n * Validate init config. Throws with clear message on invalid config.\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.\n */\nexport function validateConfig(options: AnalyticsInitConfig): void {\n const useRemoteConfig = Boolean(options.configUrl ?? options.getConfig);\n if (typeof options.appId !== 'string' || options.appId.trim() === '') {\n throw new Error('Analytics init failed: appId is required and must be a non-empty string (e.g. from environment variables).');\n }\n if (options.listeners != null && !Array.isArray(options.listeners)) {\n throw new Error('Analytics init failed: listeners must be an array.');\n }\n const behavior = options.preInitBehavior ?? 'drop';\n if (!VALID_PRE_INIT_BEHAVIORS.includes(behavior)) {\n throw new Error(\n `Analytics init failed: preInitBehavior must be 'queue' or 'drop', got '${behavior}'.`\n );\n }\n if (behavior === 'queue') {\n const maxSize = options.queueMaxSize ?? 100;\n const ttlMs = options.queueTtlMs ?? 5000;\n if (typeof maxSize !== 'number' || maxSize <= 0) {\n throw new Error('Analytics init failed: queueMaxSize must be a positive number.');\n }\n if (typeof ttlMs !== 'number' || ttlMs <= 0) {\n throw new Error('Analytics init failed: queueTtlMs must be a positive number.');\n }\n }\n if (useRemoteConfig) {\n return;\n }\n const listeners = options.listeners ?? [];\n const isRemoteShape = listeners.length > 0 && listeners.every(isRemoteListenerEntry);\n if (isRemoteShape) {\n for (let i = 0; i < listeners.length; i++) {\n const entry = listeners[i] as RemoteListenerEntry;\n if (entry.enabled === false) continue;\n if (typeof entry.id !== 'string' || entry.id.trim() === '') {\n throw new Error(`Analytics init failed: listeners[${i}].id is required and must be a non-empty string.`);\n }\n }\n return;\n }\n for (let i = 0; i < listeners.length; i++) {\n const entry = listeners[i] as ListenerEntry;\n if (entry.enabled === false) continue;\n if (entry.listener == null || typeof entry.listener !== 'object') {\n throw new Error(`Analytics init failed: listeners[${i}].listener is required.`);\n }\n const listener = entry.listener as AnalyticsListener;\n if (typeof listener.track !== 'function') {\n throw new Error(`Analytics init failed: listeners[${i}].listener must have a track method.`);\n }\n }\n}\n","/** SDK version; set at build time or from package.json. */\nexport const SDK_VERSION = '1.0.0';\n","import type {\n AnalyticsInitConfig,\n ListenerEntry,\n OnErrorContext,\n RemoteAnalyticsConfig,\n RemoteListenerEntry,\n} from './types/config.js';\nimport { PreInitBuffer } from './buffer/pre-init-buffer.js';\nimport { ListenerRegistry } from './dispatcher/registry.js';\nimport { dispatchTrack, dispatchIdentify } from './dispatcher/dispatcher.js';\nimport type { DispatcherDeps } from './dispatcher/dispatcher.js';\nimport {\n createEnricherState,\n setUserId,\n clearUserId,\n type EnricherState,\n} from './enricher/enricher.js';\nimport { buildListenersFromRemoteConfig } from './listeners/registry.js';\nimport { validateConfig } from './validate-config.js';\nimport { SDK_VERSION } from './version.js';\n\nlet enricherState: EnricherState | null = null;\nlet registry: ListenerRegistry | null = null;\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. */\nlet preInitBuffer: PreInitBuffer | null = null;\nlet onError: ((error: Error, context?: OnErrorContext) => void) | undefined;\nlet initialized = false;\n\n/** Default buffer before init: queue mode so events before init can be replayed. */\nfunction getOrCreatePreInitBuffer(): PreInitBuffer {\n if (preInitBuffer == null) {\n preInitBuffer = new PreInitBuffer({\n mode: 'queue',\n maxSize: 100,\n ttlMs: 5000,\n });\n }\n return preInitBuffer;\n}\n\nfunction getEnricherState(): EnricherState {\n if (enricherState == null) {\n throw new Error('Analytics not initialized.');\n }\n return enricherState;\n}\n\nasync function fetchRemoteConfig(options: AnalyticsInitConfig): Promise<RemoteAnalyticsConfig | null> {\n if (options.getConfig) {\n return options.getConfig();\n }\n if (options.configUrl) {\n const fetchFn = options.fetch ?? globalThis.fetch;\n const res = await fetchFn(options.configUrl);\n if (!res.ok) {\n throw new Error(`Analytics config fetch failed: ${res.status} ${res.statusText}`);\n }\n const json = await res.json();\n if (json == null || typeof json !== 'object') {\n throw new Error('Analytics config response must be a JSON object.');\n }\n return json as RemoteAnalyticsConfig;\n }\n return null;\n}\n\nfunction isInlineRemoteShape(listeners: unknown): listeners is RemoteListenerEntry[] {\n if (!Array.isArray(listeners) || listeners.length === 0) return false;\n return listeners.every(\n (entry): entry is RemoteListenerEntry =>\n entry != null &&\n typeof entry === 'object' &&\n typeof (entry as RemoteListenerEntry).id === 'string' &&\n 'config' in entry &&\n !('listener' in entry && (entry as ListenerEntry).listener != null)\n );\n}\n\n/**\n * Initialize the SDK. Idempotent; calling again re-applies config.\n * Pre-queued events (if queue mode) are flushed after listeners are ready.\n * When configUrl or getConfig is set, SDK fetches config and auto-configures listeners from the built-in registry (or listenerFactories).\n */\nexport async function init(options: AnalyticsInitConfig): Promise<void> {\n validateConfig(options);\n\n let appId: string;\n let environment: string;\n let listeners: ListenerEntry[];\n\n const remote = await fetchRemoteConfig(options);\n if (remote != null) {\n appId = (options.appId ?? '').toString().trim();\n environment = options.environment ?? 'production';\n if (!appId) {\n throw new Error('Analytics init failed: appId is required (set in init options, e.g. from environment variables).');\n }\n listeners = buildListenersFromRemoteConfig(remote, {\n factories: options.listenerFactories,\n onError: options.onError,\n });\n } else {\n appId = options.appId.trim();\n environment = options.environment ?? 'production';\n const rawListeners = options.listeners ?? [];\n if (isInlineRemoteShape(rawListeners)) {\n listeners = buildListenersFromRemoteConfig(\n { listeners: rawListeners },\n {\n factories: options.listenerFactories,\n onError: options.onError,\n }\n );\n } else {\n listeners = rawListeners as ListenerEntry[];\n }\n }\n\n const preInitBehavior = options.preInitBehavior ?? 'drop';\n const queueMaxSize = options.queueMaxSize ?? 100;\n const queueTtlMs = options.queueTtlMs ?? 5000;\n onError = options.onError;\n\n const mode = preInitBehavior === 'queue' ? 'queue' : 'drop';\n preInitBuffer = new PreInitBuffer({ mode, maxSize: queueMaxSize, ttlMs: queueTtlMs });\n\n enricherState = createEnricherState(appId, environment, SDK_VERSION, options.sessionId);\n registry = new ListenerRegistry();\n\n for (const entry of listeners) {\n if (entry.enabled === false) continue;\n try {\n const initResult = entry.listener.init(entry.config);\n if (initResult instanceof Promise) {\n await initResult;\n }\n registry.add(entry.id, entry.listener);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, { listenerId: entry.id });\n }\n }\n\n const deps: DispatcherDeps = {\n registry,\n getEnricherState,\n onError,\n };\n\n if (preInitBuffer) {\n if (preInitBehavior === 'queue') {\n preInitBuffer.flush(\n (payload) => dispatchTrack(deps, payload),\n (userId, traits) => {\n setUserId(enricherState!, userId);\n dispatchIdentify(deps, userId, traits);\n }\n );\n } else {\n preInitBuffer.flush(() => {}, () => {});\n }\n preInitBuffer = null;\n }\n\n initialized = true;\n}\n\n/**\n * Track an event. Fire-and-forget. Safe before init (queued or dropped per config).\n */\nexport function track(event: { name: string; properties?: Record<string, unknown>; timestamp?: string }): void {\n if (!initialized) {\n getOrCreatePreInitBuffer().pushTrack(event.name, event.properties, event.timestamp);\n return;\n }\n const deps: DispatcherDeps = {\n registry: registry!,\n getEnricherState,\n onError,\n };\n dispatchTrack(deps, event);\n}\n\n/**\n * Identify the user. Fire-and-forget. Safe before init (queued or dropped per config).\n */\nexport function identify(userId: string, traits?: Record<string, unknown>): void {\n if (!initialized) {\n getOrCreatePreInitBuffer().pushIdentify(userId, traits);\n return;\n }\n if (enricherState) setUserId(enricherState, userId);\n const deps: DispatcherDeps = {\n registry: registry!,\n getEnricherState,\n onError,\n };\n dispatchIdentify(deps, userId, traits);\n}\n\n/**\n * Flush listeners that batch (e.g. before page unload).\n */\nexport async function flush(): Promise<void> {\n if (!initialized || registry == null) return;\n const promises: Promise<void>[] = [];\n for (const { listener } of registry.getAll()) {\n if (typeof listener.flush === 'function') {\n const result = listener.flush();\n if (result instanceof Promise) promises.push(result);\n }\n }\n await Promise.all(promises);\n}\n\n/**\n * Clear queue and teardown listeners. Useful in tests or when identity changes.\n */\nexport async function reset(): Promise<void> {\n if (preInitBuffer) {\n preInitBuffer.flush(() => {}, () => {});\n }\n if (enricherState) clearUserId(enricherState);\n if (registry) {\n for (const { listener } of registry.getAll()) {\n if (typeof listener.teardown === 'function') {\n try {\n const result = listener.teardown();\n if (result instanceof Promise) await result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n onError?.(error, {});\n }\n }\n }\n registry.clear();\n }\n preInitBuffer = null;\n enricherState = null;\n registry = null;\n onError = undefined;\n initialized = false;\n}\n\nexport function isInitialized(): boolean {\n return initialized;\n}\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"]}
|