@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/README.md
CHANGED
|
@@ -110,6 +110,29 @@ loamly.init({
|
|
|
110
110
|
});
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
+
### Lightweight Mode
|
|
114
|
+
|
|
115
|
+
Disable specific features to reduce CPU/memory overhead:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
loamly.init({
|
|
119
|
+
apiKey: 'your-api-key',
|
|
120
|
+
features: {
|
|
121
|
+
scroll: true, // Scroll depth tracking (default: true)
|
|
122
|
+
time: true, // Time on page tracking (default: true)
|
|
123
|
+
forms: true, // Form interaction tracking (default: true)
|
|
124
|
+
spa: true, // SPA navigation support (default: true)
|
|
125
|
+
behavioralML: false, // Behavioral ML classification (default: true) - saves ~2KB CPU
|
|
126
|
+
focusBlur: true, // Focus/blur paste detection (default: true)
|
|
127
|
+
agentic: false, // Agentic browser detection (default: true) - saves ~1.5KB CPU
|
|
128
|
+
eventQueue: true, // Event queue with retry (default: true)
|
|
129
|
+
ping: false, // Heartbeat ping service (default: false - opt-in)
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Note:** All features are included in the bundle (~11KB gzipped). The `features` config only affects runtime initialization, not download size.
|
|
135
|
+
|
|
113
136
|
### `track(eventName, options?)`
|
|
114
137
|
|
|
115
138
|
Track a custom event.
|
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(index_exports);
|
|
35
35
|
|
|
36
36
|
// src/config.ts
|
|
37
|
-
var VERSION = "2.0
|
|
37
|
+
var VERSION = "2.1.0";
|
|
38
38
|
var DEFAULT_CONFIG = {
|
|
39
39
|
apiHost: "https://app.loamly.ai",
|
|
40
40
|
endpoints: {
|
|
@@ -1805,16 +1805,32 @@ function init(userConfig = {}) {
|
|
|
1805
1805
|
apiHost: userConfig.apiHost || DEFAULT_CONFIG.apiHost
|
|
1806
1806
|
};
|
|
1807
1807
|
debugMode = userConfig.debug ?? false;
|
|
1808
|
+
const features = {
|
|
1809
|
+
scroll: true,
|
|
1810
|
+
time: true,
|
|
1811
|
+
forms: true,
|
|
1812
|
+
spa: true,
|
|
1813
|
+
behavioralML: true,
|
|
1814
|
+
focusBlur: true,
|
|
1815
|
+
agentic: true,
|
|
1816
|
+
eventQueue: true,
|
|
1817
|
+
ping: false,
|
|
1818
|
+
// Opt-in only
|
|
1819
|
+
...userConfig.features
|
|
1820
|
+
};
|
|
1808
1821
|
log("Initializing Loamly Tracker v" + VERSION);
|
|
1822
|
+
log("Features:", features);
|
|
1809
1823
|
visitorId = getVisitorId();
|
|
1810
1824
|
log("Visitor ID:", visitorId);
|
|
1811
1825
|
const session = getSessionId();
|
|
1812
1826
|
sessionId = session.sessionId;
|
|
1813
1827
|
log("Session ID:", sessionId, session.isNew ? "(new)" : "(existing)");
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1828
|
+
if (features.eventQueue) {
|
|
1829
|
+
eventQueue = new EventQueue(endpoint(DEFAULT_CONFIG.endpoints.behavioral), {
|
|
1830
|
+
batchSize: DEFAULT_CONFIG.batchSize,
|
|
1831
|
+
batchTimeout: DEFAULT_CONFIG.batchTimeout
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1818
1834
|
navigationTiming = detectNavigationType();
|
|
1819
1835
|
log("Navigation timing:", navigationTiming);
|
|
1820
1836
|
aiDetection = detectAIFromReferrer(document.referrer) || detectAIFromUTM(window.location.href);
|
|
@@ -1826,21 +1842,27 @@ function init(userConfig = {}) {
|
|
|
1826
1842
|
pageview();
|
|
1827
1843
|
}
|
|
1828
1844
|
if (!userConfig.disableBehavioral) {
|
|
1829
|
-
setupAdvancedBehavioralTracking();
|
|
1845
|
+
setupAdvancedBehavioralTracking(features);
|
|
1846
|
+
}
|
|
1847
|
+
if (features.behavioralML) {
|
|
1848
|
+
behavioralClassifier = new BehavioralClassifier(1e4);
|
|
1849
|
+
behavioralClassifier.setOnClassify(handleBehavioralClassification);
|
|
1850
|
+
setupBehavioralMLTracking();
|
|
1851
|
+
}
|
|
1852
|
+
if (features.focusBlur) {
|
|
1853
|
+
focusBlurAnalyzer = new FocusBlurAnalyzer();
|
|
1854
|
+
focusBlurAnalyzer.initTracking();
|
|
1855
|
+
setTimeout(() => {
|
|
1856
|
+
if (focusBlurAnalyzer) {
|
|
1857
|
+
handleFocusBlurAnalysis(focusBlurAnalyzer.analyze());
|
|
1858
|
+
}
|
|
1859
|
+
}, 5e3);
|
|
1830
1860
|
}
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
setTimeout(() => {
|
|
1837
|
-
if (focusBlurAnalyzer) {
|
|
1838
|
-
handleFocusBlurAnalysis(focusBlurAnalyzer.analyze());
|
|
1839
|
-
}
|
|
1840
|
-
}, 5e3);
|
|
1841
|
-
agenticAnalyzer = new AgenticBrowserAnalyzer();
|
|
1842
|
-
agenticAnalyzer.init();
|
|
1843
|
-
if (visitorId && sessionId) {
|
|
1861
|
+
if (features.agentic) {
|
|
1862
|
+
agenticAnalyzer = new AgenticBrowserAnalyzer();
|
|
1863
|
+
agenticAnalyzer.init();
|
|
1864
|
+
}
|
|
1865
|
+
if (features.ping && visitorId && sessionId) {
|
|
1844
1866
|
pingService = new PingService(sessionId, visitorId, VERSION, {
|
|
1845
1867
|
interval: DEFAULT_CONFIG.pingInterval,
|
|
1846
1868
|
endpoint: endpoint(DEFAULT_CONFIG.endpoints.ping)
|
|
@@ -1852,50 +1874,66 @@ function init(userConfig = {}) {
|
|
|
1852
1874
|
});
|
|
1853
1875
|
spaRouter.start();
|
|
1854
1876
|
setupUnloadHandlers();
|
|
1877
|
+
reportHealth("initialized");
|
|
1855
1878
|
log("Initialization complete");
|
|
1856
1879
|
}
|
|
1857
|
-
function setupAdvancedBehavioralTracking() {
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
}
|
|
1868
|
-
});
|
|
1869
|
-
scrollTracker.start();
|
|
1870
|
-
timeTracker = new TimeTracker({
|
|
1871
|
-
updateIntervalMs: 1e4,
|
|
1872
|
-
// Report every 10 seconds
|
|
1873
|
-
onUpdate: (event) => {
|
|
1874
|
-
if (event.active_time_ms >= DEFAULT_CONFIG.timeSpentThresholdMs) {
|
|
1875
|
-
queueEvent("time_spent", {
|
|
1876
|
-
active_time_ms: event.active_time_ms,
|
|
1877
|
-
total_time_ms: event.total_time_ms,
|
|
1878
|
-
idle_time_ms: event.idle_time_ms,
|
|
1879
|
-
is_engaged: event.is_engaged
|
|
1880
|
+
function setupAdvancedBehavioralTracking(features) {
|
|
1881
|
+
if (features.scroll) {
|
|
1882
|
+
scrollTracker = new ScrollTracker({
|
|
1883
|
+
chunks: [30, 60, 90, 100],
|
|
1884
|
+
onChunkReached: (event) => {
|
|
1885
|
+
log("Scroll chunk:", event.chunk);
|
|
1886
|
+
queueEvent("scroll_depth", {
|
|
1887
|
+
depth: event.depth,
|
|
1888
|
+
chunk: event.chunk,
|
|
1889
|
+
time_to_reach_ms: event.time_to_reach_ms
|
|
1880
1890
|
});
|
|
1881
1891
|
}
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1892
|
+
});
|
|
1893
|
+
scrollTracker.start();
|
|
1894
|
+
}
|
|
1895
|
+
if (features.time) {
|
|
1896
|
+
timeTracker = new TimeTracker({
|
|
1897
|
+
updateIntervalMs: 1e4,
|
|
1898
|
+
// Report every 10 seconds
|
|
1899
|
+
onUpdate: (event) => {
|
|
1900
|
+
if (event.active_time_ms >= DEFAULT_CONFIG.timeSpentThresholdMs) {
|
|
1901
|
+
queueEvent("time_spent", {
|
|
1902
|
+
active_time_ms: event.active_time_ms,
|
|
1903
|
+
total_time_ms: event.total_time_ms,
|
|
1904
|
+
idle_time_ms: event.idle_time_ms,
|
|
1905
|
+
is_engaged: event.is_engaged
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
1909
|
+
});
|
|
1910
|
+
timeTracker.start();
|
|
1911
|
+
}
|
|
1912
|
+
if (features.forms) {
|
|
1913
|
+
formTracker = new FormTracker({
|
|
1914
|
+
onFormEvent: (event) => {
|
|
1915
|
+
log("Form event:", event.event_type, event.form_id);
|
|
1916
|
+
queueEvent(event.event_type, {
|
|
1917
|
+
form_id: event.form_id,
|
|
1918
|
+
form_type: event.form_type,
|
|
1919
|
+
field_name: event.field_name,
|
|
1920
|
+
field_type: event.field_type,
|
|
1921
|
+
time_to_submit_ms: event.time_to_submit_ms,
|
|
1922
|
+
is_conversion: event.is_conversion
|
|
1923
|
+
});
|
|
1924
|
+
}
|
|
1925
|
+
});
|
|
1926
|
+
formTracker.start();
|
|
1927
|
+
}
|
|
1928
|
+
if (features.spa) {
|
|
1929
|
+
spaRouter = new SPARouter({
|
|
1930
|
+
onNavigate: (event) => {
|
|
1931
|
+
log("SPA navigation:", event.navigation_type);
|
|
1932
|
+
pageview(event.to_url);
|
|
1933
|
+
}
|
|
1934
|
+
});
|
|
1935
|
+
spaRouter.start();
|
|
1936
|
+
}
|
|
1899
1937
|
document.addEventListener("click", (e) => {
|
|
1900
1938
|
const target = e.target;
|
|
1901
1939
|
const link = target.closest("a");
|
|
@@ -2197,6 +2235,39 @@ function getAgenticResult() {
|
|
|
2197
2235
|
function isTrackerInitialized() {
|
|
2198
2236
|
return initialized;
|
|
2199
2237
|
}
|
|
2238
|
+
function reportHealth(status, errorMessage) {
|
|
2239
|
+
if (!config.apiKey) return;
|
|
2240
|
+
try {
|
|
2241
|
+
const healthData = {
|
|
2242
|
+
workspace_id: config.apiKey,
|
|
2243
|
+
status,
|
|
2244
|
+
error_message: errorMessage || null,
|
|
2245
|
+
version: VERSION,
|
|
2246
|
+
url: typeof window !== "undefined" ? window.location.href : null,
|
|
2247
|
+
user_agent: typeof navigator !== "undefined" ? navigator.userAgent : null,
|
|
2248
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2249
|
+
features: {
|
|
2250
|
+
scroll_tracker: !!scrollTracker,
|
|
2251
|
+
time_tracker: !!timeTracker,
|
|
2252
|
+
form_tracker: !!formTracker,
|
|
2253
|
+
spa_router: !!spaRouter,
|
|
2254
|
+
behavioral_ml: !!behavioralClassifier,
|
|
2255
|
+
focus_blur: !!focusBlurAnalyzer,
|
|
2256
|
+
agentic: !!agenticAnalyzer,
|
|
2257
|
+
ping_service: !!pingService,
|
|
2258
|
+
event_queue: !!eventQueue
|
|
2259
|
+
}
|
|
2260
|
+
};
|
|
2261
|
+
safeFetch(endpoint(DEFAULT_CONFIG.endpoints.health), {
|
|
2262
|
+
method: "POST",
|
|
2263
|
+
headers: { "Content-Type": "application/json" },
|
|
2264
|
+
body: JSON.stringify(healthData)
|
|
2265
|
+
}).catch(() => {
|
|
2266
|
+
});
|
|
2267
|
+
log("Health reported:", status);
|
|
2268
|
+
} catch {
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2200
2271
|
function reset() {
|
|
2201
2272
|
log("Resetting tracker");
|
|
2202
2273
|
pingService?.stop();
|
|
@@ -2246,7 +2317,8 @@ var loamly = {
|
|
|
2246
2317
|
getAgentic: getAgenticResult,
|
|
2247
2318
|
isInitialized: isTrackerInitialized,
|
|
2248
2319
|
reset,
|
|
2249
|
-
debug: setDebug
|
|
2320
|
+
debug: setDebug,
|
|
2321
|
+
reportHealth
|
|
2250
2322
|
};
|
|
2251
2323
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2252
2324
|
0 && (module.exports = {
|