@loamly/tracker 2.0.1 → 2.1.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/README.md +23 -0
- package/dist/index.cjs +132 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.mjs +132 -60
- package/dist/index.mjs.map +1 -1
- package/dist/loamly.iife.global.js +132 -60
- package/dist/loamly.iife.global.js.map +1 -1
- package/dist/loamly.iife.min.global.js +1 -1
- package/dist/loamly.iife.min.global.js.map +1 -1
- package/package.json +11 -9
- package/src/config.ts +1 -1
- package/src/core.ts +177 -74
- package/src/types.ts +25 -0
- package/LICENSE +0 -23
package/dist/index.d.mts
CHANGED
|
@@ -77,6 +77,30 @@ interface LoamlyConfig {
|
|
|
77
77
|
disableBehavioral?: boolean;
|
|
78
78
|
/** Custom session timeout in milliseconds (default: 30 minutes) */
|
|
79
79
|
sessionTimeout?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Feature flags for lightweight mode
|
|
82
|
+
* Set to false to reduce initialization overhead
|
|
83
|
+
*/
|
|
84
|
+
features?: {
|
|
85
|
+
/** Scroll depth tracking (default: true) */
|
|
86
|
+
scroll?: boolean;
|
|
87
|
+
/** Time on page tracking (default: true) */
|
|
88
|
+
time?: boolean;
|
|
89
|
+
/** Form interaction tracking (default: true) */
|
|
90
|
+
forms?: boolean;
|
|
91
|
+
/** SPA navigation support (default: true) */
|
|
92
|
+
spa?: boolean;
|
|
93
|
+
/** Behavioral ML classification (default: true) */
|
|
94
|
+
behavioralML?: boolean;
|
|
95
|
+
/** Focus/blur paste detection (default: true) */
|
|
96
|
+
focusBlur?: boolean;
|
|
97
|
+
/** Agentic browser detection (default: true) */
|
|
98
|
+
agentic?: boolean;
|
|
99
|
+
/** Event queue with retry (default: true) */
|
|
100
|
+
eventQueue?: boolean;
|
|
101
|
+
/** Heartbeat ping service (default: false - opt-in) */
|
|
102
|
+
ping?: boolean;
|
|
103
|
+
};
|
|
80
104
|
}
|
|
81
105
|
interface TrackEventOptions {
|
|
82
106
|
/** Custom properties to attach to the event */
|
|
@@ -189,6 +213,7 @@ interface LoamlyTracker {
|
|
|
189
213
|
*/
|
|
190
214
|
declare const loamly: LoamlyTracker & {
|
|
191
215
|
getAgentic: () => AgenticDetectionResult | null;
|
|
216
|
+
reportHealth: (status: 'initialized' | 'error' | 'ready', errorMessage?: string) => void;
|
|
192
217
|
};
|
|
193
218
|
|
|
194
219
|
/**
|
|
@@ -245,7 +270,7 @@ declare function detectAIFromUTM(url: string): AIDetectionResult | null;
|
|
|
245
270
|
* @license MIT
|
|
246
271
|
* @see https://github.com/loamly/loamly
|
|
247
272
|
*/
|
|
248
|
-
declare const VERSION = "2.0
|
|
273
|
+
declare const VERSION = "2.1.0";
|
|
249
274
|
/**
|
|
250
275
|
* Known AI platforms for referrer detection
|
|
251
276
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -77,6 +77,30 @@ interface LoamlyConfig {
|
|
|
77
77
|
disableBehavioral?: boolean;
|
|
78
78
|
/** Custom session timeout in milliseconds (default: 30 minutes) */
|
|
79
79
|
sessionTimeout?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Feature flags for lightweight mode
|
|
82
|
+
* Set to false to reduce initialization overhead
|
|
83
|
+
*/
|
|
84
|
+
features?: {
|
|
85
|
+
/** Scroll depth tracking (default: true) */
|
|
86
|
+
scroll?: boolean;
|
|
87
|
+
/** Time on page tracking (default: true) */
|
|
88
|
+
time?: boolean;
|
|
89
|
+
/** Form interaction tracking (default: true) */
|
|
90
|
+
forms?: boolean;
|
|
91
|
+
/** SPA navigation support (default: true) */
|
|
92
|
+
spa?: boolean;
|
|
93
|
+
/** Behavioral ML classification (default: true) */
|
|
94
|
+
behavioralML?: boolean;
|
|
95
|
+
/** Focus/blur paste detection (default: true) */
|
|
96
|
+
focusBlur?: boolean;
|
|
97
|
+
/** Agentic browser detection (default: true) */
|
|
98
|
+
agentic?: boolean;
|
|
99
|
+
/** Event queue with retry (default: true) */
|
|
100
|
+
eventQueue?: boolean;
|
|
101
|
+
/** Heartbeat ping service (default: false - opt-in) */
|
|
102
|
+
ping?: boolean;
|
|
103
|
+
};
|
|
80
104
|
}
|
|
81
105
|
interface TrackEventOptions {
|
|
82
106
|
/** Custom properties to attach to the event */
|
|
@@ -189,6 +213,7 @@ interface LoamlyTracker {
|
|
|
189
213
|
*/
|
|
190
214
|
declare const loamly: LoamlyTracker & {
|
|
191
215
|
getAgentic: () => AgenticDetectionResult | null;
|
|
216
|
+
reportHealth: (status: 'initialized' | 'error' | 'ready', errorMessage?: string) => void;
|
|
192
217
|
};
|
|
193
218
|
|
|
194
219
|
/**
|
|
@@ -245,7 +270,7 @@ declare function detectAIFromUTM(url: string): AIDetectionResult | null;
|
|
|
245
270
|
* @license MIT
|
|
246
271
|
* @see https://github.com/loamly/loamly
|
|
247
272
|
*/
|
|
248
|
-
declare const VERSION = "2.0
|
|
273
|
+
declare const VERSION = "2.1.0";
|
|
249
274
|
/**
|
|
250
275
|
* Known AI platforms for referrer detection
|
|
251
276
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/config.ts
|
|
2
|
-
var VERSION = "2.0
|
|
2
|
+
var VERSION = "2.1.0";
|
|
3
3
|
var DEFAULT_CONFIG = {
|
|
4
4
|
apiHost: "https://app.loamly.ai",
|
|
5
5
|
endpoints: {
|
|
@@ -1770,16 +1770,32 @@ function init(userConfig = {}) {
|
|
|
1770
1770
|
apiHost: userConfig.apiHost || DEFAULT_CONFIG.apiHost
|
|
1771
1771
|
};
|
|
1772
1772
|
debugMode = userConfig.debug ?? false;
|
|
1773
|
+
const features = {
|
|
1774
|
+
scroll: true,
|
|
1775
|
+
time: true,
|
|
1776
|
+
forms: true,
|
|
1777
|
+
spa: true,
|
|
1778
|
+
behavioralML: true,
|
|
1779
|
+
focusBlur: true,
|
|
1780
|
+
agentic: true,
|
|
1781
|
+
eventQueue: true,
|
|
1782
|
+
ping: false,
|
|
1783
|
+
// Opt-in only
|
|
1784
|
+
...userConfig.features
|
|
1785
|
+
};
|
|
1773
1786
|
log("Initializing Loamly Tracker v" + VERSION);
|
|
1787
|
+
log("Features:", features);
|
|
1774
1788
|
visitorId = getVisitorId();
|
|
1775
1789
|
log("Visitor ID:", visitorId);
|
|
1776
1790
|
const session = getSessionId();
|
|
1777
1791
|
sessionId = session.sessionId;
|
|
1778
1792
|
log("Session ID:", sessionId, session.isNew ? "(new)" : "(existing)");
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1793
|
+
if (features.eventQueue) {
|
|
1794
|
+
eventQueue = new EventQueue(endpoint(DEFAULT_CONFIG.endpoints.behavioral), {
|
|
1795
|
+
batchSize: DEFAULT_CONFIG.batchSize,
|
|
1796
|
+
batchTimeout: DEFAULT_CONFIG.batchTimeout
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1783
1799
|
navigationTiming = detectNavigationType();
|
|
1784
1800
|
log("Navigation timing:", navigationTiming);
|
|
1785
1801
|
aiDetection = detectAIFromReferrer(document.referrer) || detectAIFromUTM(window.location.href);
|
|
@@ -1791,21 +1807,27 @@ function init(userConfig = {}) {
|
|
|
1791
1807
|
pageview();
|
|
1792
1808
|
}
|
|
1793
1809
|
if (!userConfig.disableBehavioral) {
|
|
1794
|
-
setupAdvancedBehavioralTracking();
|
|
1810
|
+
setupAdvancedBehavioralTracking(features);
|
|
1811
|
+
}
|
|
1812
|
+
if (features.behavioralML) {
|
|
1813
|
+
behavioralClassifier = new BehavioralClassifier(1e4);
|
|
1814
|
+
behavioralClassifier.setOnClassify(handleBehavioralClassification);
|
|
1815
|
+
setupBehavioralMLTracking();
|
|
1816
|
+
}
|
|
1817
|
+
if (features.focusBlur) {
|
|
1818
|
+
focusBlurAnalyzer = new FocusBlurAnalyzer();
|
|
1819
|
+
focusBlurAnalyzer.initTracking();
|
|
1820
|
+
setTimeout(() => {
|
|
1821
|
+
if (focusBlurAnalyzer) {
|
|
1822
|
+
handleFocusBlurAnalysis(focusBlurAnalyzer.analyze());
|
|
1823
|
+
}
|
|
1824
|
+
}, 5e3);
|
|
1795
1825
|
}
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
setTimeout(() => {
|
|
1802
|
-
if (focusBlurAnalyzer) {
|
|
1803
|
-
handleFocusBlurAnalysis(focusBlurAnalyzer.analyze());
|
|
1804
|
-
}
|
|
1805
|
-
}, 5e3);
|
|
1806
|
-
agenticAnalyzer = new AgenticBrowserAnalyzer();
|
|
1807
|
-
agenticAnalyzer.init();
|
|
1808
|
-
if (visitorId && sessionId) {
|
|
1826
|
+
if (features.agentic) {
|
|
1827
|
+
agenticAnalyzer = new AgenticBrowserAnalyzer();
|
|
1828
|
+
agenticAnalyzer.init();
|
|
1829
|
+
}
|
|
1830
|
+
if (features.ping && visitorId && sessionId) {
|
|
1809
1831
|
pingService = new PingService(sessionId, visitorId, VERSION, {
|
|
1810
1832
|
interval: DEFAULT_CONFIG.pingInterval,
|
|
1811
1833
|
endpoint: endpoint(DEFAULT_CONFIG.endpoints.ping)
|
|
@@ -1817,50 +1839,66 @@ function init(userConfig = {}) {
|
|
|
1817
1839
|
});
|
|
1818
1840
|
spaRouter.start();
|
|
1819
1841
|
setupUnloadHandlers();
|
|
1842
|
+
reportHealth("initialized");
|
|
1820
1843
|
log("Initialization complete");
|
|
1821
1844
|
}
|
|
1822
|
-
function setupAdvancedBehavioralTracking() {
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
}
|
|
1833
|
-
});
|
|
1834
|
-
scrollTracker.start();
|
|
1835
|
-
timeTracker = new TimeTracker({
|
|
1836
|
-
updateIntervalMs: 1e4,
|
|
1837
|
-
// Report every 10 seconds
|
|
1838
|
-
onUpdate: (event) => {
|
|
1839
|
-
if (event.active_time_ms >= DEFAULT_CONFIG.timeSpentThresholdMs) {
|
|
1840
|
-
queueEvent("time_spent", {
|
|
1841
|
-
active_time_ms: event.active_time_ms,
|
|
1842
|
-
total_time_ms: event.total_time_ms,
|
|
1843
|
-
idle_time_ms: event.idle_time_ms,
|
|
1844
|
-
is_engaged: event.is_engaged
|
|
1845
|
+
function setupAdvancedBehavioralTracking(features) {
|
|
1846
|
+
if (features.scroll) {
|
|
1847
|
+
scrollTracker = new ScrollTracker({
|
|
1848
|
+
chunks: [30, 60, 90, 100],
|
|
1849
|
+
onChunkReached: (event) => {
|
|
1850
|
+
log("Scroll chunk:", event.chunk);
|
|
1851
|
+
queueEvent("scroll_depth", {
|
|
1852
|
+
depth: event.depth,
|
|
1853
|
+
chunk: event.chunk,
|
|
1854
|
+
time_to_reach_ms: event.time_to_reach_ms
|
|
1845
1855
|
});
|
|
1846
1856
|
}
|
|
1847
|
-
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1857
|
+
});
|
|
1858
|
+
scrollTracker.start();
|
|
1859
|
+
}
|
|
1860
|
+
if (features.time) {
|
|
1861
|
+
timeTracker = new TimeTracker({
|
|
1862
|
+
updateIntervalMs: 1e4,
|
|
1863
|
+
// Report every 10 seconds
|
|
1864
|
+
onUpdate: (event) => {
|
|
1865
|
+
if (event.active_time_ms >= DEFAULT_CONFIG.timeSpentThresholdMs) {
|
|
1866
|
+
queueEvent("time_spent", {
|
|
1867
|
+
active_time_ms: event.active_time_ms,
|
|
1868
|
+
total_time_ms: event.total_time_ms,
|
|
1869
|
+
idle_time_ms: event.idle_time_ms,
|
|
1870
|
+
is_engaged: event.is_engaged
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
timeTracker.start();
|
|
1876
|
+
}
|
|
1877
|
+
if (features.forms) {
|
|
1878
|
+
formTracker = new FormTracker({
|
|
1879
|
+
onFormEvent: (event) => {
|
|
1880
|
+
log("Form event:", event.event_type, event.form_id);
|
|
1881
|
+
queueEvent(event.event_type, {
|
|
1882
|
+
form_id: event.form_id,
|
|
1883
|
+
form_type: event.form_type,
|
|
1884
|
+
field_name: event.field_name,
|
|
1885
|
+
field_type: event.field_type,
|
|
1886
|
+
time_to_submit_ms: event.time_to_submit_ms,
|
|
1887
|
+
is_conversion: event.is_conversion
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
});
|
|
1891
|
+
formTracker.start();
|
|
1892
|
+
}
|
|
1893
|
+
if (features.spa) {
|
|
1894
|
+
spaRouter = new SPARouter({
|
|
1895
|
+
onNavigate: (event) => {
|
|
1896
|
+
log("SPA navigation:", event.navigation_type);
|
|
1897
|
+
pageview(event.to_url);
|
|
1898
|
+
}
|
|
1899
|
+
});
|
|
1900
|
+
spaRouter.start();
|
|
1901
|
+
}
|
|
1864
1902
|
document.addEventListener("click", (e) => {
|
|
1865
1903
|
const target = e.target;
|
|
1866
1904
|
const link = target.closest("a");
|
|
@@ -2162,6 +2200,39 @@ function getAgenticResult() {
|
|
|
2162
2200
|
function isTrackerInitialized() {
|
|
2163
2201
|
return initialized;
|
|
2164
2202
|
}
|
|
2203
|
+
function reportHealth(status, errorMessage) {
|
|
2204
|
+
if (!config.apiKey) return;
|
|
2205
|
+
try {
|
|
2206
|
+
const healthData = {
|
|
2207
|
+
workspace_id: config.apiKey,
|
|
2208
|
+
status,
|
|
2209
|
+
error_message: errorMessage || null,
|
|
2210
|
+
version: VERSION,
|
|
2211
|
+
url: typeof window !== "undefined" ? window.location.href : null,
|
|
2212
|
+
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : null,
|
|
2213
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2214
|
+
features: {
|
|
2215
|
+
scroll_tracker: !!scrollTracker,
|
|
2216
|
+
time_tracker: !!timeTracker,
|
|
2217
|
+
form_tracker: !!formTracker,
|
|
2218
|
+
spa_router: !!spaRouter,
|
|
2219
|
+
behavioral_ml: !!behavioralClassifier,
|
|
2220
|
+
focus_blur: !!focusBlurAnalyzer,
|
|
2221
|
+
agentic: !!agenticAnalyzer,
|
|
2222
|
+
ping_service: !!pingService,
|
|
2223
|
+
event_queue: !!eventQueue
|
|
2224
|
+
}
|
|
2225
|
+
};
|
|
2226
|
+
safeFetch(endpoint(DEFAULT_CONFIG.endpoints.health), {
|
|
2227
|
+
method: "POST",
|
|
2228
|
+
headers: { "Content-Type": "application/json" },
|
|
2229
|
+
body: JSON.stringify(healthData)
|
|
2230
|
+
}).catch(() => {
|
|
2231
|
+
});
|
|
2232
|
+
log("Health reported:", status);
|
|
2233
|
+
} catch {
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2165
2236
|
function reset() {
|
|
2166
2237
|
log("Resetting tracker");
|
|
2167
2238
|
pingService?.stop();
|
|
@@ -2211,7 +2282,8 @@ var loamly = {
|
|
|
2211
2282
|
getAgentic: getAgenticResult,
|
|
2212
2283
|
isInitialized: isTrackerInitialized,
|
|
2213
2284
|
reset,
|
|
2214
|
-
debug: setDebug
|
|
2285
|
+
debug: setDebug,
|
|
2286
|
+
reportHealth
|
|
2215
2287
|
};
|
|
2216
2288
|
export {
|
|
2217
2289
|
AI_BOT_PATTERNS,
|