onedollarstats 0.0.16 → 0.0.18
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/index.js +68 -142
- package/dist/index.js.map +3 -3
- package/dist/types.d.ts +11 -12
- package/package.json +31 -7
- package/dist/utils/bot.d.ts +0 -50
- package/dist/utils/create-modal.d.ts +0 -1
- package/dist/utils/environment.d.ts +0 -5
- package/dist/utils/merge-config.d.ts +0 -3
- package/dist/utils/merge-config.test.d.ts +0 -1
- package/dist/utils/parse-utm-params.d.ts +0 -1
- package/dist/utils/props-parser.d.ts +0 -1
- package/dist/utils/resolve-path.d.ts +0 -1
- package/dist/utils/should-track.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -179,9 +179,11 @@ function detectLies() {
|
|
|
179
179
|
}
|
|
180
180
|
if (name.includes(".prototype.") && typeof val !== "function") {
|
|
181
181
|
const parts = name.split(".");
|
|
182
|
-
const
|
|
182
|
+
const protoName = parts[0];
|
|
183
183
|
const prop = parts[parts.length - 1];
|
|
184
|
-
if (
|
|
184
|
+
if (protoName && prop) {
|
|
185
|
+
const proto = safeProto(protoName);
|
|
186
|
+
if (!proto) continue;
|
|
185
187
|
const d = Object.getOwnPropertyDescriptor(proto, prop);
|
|
186
188
|
if (d?.get) {
|
|
187
189
|
const gs = Function.prototype.toString.call(d.get);
|
|
@@ -279,140 +281,33 @@ var mergeConfig = (userConfig = {}) => {
|
|
|
279
281
|
return { ...defaultConfig, ...userConfig, hostname, devmode };
|
|
280
282
|
};
|
|
281
283
|
|
|
282
|
-
// src/utils/create-modal.ts
|
|
283
|
-
var createDebugModal = (debugUrl, analyticsUrl) => {
|
|
284
|
-
if (!document.getElementById("onedollatstats-modal-styles")) {
|
|
285
|
-
const style = document.createElement("style");
|
|
286
|
-
style.id = "onedollatstats-modal-styles";
|
|
287
|
-
style.textContent = CSS;
|
|
288
|
-
document.head.appendChild(style);
|
|
289
|
-
}
|
|
290
|
-
const modal = document.createElement("div");
|
|
291
|
-
modal.className = "dev-modal";
|
|
292
|
-
modal.innerHTML = `
|
|
293
|
-
<button class="close-btn">×</button>
|
|
294
|
-
<p class="title">onedollarstats debug window</p>
|
|
295
|
-
<p>${icons.info}<span class="text">Tracking localhost as ${debugUrl}</span></p>
|
|
296
|
-
<div id="event-log" style="max-height: 100px; overflow-y: auto;"></div>
|
|
297
|
-
`;
|
|
298
|
-
document.body.appendChild(modal);
|
|
299
|
-
modal.querySelector(".close-btn")?.addEventListener("click", () => modal.remove(), { once: true });
|
|
300
|
-
if (analyticsUrl === defaultConfig.collectorUrl) {
|
|
301
|
-
const img = new Image(1, 1);
|
|
302
|
-
img.onerror = () => {
|
|
303
|
-
if (modal.querySelector("#ad-blocker-warning")) return;
|
|
304
|
-
const warning = document.createElement("p");
|
|
305
|
-
warning.id = "ad-blocker-warning";
|
|
306
|
-
warning.innerHTML = `${icons.warning}<span class="text">Health check failed - ad blocker might be interfering.</span>`;
|
|
307
|
-
modal.querySelector(".title")?.insertAdjacentElement("afterend", warning);
|
|
308
|
-
};
|
|
309
|
-
img.src = "https://collector.onedollarstats.com/pixel-health";
|
|
310
|
-
}
|
|
311
|
-
return (message, success) => {
|
|
312
|
-
const logContainer = modal.querySelector("#event-log");
|
|
313
|
-
if (!logContainer || modal.querySelector("#ad-blocker-warning")) return;
|
|
314
|
-
const entry = document.createElement("p");
|
|
315
|
-
entry.innerHTML = `${success ? icons.success : icons.error}<span class="text">${message}</span>`;
|
|
316
|
-
logContainer.appendChild(entry);
|
|
317
|
-
logContainer.scrollTop = logContainer.scrollHeight;
|
|
318
|
-
};
|
|
319
|
-
};
|
|
320
|
-
var icons = {
|
|
321
|
-
info: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="gray" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>`,
|
|
322
|
-
success: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="green" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m9 12 2 2 4-4"/></svg>`,
|
|
323
|
-
error: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="red" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>`,
|
|
324
|
-
warning: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="orange" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>`
|
|
325
|
-
};
|
|
326
|
-
var CSS = `
|
|
327
|
-
.dev-modal {
|
|
328
|
-
position: fixed;
|
|
329
|
-
bottom: 20px;
|
|
330
|
-
right: 20px;
|
|
331
|
-
background: #f6f6f7;
|
|
332
|
-
color: #21272F;
|
|
333
|
-
padding: 14px;
|
|
334
|
-
border-radius: 8px;
|
|
335
|
-
max-width: 340px;
|
|
336
|
-
max-height: 180px;
|
|
337
|
-
box-shadow: 0 5px 20px rgba(0,0,0,0.3);
|
|
338
|
-
font-family: sans-serif;
|
|
339
|
-
z-index: 99999;
|
|
340
|
-
animation: slideIn 0.3s ease-out;
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
.dev-modal .title {
|
|
344
|
-
text-transform: uppercase;
|
|
345
|
-
font-size: 11px;
|
|
346
|
-
font-weight: 500;
|
|
347
|
-
margin: 0 0 6px 0;
|
|
348
|
-
letter-spacing: 0.5px;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
.dev-modal p {
|
|
352
|
-
margin: 4px 0;
|
|
353
|
-
font-size: 14px;
|
|
354
|
-
display: flex;
|
|
355
|
-
align-items: flex-start;
|
|
356
|
-
gap: 4px;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
.dev-modal .text {
|
|
360
|
-
word-break: break-word;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
.dev-modal p svg {
|
|
364
|
-
flex-shrink: 0;
|
|
365
|
-
width: 18px;
|
|
366
|
-
height: 18px;
|
|
367
|
-
margin-top: 1px;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
.dev-modal .close-btn {
|
|
371
|
-
position: absolute;
|
|
372
|
-
top: 2px;
|
|
373
|
-
right: 8px;
|
|
374
|
-
background: transparent;
|
|
375
|
-
border: none;
|
|
376
|
-
cursor: pointer;
|
|
377
|
-
font-size: 14px;
|
|
378
|
-
font-weight: bold;
|
|
379
|
-
color: #333;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
@keyframes slideIn {
|
|
383
|
-
from {
|
|
384
|
-
opacity: 0;
|
|
385
|
-
transform: translateX(100%);
|
|
386
|
-
}
|
|
387
|
-
to {
|
|
388
|
-
opacity: 1;
|
|
389
|
-
transform: translateX(0);
|
|
390
|
-
}
|
|
391
|
-
}`;
|
|
392
|
-
|
|
393
284
|
// src/utils/parse-utm-params.ts
|
|
394
|
-
|
|
285
|
+
function parseUtmParams(urlSearchParams) {
|
|
395
286
|
const utm = {};
|
|
396
287
|
const keys = ["utm_campaign", "utm_source", "utm_medium", "utm_term", "utm_content"];
|
|
397
288
|
for (const key of keys) {
|
|
398
289
|
const raw = urlSearchParams.get(key);
|
|
399
290
|
if (!raw) continue;
|
|
400
|
-
const decoded =
|
|
291
|
+
const decoded = decodeAndTrim(raw);
|
|
401
292
|
if (decoded) {
|
|
402
293
|
utm[key] = decoded;
|
|
403
294
|
}
|
|
404
295
|
}
|
|
405
296
|
return utm;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
let
|
|
409
|
-
let
|
|
410
|
-
while (decoded !==
|
|
411
|
-
|
|
412
|
-
|
|
297
|
+
}
|
|
298
|
+
function decodeAndTrim(value) {
|
|
299
|
+
let decoded = value;
|
|
300
|
+
let previous = "";
|
|
301
|
+
while (decoded !== previous) {
|
|
302
|
+
previous = decoded;
|
|
303
|
+
try {
|
|
304
|
+
decoded = decodeURIComponent(decoded);
|
|
305
|
+
} catch {
|
|
306
|
+
return decoded.trim();
|
|
307
|
+
}
|
|
413
308
|
}
|
|
414
|
-
return
|
|
415
|
-
}
|
|
309
|
+
return decoded.trim();
|
|
310
|
+
}
|
|
416
311
|
|
|
417
312
|
// src/utils/props-parser.ts
|
|
418
313
|
var parseProps = (propsString) => {
|
|
@@ -432,7 +327,8 @@ var resolvePath = (pathOrProps) => {
|
|
|
432
327
|
if (pathOrProps) return pathOrProps;
|
|
433
328
|
const sources = [
|
|
434
329
|
{ value: document.body?.getAttribute("data-s-path"), name: "data-s-path" },
|
|
435
|
-
{ value: document.body?.getAttribute("data-s:path"), name: "data-s:path" }
|
|
330
|
+
{ value: document.body?.getAttribute("data-s:path"), name: "data-s:path" },
|
|
331
|
+
{ value: document.querySelector('meta[name="stonks-path"]')?.getAttribute("content"), name: "meta[stonks-path]" }
|
|
436
332
|
];
|
|
437
333
|
const existing = sources.filter(({ value }) => value);
|
|
438
334
|
if (existing.length > 1) {
|
|
@@ -457,17 +353,23 @@ var _AnalyticsTracker = class _AnalyticsTracker {
|
|
|
457
353
|
constructor(userConfig = {}) {
|
|
458
354
|
this.autocollectSetupDone = false;
|
|
459
355
|
this.lastPage = null;
|
|
460
|
-
this.modalLog = () => {
|
|
461
|
-
};
|
|
462
356
|
this.config = mergeConfig(userConfig);
|
|
463
357
|
if (!isClient()) return;
|
|
464
358
|
const { isLocalhost } = getEnvironment();
|
|
465
359
|
if (isLocalhost && this.config.devmode && this.config.hostname) {
|
|
466
360
|
console.log(`[onedollarstats]
|
|
467
361
|
OneDollarStats connected! Tracking localhost as ${this.config.hostname}`);
|
|
468
|
-
|
|
362
|
+
window.__stonksDebugConfig = { hostname: this.config.hostname, collectorUrl: this.config.collectorUrl };
|
|
363
|
+
window.__stonksModalQueue = [];
|
|
364
|
+
window.__stonksModalReady = false;
|
|
365
|
+
const debugScript = document.createElement("script");
|
|
366
|
+
debugScript.src = "https://assets.onedollarstats.com/stonks-debug.js";
|
|
367
|
+
debugScript.onerror = () => {
|
|
368
|
+
window.__stonksModalReady = true;
|
|
369
|
+
};
|
|
370
|
+
document.head.appendChild(debugScript);
|
|
469
371
|
}
|
|
470
|
-
|
|
372
|
+
this.setupAutocollect();
|
|
471
373
|
}
|
|
472
374
|
static getInstance(userConfig = {}) {
|
|
473
375
|
if (!isClient()) return new _AnalyticsTracker(userConfig);
|
|
@@ -543,7 +445,14 @@ UTM: ${data.utm}`;
|
|
|
543
445
|
const payloadBase64 = btoa(bin);
|
|
544
446
|
const safeGetThreshold = 1500;
|
|
545
447
|
const tryImageBeacon = payloadBase64.length <= safeGetThreshold;
|
|
546
|
-
const onComplete = (success) =>
|
|
448
|
+
const onComplete = (success) => {
|
|
449
|
+
const message = `${data.type} ${success ? "sent" : "failed to send"}`;
|
|
450
|
+
if (window.__stonksModalReady) {
|
|
451
|
+
window.__stonksModalLog?.(message, success);
|
|
452
|
+
} else {
|
|
453
|
+
window.__stonksModalQueue?.push([message, success]);
|
|
454
|
+
}
|
|
455
|
+
};
|
|
547
456
|
if (tryImageBeacon) {
|
|
548
457
|
const img = new Image(1, 1);
|
|
549
458
|
img.onload = () => onComplete(true);
|
|
@@ -555,17 +464,22 @@ UTM: ${data.utm}`;
|
|
|
555
464
|
trackPageView({ path, props }, checkBlock = false) {
|
|
556
465
|
if (!isClient()) return;
|
|
557
466
|
const viewPath = resolvePath(path);
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
467
|
+
const collectedProps = {};
|
|
468
|
+
const elements = document.querySelectorAll("[data-s\\:view-props], [data-s-view-props]");
|
|
469
|
+
for (const el of Array.from(elements)) {
|
|
470
|
+
const propsString = el.getAttribute("data-s-view-props") || el.getAttribute("data-s:view-props");
|
|
471
|
+
if (!propsString) continue;
|
|
472
|
+
const parsedProps = parseProps(propsString);
|
|
473
|
+
Object.assign(collectedProps, parsedProps);
|
|
474
|
+
}
|
|
475
|
+
const metaViewProps = document.querySelector('meta[name="stonks-props"]')?.getAttribute("content");
|
|
476
|
+
if (metaViewProps) {
|
|
477
|
+
Object.assign(collectedProps, parseProps(metaViewProps));
|
|
478
|
+
}
|
|
479
|
+
if (props) {
|
|
480
|
+
Object.assign(collectedProps, props);
|
|
481
|
+
}
|
|
482
|
+
const viewProps = Object.keys(collectedProps).length > 0 ? collectedProps : void 0;
|
|
569
483
|
if (!this.config.hashRouting && this.lastPage === viewPath) return;
|
|
570
484
|
if (checkBlock && !shouldTrackPath(viewPath, this.config)) return;
|
|
571
485
|
this.lastPage = viewPath;
|
|
@@ -618,7 +532,19 @@ UTM: ${data.utm}`;
|
|
|
618
532
|
setupAutocollect() {
|
|
619
533
|
if (!isClient() || this.autocollectSetupDone) return;
|
|
620
534
|
this.autocollectSetupDone = true;
|
|
621
|
-
const handlePageView = () =>
|
|
535
|
+
const handlePageView = () => {
|
|
536
|
+
const metaCollect = document.querySelector('meta[name="stonks-collect"]')?.getAttribute("content");
|
|
537
|
+
const bodyCollect = document.body?.getAttribute("data-s-collect") || document.body?.getAttribute("data-s:collect");
|
|
538
|
+
if (metaCollect === "false" || bodyCollect === "false") {
|
|
539
|
+
this.lastPage = null;
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
if (!this.config.autocollect && metaCollect !== "true" && bodyCollect !== "true") {
|
|
543
|
+
this.lastPage = null;
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
this.trackPageView({}, true);
|
|
547
|
+
};
|
|
622
548
|
const onVisibility = () => {
|
|
623
549
|
if (document.visibilityState === "visible") handlePageView();
|
|
624
550
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/utils/bot.ts", "../src/utils/environment.ts", "../src/utils/merge-config.ts", "../src/utils/
|
|
4
|
-
"sourcesContent": ["/**\n * Bot & crawler detection \u2014 standalone module.\n *\n * Usage:\n * import { detectBot } from '@aspect/fingerprint/bot'\n * const result = detectBot()\n * if (result.isBot) console.log(result.botKind, result.signals)\n *\n * Detects:\n * - Known search engine crawlers (Googlebot, Bingbot, Yandex, Baidu, etc.)\n * - Social media crawlers (Facebook, Twitter, LinkedIn, etc.)\n * - Headless browsers (Puppeteer, Playwright, Selenium, PhantomJS)\n * - General automation tools via navigator.webdriver and injected globals\n * - API tampering / lie detection (prototype spoofing, proxy wrapping)\n *\n * Zero dependencies \u2014 can be imported and used independently of the\n * fingerprinting library.\n */\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type BotKind =\n | 'search_engine' // Googlebot, Bingbot, Yandex, Baidu, DuckDuckBot, etc.\n | 'social_crawler' // Facebook, Twitter/X, LinkedIn, Slack, Discord, etc.\n | 'headless' // Headless Chrome/Firefox, PhantomJS\n | 'automation' // Selenium, Puppeteer, Playwright (non-headless)\n | 'library' // curl, wget, python-requests, node-fetch, etc.\n | 'unknown_bot' // Signals say bot, but can't classify further\n | 'human' // No bot signals detected\n\nexport interface BotSignals {\n /** navigator.userAgent matched a known bot pattern. */\n userAgentBot: string | null\n /** navigator.webdriver is true. */\n webdriver: boolean\n /** Headless browser indicators detected. */\n headless: boolean\n /** Automation globals found on window. */\n automationGlobals: string[]\n /** Number of API lies (toString/proxy tampering) detected. */\n liesDetected: number\n /** Proxy wrapping detected on native functions. */\n hasProxy: boolean\n /** navigator.languages is empty or missing. */\n missingLanguages: boolean\n /** navigator.plugins is empty (non-mobile). */\n missingPlugins: boolean\n}\n\nexport interface BotDetectionResult {\n /** True when any bot signal fires. */\n isBot: boolean\n /** Classified category of the detected bot. */\n botKind: BotKind\n /** Individual signal results for debugging / logging. */\n signals: BotSignals\n}\n\n// ---------------------------------------------------------------------------\n// Known bot UA patterns\n// ---------------------------------------------------------------------------\n\ninterface BotPattern {\n pattern: RegExp\n kind: BotKind\n name: string\n}\n\nconst BOT_PATTERNS: BotPattern[] = [\n // Search engines\n { pattern: /Googlebot/i, kind: 'search_engine', name: 'Googlebot' },\n { pattern: /Google-InspectionTool/i, kind: 'search_engine', name: 'Googlebot' },\n { pattern: /Storebot-Google/i, kind: 'search_engine', name: 'Googlebot' },\n { pattern: /AdsBot-Google/i, kind: 'search_engine', name: 'Google Ads' },\n { pattern: /Mediapartners-Google/i, kind: 'search_engine', name: 'Google Adsense' },\n { pattern: /bingbot/i, kind: 'search_engine', name: 'Bingbot' },\n { pattern: /msnbot/i, kind: 'search_engine', name: 'MSNBot' },\n { pattern: /YandexBot/i, kind: 'search_engine', name: 'YandexBot' },\n { pattern: /YandexAccessibilityBot/i, kind: 'search_engine', name: 'YandexBot' },\n { pattern: /Baiduspider/i, kind: 'search_engine', name: 'Baidu' },\n { pattern: /DuckDuckBot/i, kind: 'search_engine', name: 'DuckDuckBot' },\n { pattern: /Sogou/i, kind: 'search_engine', name: 'Sogou' },\n { pattern: /Exabot/i, kind: 'search_engine', name: 'Exabot' },\n { pattern: /ia_archiver/i, kind: 'search_engine', name: 'Alexa' },\n { pattern: /SemrushBot/i, kind: 'search_engine', name: 'SemrushBot' },\n { pattern: /AhrefsBot/i, kind: 'search_engine', name: 'AhrefsBot' },\n { pattern: /MJ12bot/i, kind: 'search_engine', name: 'MJ12bot' },\n { pattern: /DotBot/i, kind: 'search_engine', name: 'DotBot' },\n { pattern: /PetalBot/i, kind: 'search_engine', name: 'PetalBot' },\n { pattern: /Applebot/i, kind: 'search_engine', name: 'Applebot' },\n { pattern: /GPTBot/i, kind: 'search_engine', name: 'GPTBot' },\n { pattern: /ChatGPT-User/i, kind: 'search_engine', name: 'ChatGPT' },\n { pattern: /ClaudeBot/i, kind: 'search_engine', name: 'ClaudeBot' },\n { pattern: /CCBot/i, kind: 'search_engine', name: 'Common Crawl' },\n { pattern: /anthropic-ai/i, kind: 'search_engine', name: 'Anthropic' },\n { pattern: /PerplexityBot/i, kind: 'search_engine', name: 'PerplexityBot' },\n\n // Social crawlers\n { pattern: /facebookexternalhit/i, kind: 'social_crawler', name: 'Facebook' },\n { pattern: /Facebot/i, kind: 'social_crawler', name: 'Facebook' },\n { pattern: /Twitterbot/i, kind: 'social_crawler', name: 'Twitter' },\n { pattern: /LinkedInBot/i, kind: 'social_crawler', name: 'LinkedIn' },\n { pattern: /Slackbot/i, kind: 'social_crawler', name: 'Slack' },\n { pattern: /Discordbot/i, kind: 'social_crawler', name: 'Discord' },\n { pattern: /TelegramBot/i, kind: 'social_crawler', name: 'Telegram' },\n { pattern: /WhatsApp/i, kind: 'social_crawler', name: 'WhatsApp' },\n { pattern: /Pinterestbot/i, kind: 'social_crawler', name: 'Pinterest' },\n { pattern: /Snapchat/i, kind: 'social_crawler', name: 'Snapchat' },\n\n // Headless / automation\n { pattern: /HeadlessChrome/i, kind: 'headless', name: 'Headless Chrome' },\n { pattern: /PhantomJS/i, kind: 'headless', name: 'PhantomJS' },\n { pattern: /Selenium/i, kind: 'automation', name: 'Selenium' },\n { pattern: /Puppeteer/i, kind: 'automation', name: 'Puppeteer' },\n\n // HTTP libraries\n { pattern: /curl\\//i, kind: 'library', name: 'curl' },\n { pattern: /Wget\\//i, kind: 'library', name: 'Wget' },\n { pattern: /python-requests/i, kind: 'library', name: 'Python Requests' },\n { pattern: /python-urllib/i, kind: 'library', name: 'Python urllib' },\n { pattern: /node-fetch/i, kind: 'library', name: 'node-fetch' },\n { pattern: /axios\\//i, kind: 'library', name: 'Axios' },\n { pattern: /Go-http-client/i, kind: 'library', name: 'Go HTTP' },\n { pattern: /Java\\//i, kind: 'library', name: 'Java HTTP' },\n { pattern: /libwww-perl/i, kind: 'library', name: 'Perl LWP' },\n { pattern: /Apache-HttpClient/i, kind: 'library', name: 'Apache HttpClient' },\n { pattern: /okhttp/i, kind: 'library', name: 'OkHttp' },\n { pattern: /Scrapy/i, kind: 'library', name: 'Scrapy' },\n\n // Generic catch-all (must be last)\n { pattern: /bot|crawl|spider|slurp|fetch|archiver/i, kind: 'unknown_bot', name: 'generic' },\n]\n\n// ---------------------------------------------------------------------------\n// Automation globals injected by common frameworks\n// ---------------------------------------------------------------------------\n\nconst AUTOMATION_GLOBALS = [\n // Selenium\n '__selenium_unwrapped',\n '__selenium_evaluate',\n '__webdriver_evaluate',\n '__webdriver_script_fn',\n '__webdriver_script_func',\n '__webdriver_script_function',\n '__fxdriver_evaluate',\n '__fxdriver_unwrapped',\n '_Selenium_IDE_Recorder',\n // Puppeteer / CDP\n '__puppeteer_evaluation_script__',\n // PhantomJS\n 'callPhantom',\n '_phantom',\n 'phantom',\n // Nightmare.js\n '__nightmare',\n // Playwright (injects page.exposeFunction bindings)\n '__playwright',\n '__pw_manual',\n // CasperJS\n '__casper',\n // TestCafe\n '__testcafe',\n // WebDriver (generic)\n 'webdriver',\n 'domAutomation',\n 'domAutomationController',\n] as const\n\n// ---------------------------------------------------------------------------\n// Core detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detect whether the current browser context is a bot or crawler.\n * Synchronous and lightweight \u2014 no async APIs needed.\n */\nexport function detectBot(): BotDetectionResult {\n const signals = collectBotSignals()\n\n const isBot =\n signals.userAgentBot !== null ||\n signals.webdriver ||\n signals.headless ||\n signals.automationGlobals.length > 0 ||\n signals.liesDetected > 2 ||\n (signals.liesDetected > 0 && signals.hasProxy)\n\n let botKind: BotKind = 'human'\n if (isBot) {\n if (signals.userAgentBot !== null) {\n // Find the matching pattern to determine kind\n const ua = navigator.userAgent || ''\n const match = BOT_PATTERNS.find(p => p.pattern.test(ua))\n botKind = match?.kind ?? 'unknown_bot'\n } else if (signals.headless) {\n botKind = 'headless'\n } else if (signals.webdriver || signals.automationGlobals.length > 0) {\n botKind = 'automation'\n } else {\n botKind = 'unknown_bot'\n }\n }\n\n return { isBot, botKind, signals }\n}\n\n// ---------------------------------------------------------------------------\n// Signal collectors\n// ---------------------------------------------------------------------------\n\nfunction collectBotSignals(): BotSignals {\n return {\n userAgentBot: detectUserAgentBot(),\n webdriver: detectWebdriver(),\n headless: detectHeadless(),\n automationGlobals: detectAutomationGlobals(),\n ...detectLies(),\n missingLanguages: detectMissingLanguages(),\n missingPlugins: detectMissingPlugins(),\n }\n}\n\n/** Check UA string against known bot patterns. */\nfunction detectUserAgentBot(): string | null {\n const ua = navigator.userAgent || ''\n if (!ua) return 'empty-ua'\n for (const { pattern, name } of BOT_PATTERNS) {\n if (pattern.test(ua)) return name\n }\n return null\n}\n\n/** navigator.webdriver is set by WebDriver-based automation. */\nfunction detectWebdriver(): boolean {\n return !!(navigator as any).webdriver\n}\n\n/** Headless browser indicators. */\nfunction detectHeadless(): boolean {\n const w = window as any\n const n = navigator as any\n\n // Chrome-specific: headless mode omits the chrome runtime object\n if (/Chrome/.test(n.userAgent) && !w.chrome) return true\n\n // HeadlessChrome in UA\n if (/HeadlessChrome/.test(n.userAgent)) return true\n\n // Notification permission is always \"denied\" in headless Chrome\n // (in headed mode it defaults to \"default\")\n try {\n if (Notification.permission === 'denied' && n.permissions) {\n // Double-check: headless also lacks plugins\n if ((!n.plugins || n.plugins.length === 0) && !/Mobile|Android/i.test(n.userAgent)) {\n return true\n }\n }\n } catch { /* permissions API unavailable */ }\n\n return false\n}\n\n/** Detect automation framework globals on window. */\nfunction detectAutomationGlobals(): string[] {\n const w = window as any\n return AUTOMATION_GLOBALS.filter(key => {\n try { return key in w && w[key] !== undefined }\n catch { return false }\n }) as string[]\n}\n\n/**\n * Lie detection \u2014 detect API tampering (proxies, toString spoofing).\n * Extracted from the fingerprint lies module; self-contained here.\n */\nfunction detectLies(): { liesDetected: number; hasProxy: boolean } {\n let liesDetected = 0\n let hasProxy = false\n\n const apisToTest: Array<[string, () => unknown]> = [\n ['Navigator.prototype.userAgent', () => desc(Navigator.prototype, 'userAgent')],\n ['Navigator.prototype.languages', () => desc(Navigator.prototype, 'languages')],\n ['Navigator.prototype.platform', () => desc(Navigator.prototype, 'platform')],\n ['Navigator.prototype.hardwareConcurrency', () => desc(Navigator.prototype, 'hardwareConcurrency')],\n ['Navigator.prototype.webdriver', () => desc(Navigator.prototype, 'webdriver')],\n ['HTMLCanvasElement.prototype.toDataURL', () => HTMLCanvasElement.prototype.toDataURL],\n ['CanvasRenderingContext2D.prototype.fillText', () => CanvasRenderingContext2D.prototype.fillText],\n ['Date.prototype.getTimezoneOffset', () => Date.prototype.getTimezoneOffset],\n ]\n\n for (const [name, accessor] of apisToTest) {\n try {\n const val = accessor()\n if (val === undefined || val === null) continue\n\n // toString format check\n if (typeof val === 'function') {\n const str = Function.prototype.toString.call(val)\n if (!isNativeToString(str)) liesDetected++\n }\n\n // Getter integrity check\n if (name.includes('.prototype.') && typeof val !== 'function') {\n const parts = name.split('.')\n const proto = safeProto(parts[0])\n const prop = parts[parts.length - 1]\n if (proto) {\n const d = Object.getOwnPropertyDescriptor(proto, prop)\n if (d?.get) {\n const gs = Function.prototype.toString.call(d.get)\n if (!isNativeToString(gs)) liesDetected++\n }\n }\n }\n\n // Proxy detection\n if (typeof val === 'function') {\n if (val.toString !== Function.prototype.toString) {\n try {\n const native = Function.prototype.toString.call(val)\n const custom = val.toString()\n if (native !== custom) { liesDetected++; hasProxy = true }\n } catch { liesDetected++; hasProxy = true }\n }\n }\n } catch { /* skip inaccessible */ }\n }\n\n // Meta-test: toString integrity\n try {\n const s = Function.prototype.toString.call(Function.prototype.toString)\n if (!isNativeToString(s)) liesDetected++\n } catch { /* skip */ }\n\n return { liesDetected, hasProxy }\n}\n\n/** Missing navigator.languages is a strong headless indicator. */\nfunction detectMissingLanguages(): boolean {\n const langs = navigator.languages\n return !langs || langs.length === 0\n}\n\n/** Missing plugins on desktop is a headless indicator. */\nfunction detectMissingPlugins(): boolean {\n if (/Mobile|Android/i.test(navigator.userAgent)) return false\n return !navigator.plugins || navigator.plugins.length === 0\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isNativeToString(str: string): boolean {\n return /^function\\s[^{]*\\{\\s*\\[native code\\]\\s*\\}$/.test(str) ||\n str === 'function () { [native code] }' ||\n /^\\(\\)\\s*=>\\s*\\{\\s*\\[native code\\]\\s*\\}$/.test(str)\n}\n\nfunction desc(proto: object, prop: string) {\n return Object.getOwnPropertyDescriptor(proto, prop)\n}\n\nfunction safeProto(name: string): object | null {\n try { return (window as any)[name]?.prototype ?? null }\n catch { return null }\n}\n", "export const getEnvironment = (): {\n isLocalhost: boolean;\n isHeadlessBrowser: boolean;\n} => ({\n isLocalhost:\n (/^localhost$|^127(\\.[0-9]+){0,2}\\.[0-9]+$|^\\[::1?\\]$/.test(location.hostname) &&\n (location.protocol === \"http:\" || location.protocol === \"https:\")) ||\n location.protocol === \"file:\",\n isHeadlessBrowser: Boolean(\n window.navigator.webdriver ||\n (\"_phantom\" in window && window._phantom) ||\n (\"__nightmare\" in window && window.__nightmare) ||\n (\"Cypress\" in window && window.Cypress)\n )\n});\nexport const isClient = (): boolean => {\n try {\n // Basic checks for window and document\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return false;\n\n // Check for navigator safely\n const ua = typeof navigator !== \"undefined\" ? navigator.userAgent : \"\";\n if (/node|jsdom/i.test(ua)) return false;\n return true;\n } catch {\n return false;\n }\n};\n", "import type { AnalyticsConfig, InternalAnalyticsConfig } from \"../types\";\nimport { getEnvironment } from \"./environment\";\n\nexport const defaultConfig: InternalAnalyticsConfig = {\n hostname: null,\n devmode: false,\n collectorUrl: \"https://collector.onedollarstats.com/events\",\n hashRouting: false,\n autocollect: true,\n excludePages: [],\n includePages: []\n};\n\nexport const mergeConfig = (userConfig: AnalyticsConfig = {}): InternalAnalyticsConfig => {\n const { isLocalhost } = getEnvironment();\n\n const devmode = !isLocalhost ? false : (userConfig.devmode ?? !!userConfig.trackLocalhostAs);\n\n let hostname: string | null;\n if (userConfig.hostname) {\n const trimmed = userConfig.hostname.trim();\n hostname = trimmed || null;\n } else if (devmode && userConfig.trackLocalhostAs) {\n hostname = userConfig.trackLocalhostAs;\n } else {\n hostname = null;\n }\n\n // Merge default config, user config, and computed values\n return { ...defaultConfig, ...userConfig, hostname, devmode };\n};\n", "import { defaultConfig } from \"./merge-config\";\n\nexport const createDebugModal = (debugUrl: string, analyticsUrl: string) => {\n if (!document.getElementById(\"onedollatstats-modal-styles\")) {\n const style = document.createElement(\"style\");\n style.id = \"onedollatstats-modal-styles\";\n style.textContent = CSS;\n document.head.appendChild(style);\n }\n\n // Create modal\n const modal = document.createElement(\"div\");\n modal.className = \"dev-modal\";\n modal.innerHTML = `\n <button class=\"close-btn\">×</button>\n <p class=\"title\">onedollarstats debug window</p>\n <p>${icons.info}<span class=\"text\">Tracking localhost as ${debugUrl}</span></p>\n <div id=\"event-log\" style=\"max-height: 100px; overflow-y: auto;\"></div>\n `;\n document.body.appendChild(modal);\n\n // Close handler\n modal.querySelector(\".close-btn\")?.addEventListener(\"click\", () => modal.remove(), { once: true });\n\n // Health check\n if (analyticsUrl === defaultConfig.collectorUrl) {\n const img = new Image(1, 1);\n img.onerror = () => {\n if (modal.querySelector(\"#ad-blocker-warning\")) return;\n const warning = document.createElement(\"p\");\n warning.id = \"ad-blocker-warning\";\n warning.innerHTML = `${icons.warning}<span class=\"text\">Health check failed - ad blocker might be interfering.</span>`;\n modal.querySelector(\".title\")?.insertAdjacentElement(\"afterend\", warning);\n };\n img.src = \"https://collector.onedollarstats.com/pixel-health\";\n }\n\n // Log rendering function\n return (message: string, success: boolean): void => {\n const logContainer = modal.querySelector(\"#event-log\");\n if (!logContainer || modal.querySelector(\"#ad-blocker-warning\")) return;\n\n const entry = document.createElement(\"p\");\n entry.innerHTML = `${success ? icons.success : icons.error}<span class=\"text\">${message}</span>`;\n logContainer.appendChild(entry);\n logContainer.scrollTop = logContainer.scrollHeight;\n };\n};\n\nconst icons = {\n info: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"gray\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 16v-4\"/><path d=\"M12 8h.01\"/></svg>`,\n success: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"green\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m9 12 2 2 4-4\"/></svg>`,\n error: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"red\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"m15 9-6 6\"/><path d=\"m9 9 6 6\"/></svg>`,\n warning: `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"orange\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg>`\n} as const;\n\nconst CSS = `\n .dev-modal {\n position: fixed;\n bottom: 20px;\n right: 20px;\n background: #f6f6f7;\n color: #21272F;\n padding: 14px;\n border-radius: 8px;\n max-width: 340px;\n max-height: 180px;\n box-shadow: 0 5px 20px rgba(0,0,0,0.3);\n font-family: sans-serif;\n z-index: 99999;\n animation: slideIn 0.3s ease-out;\n}\n\n.dev-modal .title {\n text-transform: uppercase;\n font-size: 11px;\n font-weight: 500;\n margin: 0 0 6px 0;\n letter-spacing: 0.5px;\n}\n\n.dev-modal p {\n margin: 4px 0;\n font-size: 14px;\n display: flex;\n align-items: flex-start;\n gap: 4px;\n}\n\n.dev-modal .text {\n word-break: break-word;\n}\n\n.dev-modal p svg {\n flex-shrink: 0;\n width: 18px;\n height: 18px;\n margin-top: 1px;\n}\n\n.dev-modal .close-btn {\n position: absolute;\n top: 2px;\n right: 8px;\n background: transparent;\n border: none;\n cursor: pointer;\n font-size: 14px;\n font-weight: bold;\n color: #333;\n}\n\n@keyframes slideIn {\n from {\n opacity: 0;\n transform: translateX(100%);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n}`;\n", "export const parseUtmParams = (urlSearchParams: URLSearchParams) => {\n const utm: Record<string, string> = {};\n const keys = [\"utm_campaign\", \"utm_source\", \"utm_medium\", \"utm_term\", \"utm_content\"] as const;\n\n for (const key of keys) {\n const raw = urlSearchParams.get(key);\n if (!raw) continue;\n\n const decoded = recursiveDecode(raw).trim();\n if (decoded) {\n utm[key] = decoded;\n }\n }\n\n return utm;\n};\n\nconst recursiveDecode = (value: string): string => {\n let current = value;\n let decoded = decodeURIComponent(current);\n\n while (decoded !== current) {\n current = decoded;\n decoded = decodeURIComponent(current);\n }\n\n return current;\n};\n", "export const parseProps = (propsString: string): Record<string, string> | undefined => {\n if (!propsString) return undefined;\n // \"key1=value1;key2=value2\"\n\n const splittedProps = propsString.split(\";\");\n const propsObj: Record<string, string> = {};\n\n for (const keyValueString of splittedProps) {\n const keyValuePair = keyValueString.split(\"=\").map((el) => el.trim());\n if (keyValuePair.length !== 2 || keyValuePair[0] === \"\" || keyValuePair[1] === \"\") continue;\n // @ts-ignore\n propsObj[keyValuePair[0]] = keyValuePair[1];\n }\n\n return Object.keys(propsObj).length === 0 ? undefined : propsObj;\n};\n", "export const resolvePath = (pathOrProps?: string): string => {\n if (pathOrProps) return pathOrProps;\n\n const sources = [\n { value: document.body?.getAttribute(\"data-s-path\"), name: \"data-s-path\" },\n { value: document.body?.getAttribute(\"data-s:path\"), name: \"data-s:path\" }\n ];\n\n // Only keep sources that actually exist\n const existing = sources.filter(({ value }) => value);\n\n if (existing.length > 1) {\n console.warn(\"[onedollarstats] Multiple path sources found. Using priority order:\", existing.map(({ name }) => name).join(\" > \"));\n }\n\n // Return first available value, fallback to location.pathname\n return existing[0]?.value ?? location.pathname;\n};\n", "import type { InternalAnalyticsConfig } from \"../types\";\n\nconst matchesPattern = (path: string, pattern: string): boolean => {\n // Escape special regex characters except '*' which becomes '.*'\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\");\n return new RegExp(`^${escaped}$`).test(path);\n};\n\nexport const shouldTrackPath = (path: string, config: InternalAnalyticsConfig): boolean => {\n // Exclude pages first\n if (config.excludePages.some((pattern) => matchesPattern(path, pattern))) return false;\n // If includePages is defined, only allow matching paths\n if (config.includePages.length && !config.includePages.some((pattern) => matchesPattern(path, pattern))) return false;\n return true;\n};\n", "import type { AnalyticsConfig, BaseProps, BodyToSend, Event, InternalAnalyticsConfig, ViewArguments } from \"./types\";\nimport { detectBot } from \"./utils/bot\";\nimport { createDebugModal } from \"./utils/create-modal\";\nimport { getEnvironment, isClient } from \"./utils/environment\";\nimport { mergeConfig } from \"./utils/merge-config\";\nimport { parseUtmParams } from \"./utils/parse-utm-params\";\nimport { parseProps } from \"./utils/props-parser\";\nimport { resolvePath } from \"./utils/resolve-path\";\nimport { shouldTrackPath } from \"./utils/should-track\";\n\nclass AnalyticsTracker {\n private static instance: AnalyticsTracker | null = null;\n\n private autocollectSetupDone = false;\n private config: InternalAnalyticsConfig;\n private lastPage: string | null = null;\n private modalLog: (message: string, success: boolean) => void = () => {};\n\n public static getInstance(userConfig: AnalyticsConfig = {}): AnalyticsTracker {\n // Fresh no-op instance for SSR\n if (!isClient()) return new AnalyticsTracker(userConfig);\n\n if (!AnalyticsTracker.instance) {\n AnalyticsTracker.instance = new AnalyticsTracker(userConfig);\n }\n return AnalyticsTracker.instance;\n }\n\n private constructor(userConfig: AnalyticsConfig = {}) {\n this.config = mergeConfig(userConfig);\n\n // Skip setup in non-client environments\n if (!isClient()) return;\n\n const { isLocalhost } = getEnvironment();\n\n // Debug log on localhost\n if (isLocalhost && this.config.devmode && this.config.hostname) {\n console.log(`[onedollarstats]\\nOneDollarStats connected! Tracking localhost as ${this.config.hostname}`);\n\n this.modalLog = createDebugModal(this.config.hostname, this.config.collectorUrl);\n }\n\n // Auto-start autocollect\n if (this.config.autocollect) this.setupAutocollect();\n }\n\n private async sendWithBeaconOrFetch(stringifiedBody: string, callback: (success: boolean) => void): Promise<void> {\n // First fallback: try sendBeacon\n if (navigator.sendBeacon?.(this.config.collectorUrl, stringifiedBody)) {\n callback(true);\n return;\n }\n\n // Second fallback: use fetch() with keepalive\n fetch(this.config.collectorUrl, {\n method: \"POST\",\n body: stringifiedBody,\n headers: { \"Content-Type\": \"application/json\" },\n keepalive: true\n })\n .then(({ ok }) => callback(ok))\n .catch((err: Error) => {\n console.error(\"[onedollarstats] fetch() failed:\", err.message);\n callback(false);\n });\n }\n\n // Handles localhost replacement, referrer, UTM parameters, and debug mode.\n // Uses img beacon then `navigator.sendBeacon` if available, otherwise falls back to `fetch`.\n private async send(data: Event): Promise<void> {\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.devmode) || isHeadlessBrowser) return;\n\n const { isBot, botKind } = detectBot();\n\n if (isBot && botKind !== \"human\") return;\n\n const urlToSend = new URL(this.config.hostname ? `https://${this.config.hostname}${location.pathname}` : location.href);\n\n // Clean query string unless UTM is explicitly provided\n urlToSend.search = \"\";\n if (data.path) urlToSend.pathname = data.path;\n\n const cleanUrl = urlToSend.href.replace(/\\/$/, \"\");\n\n // Determine referrer\n let referrer: string | undefined = data.referrer;\n try {\n if (!referrer && document.referrer && document.referrer !== \"null\") {\n const referrerURL = new URL(document.referrer);\n if (referrerURL.hostname !== urlToSend.hostname) referrer = referrerURL.href;\n }\n } catch {} // ignore malformed referrer\n\n // Build request body\n const body: BodyToSend = {\n u: cleanUrl,\n e: [\n {\n t: data.type,\n h: this.config.hashRouting,\n r: referrer,\n p: data.props\n }\n ],\n debug: this.config.devmode\n };\n\n if (data.utm && Object.keys(data.utm).length > 0) body.qs = data.utm;\n\n if (body.debug) {\n let logMessage = `[onedollarstats]\\nEvent name: ${data.type}\\nEvent collected from: ${cleanUrl}`;\n if (data.props && Object.keys(data.props).length > 0) logMessage += `\\nProps: ${JSON.stringify(data.props, null, 2)}`;\n if (referrer) logMessage += `\\nReferrer: ${referrer}`;\n if (this.config.hashRouting) logMessage += `\\nHashRouting: ${this.config.hashRouting}`;\n if (data.utm && Object.keys(data.utm).length > 0) logMessage += `\\nUTM: ${data.utm}`;\n\n console.log(logMessage);\n }\n\n // Prepare the event payload\n const stringifiedBody = JSON.stringify(body);\n\n // Encode for safe inclusion in a query string (UTF-8 \u2192 Base64)\n const bytes = new TextEncoder().encode(stringifiedBody); // UTF-8 \u2192 bytes\n const bin = String.fromCharCode(...bytes); // bytes \u2192 binary string\n const payloadBase64 = btoa(bin); // binary \u2192 Base64\n\n const safeGetThreshold = 1500; // limit for query-string-containing URLs\n const tryImageBeacon = payloadBase64.length <= safeGetThreshold;\n\n const onComplete = (success: boolean) => this.modalLog(`${data.type} ${success ? \"sent\" : \"failed to send\"}`, success);\n\n if (tryImageBeacon) {\n // Send via image beacon\n const img = new Image(1, 1);\n\n img.onload = () => onComplete(true);\n // If loading image fails (server unavailable, blocked, etc.)\n img.onerror = () => this.sendWithBeaconOrFetch(stringifiedBody, onComplete);\n\n // Primary attempt: send data via image beacon (GET request with query string)\n img.src = `${this.config.collectorUrl}?data=${payloadBase64}`;\n } else await this.sendWithBeaconOrFetch(stringifiedBody, onComplete);\n }\n\n // Prevents duplicate pageviews and respects include/exclude page rules. Automatically parses UTM parameters from URL.\n private trackPageView({ path, props }: ViewArguments, checkBlock: boolean = false) {\n if (!isClient()) return;\n\n const viewPath = resolvePath(path);\n\n const viewProps =\n props ||\n (() => {\n const newProps = {};\n const elements = document.querySelectorAll(\"[data-s\\\\:view-props], [data-s-view-props]\");\n\n for (const el of Array.from(elements)) {\n const propsString = el.getAttribute(\"data-s-view-props\") || el.getAttribute(\"data-s:view-props\");\n if (!propsString) continue;\n const parsedProps = parseProps(propsString);\n Object.assign(newProps, parsedProps);\n }\n\n return Object.keys(newProps).length ? newProps : undefined;\n })();\n\n // Skip duplicate pageviews or excluded pages\n if (!this.config.hashRouting && this.lastPage === viewPath) return;\n\n // Skip page if checkBlock is true and the path should be excluded\n if (checkBlock && !shouldTrackPath(viewPath, this.config)) return;\n\n this.lastPage = viewPath;\n\n const utm = parseUtmParams(new URLSearchParams(location.search));\n this.send({ type: \"PageView\", path: viewPath, props: viewProps, utm });\n }\n\n /**\n * Tracks a custom event.\n * Can accept path string or a props object.\n *\n * @param eventName Name of the event to track.\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props object if path string is provided.\n */\n public async event(eventName: string, pathOrProps?: string | BaseProps, rawProps?: BaseProps) {\n if (!isClient()) return;\n\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.devmode) || isHeadlessBrowser) return;\n\n const path = resolvePath(typeof pathOrProps === \"string\" ? pathOrProps : undefined);\n const props = typeof pathOrProps === \"object\" ? pathOrProps : rawProps;\n\n this.send({ type: eventName, path, props });\n }\n\n /**\n * Records a page view.\n * Can accept path string or a props object.\n *\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props when first arg is a path string.\n */\n public async view(pathOrProps?: string | BaseProps, props?: BaseProps) {\n if (!isClient()) return;\n\n const args: ViewArguments = {};\n\n if (typeof pathOrProps === \"string\") {\n args.path = pathOrProps;\n args.props = props;\n } else if (typeof pathOrProps === \"object\") {\n args.props = pathOrProps;\n }\n\n this.trackPageView(args);\n }\n\n /**\n * Installs global DOM/window listeners exactly once for:\n * - visibilitychange\n * - history.pushState\n * - popstate\n * - hashchange\n * - click autocapture for elements annotated with `data-s:event` & `data-s-event`\n *\n */\n private setupAutocollect() {\n if (!isClient() || this.autocollectSetupDone) return;\n this.autocollectSetupDone = true;\n\n const handlePageView = () => this.trackPageView({}, true);\n\n // visibilitychange\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") handlePageView();\n };\n document.addEventListener(\"visibilitychange\", onVisibility);\n\n // pushState\n const origPush = history.pushState.bind(history);\n history.pushState = (...args) => {\n origPush(...args);\n requestAnimationFrame(() => {\n handlePageView();\n });\n };\n\n // popstate\n window.addEventListener(\"popstate\", handlePageView);\n\n // hashchange\n window.addEventListener(\"hashchange\", handlePageView);\n\n // click autocapture\n const onClick: EventListener = (ev: Event) => {\n const clickEvent = ev as MouseEvent;\n if (clickEvent.type === \"auxclick\" && clickEvent.button !== 1) return;\n\n const target = clickEvent.target as Element | null;\n if (!target) return;\n\n // Check if inside <a> or <button>\n const insideInteractive = !!target.closest(\"a, button\");\n\n let el: Element | null = target;\n let depth = 0;\n\n while (el) {\n const eventName = el.getAttribute(\"data-s-event\") || el.getAttribute(\"data-s:event\");\n if (eventName) {\n const propsAttr = el.getAttribute(\"data-s-event-props\") || el.getAttribute(\"data-s:event-props\");\n const props = propsAttr ? parseProps(propsAttr) : undefined;\n const path = el.getAttribute(\"data-s-event-path\") || el.getAttribute(\"data-s:event-path\") || undefined;\n\n if ((path && !shouldTrackPath(path, this.config)) || !shouldTrackPath(location.pathname, this.config)) {\n return;\n }\n\n this.event(eventName, path ?? props, props);\n return;\n }\n\n el = el.parentElement;\n depth++;\n\n // If not in <a>/<button>, stop after 3 levels\n if (!insideInteractive && depth >= 3) break;\n }\n };\n\n document.addEventListener(\"click\", onClick);\n\n // Fire initial pageview if already visible\n if (document.visibilityState === \"visible\") handlePageView();\n }\n}\n\nexport const configure = (userConfig: AnalyticsConfig = {}) => {\n AnalyticsTracker.getInstance(userConfig);\n};\n\nexport const event = async (eventName: string, pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.event(eventName, pathOrProps, props);\n};\n\nexport const view = async (pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.view(pathOrProps, props);\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../src/utils/bot.ts", "../src/utils/environment.ts", "../src/utils/merge-config.ts", "../src/utils/parse-utm-params.ts", "../src/utils/props-parser.ts", "../src/utils/resolve-path.ts", "../src/utils/should-track.ts", "../src/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Bot & crawler detection module.\n *\n * Detects:\n * - Known search engine crawlers (Googlebot, Bingbot, Yandex, Baidu, etc.)\n * - Social media crawlers (Facebook, Twitter, LinkedIn, etc.)\n * - Headless browsers (Puppeteer, Playwright, Selenium, PhantomJS)\n * - General automation tools via navigator.webdriver and injected globals\n * - API tampering / lie detection (prototype spoofing, proxy wrapping)\n */\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type BotKind =\n | 'search_engine' // Googlebot, Bingbot, Yandex, Baidu, DuckDuckBot, etc.\n | 'social_crawler' // Facebook, Twitter/X, LinkedIn, Slack, Discord, etc.\n | 'headless' // Headless Chrome/Firefox, PhantomJS\n | 'automation' // Selenium, Puppeteer, Playwright (non-headless)\n | 'library' // curl, wget, python-requests, node-fetch, etc.\n | 'unknown_bot' // Signals say bot, but can't classify further\n | 'human' // No bot signals detected\n\nexport interface BotSignals {\n /** navigator.userAgent matched a known bot pattern. */\n userAgentBot: string | null\n /** navigator.webdriver is true. */\n webdriver: boolean\n /** Headless browser indicators detected. */\n headless: boolean\n /** Automation globals found on window. */\n automationGlobals: string[]\n /** Number of API lies (toString/proxy tampering) detected. */\n liesDetected: number\n /** Proxy wrapping detected on native functions. */\n hasProxy: boolean\n /** navigator.languages is empty or missing. */\n missingLanguages: boolean\n /** navigator.plugins is empty (non-mobile). */\n missingPlugins: boolean\n}\n\nexport interface BotDetectionResult {\n /** True when any bot signal fires. */\n isBot: boolean\n /** Classified category of the detected bot. */\n botKind: BotKind\n /** Individual signal results for debugging / logging. */\n signals: BotSignals\n}\n\n// ---------------------------------------------------------------------------\n// Known bot UA patterns\n// ---------------------------------------------------------------------------\n\ninterface BotPattern {\n pattern: RegExp\n kind: BotKind\n name: string\n}\n\nconst BOT_PATTERNS: BotPattern[] = [\n // Search engines\n { pattern: /Googlebot/i, kind: 'search_engine', name: 'Googlebot' },\n { pattern: /Google-InspectionTool/i, kind: 'search_engine', name: 'Googlebot' },\n { pattern: /Storebot-Google/i, kind: 'search_engine', name: 'Googlebot' },\n { pattern: /AdsBot-Google/i, kind: 'search_engine', name: 'Google Ads' },\n { pattern: /Mediapartners-Google/i, kind: 'search_engine', name: 'Google Adsense' },\n { pattern: /bingbot/i, kind: 'search_engine', name: 'Bingbot' },\n { pattern: /msnbot/i, kind: 'search_engine', name: 'MSNBot' },\n { pattern: /YandexBot/i, kind: 'search_engine', name: 'YandexBot' },\n { pattern: /YandexAccessibilityBot/i, kind: 'search_engine', name: 'YandexBot' },\n { pattern: /Baiduspider/i, kind: 'search_engine', name: 'Baidu' },\n { pattern: /DuckDuckBot/i, kind: 'search_engine', name: 'DuckDuckBot' },\n { pattern: /Sogou/i, kind: 'search_engine', name: 'Sogou' },\n { pattern: /Exabot/i, kind: 'search_engine', name: 'Exabot' },\n { pattern: /ia_archiver/i, kind: 'search_engine', name: 'Alexa' },\n { pattern: /SemrushBot/i, kind: 'search_engine', name: 'SemrushBot' },\n { pattern: /AhrefsBot/i, kind: 'search_engine', name: 'AhrefsBot' },\n { pattern: /MJ12bot/i, kind: 'search_engine', name: 'MJ12bot' },\n { pattern: /DotBot/i, kind: 'search_engine', name: 'DotBot' },\n { pattern: /PetalBot/i, kind: 'search_engine', name: 'PetalBot' },\n { pattern: /Applebot/i, kind: 'search_engine', name: 'Applebot' },\n { pattern: /GPTBot/i, kind: 'search_engine', name: 'GPTBot' },\n { pattern: /ChatGPT-User/i, kind: 'search_engine', name: 'ChatGPT' },\n { pattern: /ClaudeBot/i, kind: 'search_engine', name: 'ClaudeBot' },\n { pattern: /CCBot/i, kind: 'search_engine', name: 'Common Crawl' },\n { pattern: /anthropic-ai/i, kind: 'search_engine', name: 'Anthropic' },\n { pattern: /PerplexityBot/i, kind: 'search_engine', name: 'PerplexityBot' },\n\n // Social crawlers\n { pattern: /facebookexternalhit/i, kind: 'social_crawler', name: 'Facebook' },\n { pattern: /Facebot/i, kind: 'social_crawler', name: 'Facebook' },\n { pattern: /Twitterbot/i, kind: 'social_crawler', name: 'Twitter' },\n { pattern: /LinkedInBot/i, kind: 'social_crawler', name: 'LinkedIn' },\n { pattern: /Slackbot/i, kind: 'social_crawler', name: 'Slack' },\n { pattern: /Discordbot/i, kind: 'social_crawler', name: 'Discord' },\n { pattern: /TelegramBot/i, kind: 'social_crawler', name: 'Telegram' },\n { pattern: /WhatsApp/i, kind: 'social_crawler', name: 'WhatsApp' },\n { pattern: /Pinterestbot/i, kind: 'social_crawler', name: 'Pinterest' },\n { pattern: /Snapchat/i, kind: 'social_crawler', name: 'Snapchat' },\n\n // Headless / automation\n { pattern: /HeadlessChrome/i, kind: 'headless', name: 'Headless Chrome' },\n { pattern: /PhantomJS/i, kind: 'headless', name: 'PhantomJS' },\n { pattern: /Selenium/i, kind: 'automation', name: 'Selenium' },\n { pattern: /Puppeteer/i, kind: 'automation', name: 'Puppeteer' },\n\n // HTTP libraries\n { pattern: /curl\\//i, kind: 'library', name: 'curl' },\n { pattern: /Wget\\//i, kind: 'library', name: 'Wget' },\n { pattern: /python-requests/i, kind: 'library', name: 'Python Requests' },\n { pattern: /python-urllib/i, kind: 'library', name: 'Python urllib' },\n { pattern: /node-fetch/i, kind: 'library', name: 'node-fetch' },\n { pattern: /axios\\//i, kind: 'library', name: 'Axios' },\n { pattern: /Go-http-client/i, kind: 'library', name: 'Go HTTP' },\n { pattern: /Java\\//i, kind: 'library', name: 'Java HTTP' },\n { pattern: /libwww-perl/i, kind: 'library', name: 'Perl LWP' },\n { pattern: /Apache-HttpClient/i, kind: 'library', name: 'Apache HttpClient' },\n { pattern: /okhttp/i, kind: 'library', name: 'OkHttp' },\n { pattern: /Scrapy/i, kind: 'library', name: 'Scrapy' },\n\n // Generic catch-all (must be last)\n { pattern: /bot|crawl|spider|slurp|fetch|archiver/i, kind: 'unknown_bot', name: 'generic' },\n]\n\n// ---------------------------------------------------------------------------\n// Automation globals injected by common frameworks\n// ---------------------------------------------------------------------------\n\nconst AUTOMATION_GLOBALS = [\n // Selenium\n '__selenium_unwrapped',\n '__selenium_evaluate',\n '__webdriver_evaluate',\n '__webdriver_script_fn',\n '__webdriver_script_func',\n '__webdriver_script_function',\n '__fxdriver_evaluate',\n '__fxdriver_unwrapped',\n '_Selenium_IDE_Recorder',\n // Puppeteer / CDP\n '__puppeteer_evaluation_script__',\n // PhantomJS\n 'callPhantom',\n '_phantom',\n 'phantom',\n // Nightmare.js\n '__nightmare',\n // Playwright (injects page.exposeFunction bindings)\n '__playwright',\n '__pw_manual',\n // CasperJS\n '__casper',\n // TestCafe\n '__testcafe',\n // WebDriver (generic)\n 'webdriver',\n 'domAutomation',\n 'domAutomationController',\n] as const\n\n// ---------------------------------------------------------------------------\n// Core detection\n// ---------------------------------------------------------------------------\n\n/**\n * Detect whether the current browser context is a bot or crawler.\n * Synchronous and lightweight \u2014 no async APIs needed.\n */\nexport function detectBot(): BotDetectionResult {\n const signals = collectBotSignals()\n\n const isBot =\n signals.userAgentBot !== null ||\n signals.webdriver ||\n signals.headless ||\n signals.automationGlobals.length > 0 ||\n signals.liesDetected > 2 ||\n (signals.liesDetected > 0 && signals.hasProxy)\n\n let botKind: BotKind = 'human'\n if (isBot) {\n if (signals.userAgentBot !== null) {\n // Find the matching pattern to determine kind\n const ua = navigator.userAgent || ''\n const match = BOT_PATTERNS.find(p => p.pattern.test(ua))\n botKind = match?.kind ?? 'unknown_bot'\n } else if (signals.headless) {\n botKind = 'headless'\n } else if (signals.webdriver || signals.automationGlobals.length > 0) {\n botKind = 'automation'\n } else {\n botKind = 'unknown_bot'\n }\n }\n\n return { isBot, botKind, signals }\n}\n\n// ---------------------------------------------------------------------------\n// Signal collectors\n// ---------------------------------------------------------------------------\n\nfunction collectBotSignals(): BotSignals {\n return {\n userAgentBot: detectUserAgentBot(),\n webdriver: detectWebdriver(),\n headless: detectHeadless(),\n automationGlobals: detectAutomationGlobals(),\n ...detectLies(),\n missingLanguages: detectMissingLanguages(),\n missingPlugins: detectMissingPlugins(),\n }\n}\n\n/** Check UA string against known bot patterns. */\nfunction detectUserAgentBot(): string | null {\n const ua = navigator.userAgent || ''\n if (!ua) return 'empty-ua'\n for (const { pattern, name } of BOT_PATTERNS) {\n if (pattern.test(ua)) return name\n }\n return null\n}\n\n/** navigator.webdriver is set by WebDriver-based automation. */\nfunction detectWebdriver(): boolean {\n return !!(navigator as any).webdriver\n}\n\n/** Headless browser indicators. */\nfunction detectHeadless(): boolean {\n const w = window as any\n const n = navigator as any\n\n // Chrome-specific: headless mode omits the chrome runtime object\n if (/Chrome/.test(n.userAgent) && !w.chrome) return true\n\n // HeadlessChrome in UA\n if (/HeadlessChrome/.test(n.userAgent)) return true\n\n // Notification permission is always \"denied\" in headless Chrome\n // (in headed mode it defaults to \"default\")\n try {\n if (Notification.permission === 'denied' && n.permissions) {\n // Double-check: headless also lacks plugins\n if ((!n.plugins || n.plugins.length === 0) && !/Mobile|Android/i.test(n.userAgent)) {\n return true\n }\n }\n } catch { /* permissions API unavailable */ }\n\n return false\n}\n\n/** Detect automation framework globals on window. */\nfunction detectAutomationGlobals(): string[] {\n const w = window as any\n return AUTOMATION_GLOBALS.filter(key => {\n try { return key in w && w[key] !== undefined }\n catch { return false }\n }) as string[]\n}\n\n/**\n * Lie detection \u2014 detect API tampering (proxies, toString spoofing).\n * Extracted from the fingerprint lies module; self-contained here.\n */\nfunction detectLies(): { liesDetected: number; hasProxy: boolean } {\n let liesDetected = 0\n let hasProxy = false\n\n const apisToTest: Array<[string, () => unknown]> = [\n ['Navigator.prototype.userAgent', () => desc(Navigator.prototype, 'userAgent')],\n ['Navigator.prototype.languages', () => desc(Navigator.prototype, 'languages')],\n ['Navigator.prototype.platform', () => desc(Navigator.prototype, 'platform')],\n ['Navigator.prototype.hardwareConcurrency', () => desc(Navigator.prototype, 'hardwareConcurrency')],\n ['Navigator.prototype.webdriver', () => desc(Navigator.prototype, 'webdriver')],\n ['HTMLCanvasElement.prototype.toDataURL', () => HTMLCanvasElement.prototype.toDataURL],\n ['CanvasRenderingContext2D.prototype.fillText', () => CanvasRenderingContext2D.prototype.fillText],\n ['Date.prototype.getTimezoneOffset', () => Date.prototype.getTimezoneOffset],\n ]\n\n for (const [name, accessor] of apisToTest) {\n try {\n const val = accessor()\n if (val === undefined || val === null) continue\n\n // toString format check\n if (typeof val === 'function') {\n const str = Function.prototype.toString.call(val)\n if (!isNativeToString(str)) liesDetected++\n }\n\n // Getter integrity check\n if (name.includes('.prototype.') && typeof val !== 'function') {\n const parts = name.split('.')\n const protoName = parts[0]\n const prop = parts[parts.length - 1]\n if (protoName && prop) {\n const proto = safeProto(protoName)\n if (!proto) continue\n const d = Object.getOwnPropertyDescriptor(proto, prop)\n if (d?.get) {\n const gs = Function.prototype.toString.call(d.get)\n if (!isNativeToString(gs)) liesDetected++\n }\n }\n }\n\n // Proxy detection\n if (typeof val === 'function') {\n if (val.toString !== Function.prototype.toString) {\n try {\n const native = Function.prototype.toString.call(val)\n const custom = val.toString()\n if (native !== custom) { liesDetected++; hasProxy = true }\n } catch { liesDetected++; hasProxy = true }\n }\n }\n } catch { /* skip inaccessible */ }\n }\n\n // Meta-test: toString integrity\n try {\n const s = Function.prototype.toString.call(Function.prototype.toString)\n if (!isNativeToString(s)) liesDetected++\n } catch { /* skip */ }\n\n return { liesDetected, hasProxy }\n}\n\n/** Missing navigator.languages is a strong headless indicator. */\nfunction detectMissingLanguages(): boolean {\n const langs = navigator.languages\n return !langs || langs.length === 0\n}\n\n/** Missing plugins on desktop is a headless indicator. */\nfunction detectMissingPlugins(): boolean {\n if (/Mobile|Android/i.test(navigator.userAgent)) return false\n return !navigator.plugins || navigator.plugins.length === 0\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isNativeToString(str: string): boolean {\n return /^function\\s[^{]*\\{\\s*\\[native code\\]\\s*\\}$/.test(str) ||\n str === 'function () { [native code] }' ||\n /^\\(\\)\\s*=>\\s*\\{\\s*\\[native code\\]\\s*\\}$/.test(str)\n}\n\nfunction desc(proto: object, prop: string) {\n return Object.getOwnPropertyDescriptor(proto, prop)\n}\n\nfunction safeProto(name: string): object | null {\n try { return (window as any)[name]?.prototype ?? null }\n catch { return null }\n}\n", "export const getEnvironment = (): {\n isLocalhost: boolean;\n isHeadlessBrowser: boolean;\n} => ({\n isLocalhost:\n (/^localhost$|^127(\\.[0-9]+){0,2}\\.[0-9]+$|^\\[::1?\\]$/.test(location.hostname) &&\n (location.protocol === \"http:\" || location.protocol === \"https:\")) ||\n location.protocol === \"file:\",\n isHeadlessBrowser: Boolean(\n window.navigator.webdriver ||\n (\"_phantom\" in window && window._phantom) ||\n (\"__nightmare\" in window && window.__nightmare) ||\n (\"Cypress\" in window && window.Cypress)\n )\n});\nexport const isClient = (): boolean => {\n try {\n // Basic checks for window and document\n if (typeof window === \"undefined\" || typeof document === \"undefined\") return false;\n\n // Check for navigator safely\n const ua = typeof navigator !== \"undefined\" ? navigator.userAgent : \"\";\n if (/node|jsdom/i.test(ua)) return false;\n return true;\n } catch {\n return false;\n }\n};\n", "import type { AnalyticsConfig, InternalAnalyticsConfig } from \"../types\";\nimport { getEnvironment } from \"./environment\";\n\nexport const defaultConfig: InternalAnalyticsConfig = {\n hostname: null,\n devmode: false,\n collectorUrl: \"https://collector.onedollarstats.com/events\",\n hashRouting: false,\n autocollect: true,\n excludePages: [],\n includePages: []\n};\n\nexport const mergeConfig = (userConfig: AnalyticsConfig = {}): InternalAnalyticsConfig => {\n const { isLocalhost } = getEnvironment();\n\n const devmode = !isLocalhost ? false : (userConfig.devmode ?? !!userConfig.trackLocalhostAs);\n\n let hostname: string | null;\n if (userConfig.hostname) {\n const trimmed = userConfig.hostname.trim();\n hostname = trimmed || null;\n } else if (devmode && userConfig.trackLocalhostAs) {\n hostname = userConfig.trackLocalhostAs;\n } else {\n hostname = null;\n }\n\n // Merge default config, user config, and computed values\n return { ...defaultConfig, ...userConfig, hostname, devmode };\n};\n", "export function parseUtmParams(urlSearchParams: URLSearchParams) {\n const utm: Record<string, string> = {};\n const keys = [\"utm_campaign\", \"utm_source\", \"utm_medium\", \"utm_term\", \"utm_content\"] as const;\n\n for (const key of keys) {\n const raw = urlSearchParams.get(key);\n if (!raw) continue;\n\n const decoded = decodeAndTrim(raw);\n if (decoded) {\n utm[key] = decoded;\n }\n }\n\n return utm;\n}\n\nfunction decodeAndTrim(value: string): string {\n let decoded = value;\n let previous = \"\";\n\n while (decoded !== previous) {\n previous = decoded;\n try {\n decoded = decodeURIComponent(decoded);\n } catch {\n return decoded.trim();\n }\n }\n\n return decoded.trim();\n}\n", "export const parseProps = (propsString: string): Record<string, string> | undefined => {\n if (!propsString) return undefined;\n // \"key1=value1;key2=value2\"\n\n const splittedProps = propsString.split(\";\");\n const propsObj: Record<string, string> = {};\n\n for (const keyValueString of splittedProps) {\n const keyValuePair = keyValueString.split(\"=\").map((el) => el.trim());\n if (keyValuePair.length !== 2 || keyValuePair[0] === \"\" || keyValuePair[1] === \"\") continue;\n // @ts-ignore\n propsObj[keyValuePair[0]] = keyValuePair[1];\n }\n\n return Object.keys(propsObj).length === 0 ? undefined : propsObj;\n};\n", "export const resolvePath = (pathOrProps?: string): string => {\n if (pathOrProps) return pathOrProps;\n\n const sources = [\n { value: document.body?.getAttribute(\"data-s-path\"), name: \"data-s-path\" },\n { value: document.body?.getAttribute(\"data-s:path\"), name: \"data-s:path\" },\n { value: document.querySelector('meta[name=\"stonks-path\"]')?.getAttribute(\"content\"), name: \"meta[stonks-path]\" }\n ];\n\n // Only keep sources that actually exist\n const existing = sources.filter(({ value }) => value);\n\n if (existing.length > 1) {\n console.warn(\"[onedollarstats] Multiple path sources found. Using priority order:\", existing.map(({ name }) => name).join(\" > \"));\n }\n\n // Return first available value, fallback to location.pathname\n return existing[0]?.value ?? location.pathname;\n};\n", "import type { InternalAnalyticsConfig } from \"../types\";\n\nconst matchesPattern = (path: string, pattern: string): boolean => {\n // Escape special regex characters except '*' which becomes '.*'\n const escaped = pattern.replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\*/g, \".*\");\n return new RegExp(`^${escaped}$`).test(path);\n};\n\nexport const shouldTrackPath = (path: string, config: InternalAnalyticsConfig): boolean => {\n // Exclude pages first\n if (config.excludePages.some((pattern) => matchesPattern(path, pattern))) return false;\n // If includePages is defined, only allow matching paths\n if (config.includePages.length && !config.includePages.some((pattern) => matchesPattern(path, pattern))) return false;\n return true;\n};\n", "import type { AnalyticsConfig, BaseProps, BodyToSend, Event, InternalAnalyticsConfig, ViewArguments } from \"./types\";\nimport { detectBot } from \"./utils/bot\";\nimport { getEnvironment, isClient } from \"./utils/environment\";\nimport { mergeConfig } from \"./utils/merge-config\";\nimport { parseUtmParams } from \"./utils/parse-utm-params\";\nimport { parseProps } from \"./utils/props-parser\";\nimport { resolvePath } from \"./utils/resolve-path\";\nimport { shouldTrackPath } from \"./utils/should-track\";\n\ndeclare const DEBUG_SCRIPT_URL: string;\n\nclass AnalyticsTracker {\n private static instance: AnalyticsTracker | null = null;\n\n private autocollectSetupDone = false;\n private config: InternalAnalyticsConfig;\n private lastPage: string | null = null;\n\n public static getInstance(userConfig: AnalyticsConfig = {}): AnalyticsTracker {\n // Fresh no-op instance for SSR\n if (!isClient()) return new AnalyticsTracker(userConfig);\n\n if (!AnalyticsTracker.instance) {\n AnalyticsTracker.instance = new AnalyticsTracker(userConfig);\n }\n return AnalyticsTracker.instance;\n }\n\n private constructor(userConfig: AnalyticsConfig = {}) {\n this.config = mergeConfig(userConfig);\n\n // Skip setup in non-client environments\n if (!isClient()) return;\n\n const { isLocalhost } = getEnvironment();\n\n // Debug log on localhost\n if (isLocalhost && this.config.devmode && this.config.hostname) {\n console.log(`[onedollarstats]\\nOneDollarStats connected! Tracking localhost as ${this.config.hostname}`);\n\n // Set up debug modal loading: store config + queue, then dynamically load the debug script\n window.__stonksDebugConfig = { hostname: this.config.hostname, collectorUrl: this.config.collectorUrl };\n window.__stonksModalQueue = [];\n window.__stonksModalReady = false;\n\n const debugScript = document.createElement(\"script\");\n debugScript.src = DEBUG_SCRIPT_URL;\n debugScript.onerror = () => {\n // If the debug script fails to load, mark as ready so queued events are not stuck\n window.__stonksModalReady = true;\n };\n document.head.appendChild(debugScript);\n }\n\n // Auto-start autocollect (always set up listeners; handlePageView checks config)\n this.setupAutocollect();\n }\n\n private async sendWithBeaconOrFetch(stringifiedBody: string, callback: (success: boolean) => void): Promise<void> {\n // First fallback: try sendBeacon\n if (navigator.sendBeacon?.(this.config.collectorUrl, stringifiedBody)) {\n callback(true);\n return;\n }\n\n // Second fallback: use fetch() with keepalive\n fetch(this.config.collectorUrl, {\n method: \"POST\",\n body: stringifiedBody,\n headers: { \"Content-Type\": \"application/json\" },\n keepalive: true\n })\n .then(({ ok }) => callback(ok))\n .catch((err: Error) => {\n console.error(\"[onedollarstats] fetch() failed:\", err.message);\n callback(false);\n });\n }\n\n // Handles localhost replacement, referrer, UTM parameters, and debug mode.\n // Uses img beacon then `navigator.sendBeacon` if available, otherwise falls back to `fetch`.\n private async send(data: Event): Promise<void> {\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.devmode) || isHeadlessBrowser) return;\n\n const { isBot, botKind } = detectBot();\n\n if (isBot && botKind !== \"human\") return;\n\n const urlToSend = new URL(this.config.hostname ? `https://${this.config.hostname}${location.pathname}` : location.href);\n\n // Clean query string unless UTM is explicitly provided\n urlToSend.search = \"\";\n if (data.path) urlToSend.pathname = data.path;\n\n const cleanUrl = urlToSend.href.replace(/\\/$/, \"\");\n\n // Determine referrer\n let referrer: string | undefined = data.referrer;\n try {\n if (!referrer && document.referrer && document.referrer !== \"null\") {\n const referrerURL = new URL(document.referrer);\n if (referrerURL.hostname !== urlToSend.hostname) referrer = referrerURL.href;\n }\n } catch {} // ignore malformed referrer\n\n // Build request body\n const body: BodyToSend = {\n u: cleanUrl,\n e: [\n {\n t: data.type,\n h: this.config.hashRouting,\n r: referrer,\n p: data.props\n }\n ],\n debug: this.config.devmode\n };\n\n if (data.utm && Object.keys(data.utm).length > 0) body.qs = data.utm;\n\n if (body.debug) {\n let logMessage = `[onedollarstats]\\nEvent name: ${data.type}\\nEvent collected from: ${cleanUrl}`;\n if (data.props && Object.keys(data.props).length > 0) logMessage += `\\nProps: ${JSON.stringify(data.props, null, 2)}`;\n if (referrer) logMessage += `\\nReferrer: ${referrer}`;\n if (this.config.hashRouting) logMessage += `\\nHashRouting: ${this.config.hashRouting}`;\n if (data.utm && Object.keys(data.utm).length > 0) logMessage += `\\nUTM: ${data.utm}`;\n\n console.log(logMessage);\n }\n\n // Prepare the event payload\n const stringifiedBody = JSON.stringify(body);\n\n // Encode for safe inclusion in a query string (UTF-8 \u2192 Base64)\n const bytes = new TextEncoder().encode(stringifiedBody); // UTF-8 \u2192 bytes\n const bin = String.fromCharCode(...bytes); // bytes \u2192 binary string\n const payloadBase64 = btoa(bin); // binary \u2192 Base64\n\n const safeGetThreshold = 1500; // limit for query-string-containing URLs\n const tryImageBeacon = payloadBase64.length <= safeGetThreshold;\n\n const onComplete = (success: boolean) => {\n const message = `${data.type} ${success ? \"sent\" : \"failed to send\"}`;\n if (window.__stonksModalReady) {\n window.__stonksModalLog?.(message, success);\n } else {\n window.__stonksModalQueue?.push([message, success]);\n }\n };\n\n if (tryImageBeacon) {\n // Send via image beacon\n const img = new Image(1, 1);\n\n img.onload = () => onComplete(true);\n // If loading image fails (server unavailable, blocked, etc.)\n img.onerror = () => this.sendWithBeaconOrFetch(stringifiedBody, onComplete);\n\n // Primary attempt: send data via image beacon (GET request with query string)\n img.src = `${this.config.collectorUrl}?data=${payloadBase64}`;\n } else await this.sendWithBeaconOrFetch(stringifiedBody, onComplete);\n }\n\n // Prevents duplicate pageviews and respects include/exclude page rules. Automatically parses UTM parameters from URL.\n private trackPageView({ path, props }: ViewArguments, checkBlock: boolean = false) {\n if (!isClient()) return;\n\n const viewPath = resolvePath(path);\n\n // Collect props from DOM attributes\n const collectedProps: Record<string, string> = {};\n const elements = document.querySelectorAll(\"[data-s\\\\:view-props], [data-s-view-props]\");\n\n for (const el of Array.from(elements)) {\n const propsString = el.getAttribute(\"data-s-view-props\") || el.getAttribute(\"data-s:view-props\");\n if (!propsString) continue;\n const parsedProps = parseProps(propsString);\n Object.assign(collectedProps, parsedProps);\n }\n\n // Collect props from meta tag (overrides DOM attributes)\n const metaViewProps = document\n .querySelector('meta[name=\"stonks-props\"]')\n ?.getAttribute(\"content\");\n if (metaViewProps) {\n Object.assign(collectedProps, parseProps(metaViewProps));\n }\n\n // Explicit props override everything\n if (props) {\n Object.assign(collectedProps, props);\n }\n\n const viewProps = Object.keys(collectedProps).length > 0 ? collectedProps : undefined;\n\n // Skip duplicate pageviews or excluded pages\n if (!this.config.hashRouting && this.lastPage === viewPath) return;\n\n // Skip page if checkBlock is true and the path should be excluded\n if (checkBlock && !shouldTrackPath(viewPath, this.config)) return;\n\n this.lastPage = viewPath;\n\n const utm = parseUtmParams(new URLSearchParams(location.search));\n this.send({ type: \"PageView\", path: viewPath, props: viewProps, utm });\n }\n\n /**\n * Tracks a custom event.\n * Can accept path string or a props object.\n *\n * @param eventName Name of the event to track.\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props object if path string is provided.\n */\n public async event(eventName: string, pathOrProps?: string | BaseProps, rawProps?: BaseProps) {\n if (!isClient()) return;\n\n const { isLocalhost, isHeadlessBrowser } = getEnvironment();\n if ((isLocalhost && !this.config.devmode) || isHeadlessBrowser) return;\n\n const path = resolvePath(typeof pathOrProps === \"string\" ? pathOrProps : undefined);\n const props = typeof pathOrProps === \"object\" ? pathOrProps : rawProps;\n\n this.send({ type: eventName, path, props });\n }\n\n /**\n * Records a page view.\n * Can accept path string or a props object.\n *\n * @param pathOrProps Optional path string or props object.\n * @param props Optional props when first arg is a path string.\n */\n public async view(pathOrProps?: string | BaseProps, props?: BaseProps) {\n if (!isClient()) return;\n\n const args: ViewArguments = {};\n\n if (typeof pathOrProps === \"string\") {\n args.path = pathOrProps;\n args.props = props;\n } else if (typeof pathOrProps === \"object\") {\n args.props = pathOrProps;\n }\n\n this.trackPageView(args);\n }\n\n /**\n * Installs global DOM/window listeners exactly once for:\n * - visibilitychange\n * - history.pushState\n * - popstate\n * - hashchange\n * - click autocapture for elements annotated with `data-s:event` & `data-s-event`\n *\n */\n private setupAutocollect() {\n if (!isClient() || this.autocollectSetupDone) return;\n this.autocollectSetupDone = true;\n\n const handlePageView = () => {\n // Check meta tag and body attribute for collection control\n const metaCollect = document\n .querySelector('meta[name=\"stonks-collect\"]')\n ?.getAttribute(\"content\");\n const bodyCollect =\n document.body?.getAttribute(\"data-s-collect\") ||\n document.body?.getAttribute(\"data-s:collect\");\n\n // Explicitly disabled\n if (metaCollect === \"false\" || bodyCollect === \"false\") {\n this.lastPage = null;\n return;\n }\n\n // If autocollect is off, only collect if explicitly enabled via meta/body\n if (!this.config.autocollect && metaCollect !== \"true\" && bodyCollect !== \"true\") {\n this.lastPage = null;\n return;\n }\n\n this.trackPageView({}, true);\n };\n\n // visibilitychange\n const onVisibility = () => {\n if (document.visibilityState === \"visible\") handlePageView();\n };\n document.addEventListener(\"visibilitychange\", onVisibility);\n\n // pushState\n const origPush = history.pushState.bind(history);\n history.pushState = (...args) => {\n origPush(...args);\n requestAnimationFrame(() => {\n handlePageView();\n });\n };\n\n // popstate\n window.addEventListener(\"popstate\", handlePageView);\n\n // hashchange\n window.addEventListener(\"hashchange\", handlePageView);\n\n // click autocapture\n const onClick: EventListener = (ev: Event) => {\n const clickEvent = ev as MouseEvent;\n if (clickEvent.type === \"auxclick\" && clickEvent.button !== 1) return;\n\n const target = clickEvent.target as Element | null;\n if (!target) return;\n\n // Check if inside <a> or <button>\n const insideInteractive = !!target.closest(\"a, button\");\n\n let el: Element | null = target;\n let depth = 0;\n\n while (el) {\n const eventName = el.getAttribute(\"data-s-event\") || el.getAttribute(\"data-s:event\");\n if (eventName) {\n const propsAttr = el.getAttribute(\"data-s-event-props\") || el.getAttribute(\"data-s:event-props\");\n const props = propsAttr ? parseProps(propsAttr) : undefined;\n const path = el.getAttribute(\"data-s-event-path\") || el.getAttribute(\"data-s:event-path\") || undefined;\n\n if ((path && !shouldTrackPath(path, this.config)) || !shouldTrackPath(location.pathname, this.config)) {\n return;\n }\n\n this.event(eventName, path ?? props, props);\n return;\n }\n\n el = el.parentElement;\n depth++;\n\n // If not in <a>/<button>, stop after 3 levels\n if (!insideInteractive && depth >= 3) break;\n }\n };\n\n document.addEventListener(\"click\", onClick);\n\n // Fire initial pageview if already visible\n if (document.visibilityState === \"visible\") handlePageView();\n }\n}\n\nexport const configure = (userConfig: AnalyticsConfig = {}) => {\n AnalyticsTracker.getInstance(userConfig);\n};\n\nexport const event = async (eventName: string, pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.event(eventName, pathOrProps, props);\n};\n\nexport const view = async (pathOrProps?: string | BaseProps, props?: BaseProps) => {\n const instance = AnalyticsTracker.getInstance();\n await instance.view(pathOrProps, props);\n};\n"],
|
|
5
|
+
"mappings": ";AA8DA,IAAM,eAA6B;AAAA;AAAA,EAEjC,EAAE,SAAS,cAA0B,MAAM,iBAAkB,MAAM,YAAY;AAAA,EAC/E,EAAE,SAAS,0BAA2B,MAAM,iBAAkB,MAAM,YAAY;AAAA,EAChF,EAAE,SAAS,oBAA2B,MAAM,iBAAkB,MAAM,YAAY;AAAA,EAChF,EAAE,SAAS,kBAA2B,MAAM,iBAAkB,MAAM,aAAa;AAAA,EACjF,EAAE,SAAS,yBAA2B,MAAM,iBAAkB,MAAM,iBAAiB;AAAA,EACrF,EAAE,SAAS,YAA0B,MAAM,iBAAkB,MAAM,UAAU;AAAA,EAC7E,EAAE,SAAS,WAA0B,MAAM,iBAAkB,MAAM,SAAS;AAAA,EAC5E,EAAE,SAAS,cAA0B,MAAM,iBAAkB,MAAM,YAAY;AAAA,EAC/E,EAAE,SAAS,2BAA2B,MAAM,iBAAiB,MAAM,YAAY;AAAA,EAC/E,EAAE,SAAS,gBAA0B,MAAM,iBAAkB,MAAM,QAAQ;AAAA,EAC3E,EAAE,SAAS,gBAA0B,MAAM,iBAAkB,MAAM,cAAc;AAAA,EACjF,EAAE,SAAS,UAA0B,MAAM,iBAAkB,MAAM,QAAQ;AAAA,EAC3E,EAAE,SAAS,WAA0B,MAAM,iBAAkB,MAAM,SAAS;AAAA,EAC5E,EAAE,SAAS,gBAA0B,MAAM,iBAAkB,MAAM,QAAQ;AAAA,EAC3E,EAAE,SAAS,eAA0B,MAAM,iBAAkB,MAAM,aAAa;AAAA,EAChF,EAAE,SAAS,cAA0B,MAAM,iBAAkB,MAAM,YAAY;AAAA,EAC/E,EAAE,SAAS,YAA0B,MAAM,iBAAkB,MAAM,UAAU;AAAA,EAC7E,EAAE,SAAS,WAA0B,MAAM,iBAAkB,MAAM,SAAS;AAAA,EAC5E,EAAE,SAAS,aAA0B,MAAM,iBAAkB,MAAM,WAAW;AAAA,EAC9E,EAAE,SAAS,aAA0B,MAAM,iBAAkB,MAAM,WAAW;AAAA,EAC9E,EAAE,SAAS,WAA0B,MAAM,iBAAkB,MAAM,SAAS;AAAA,EAC5E,EAAE,SAAS,iBAA0B,MAAM,iBAAkB,MAAM,UAAU;AAAA,EAC7E,EAAE,SAAS,cAA0B,MAAM,iBAAkB,MAAM,YAAY;AAAA,EAC/E,EAAE,SAAS,UAA0B,MAAM,iBAAkB,MAAM,eAAe;AAAA,EAClF,EAAE,SAAS,iBAA0B,MAAM,iBAAkB,MAAM,YAAY;AAAA,EAC/E,EAAE,SAAS,kBAA0B,MAAM,iBAAkB,MAAM,gBAAgB;AAAA;AAAA,EAGnF,EAAE,SAAS,wBAA0B,MAAM,kBAAkB,MAAM,WAAW;AAAA,EAC9E,EAAE,SAAS,YAAyB,MAAM,kBAAkB,MAAM,WAAW;AAAA,EAC7E,EAAE,SAAS,eAAyB,MAAM,kBAAkB,MAAM,UAAU;AAAA,EAC5E,EAAE,SAAS,gBAAyB,MAAM,kBAAkB,MAAM,WAAW;AAAA,EAC7E,EAAE,SAAS,aAAyB,MAAM,kBAAkB,MAAM,QAAQ;AAAA,EAC1E,EAAE,SAAS,eAAyB,MAAM,kBAAkB,MAAM,UAAU;AAAA,EAC5E,EAAE,SAAS,gBAAyB,MAAM,kBAAkB,MAAM,WAAW;AAAA,EAC7E,EAAE,SAAS,aAAyB,MAAM,kBAAkB,MAAM,WAAW;AAAA,EAC7E,EAAE,SAAS,iBAAyB,MAAM,kBAAkB,MAAM,YAAY;AAAA,EAC9E,EAAE,SAAS,aAAyB,MAAM,kBAAkB,MAAM,WAAW;AAAA;AAAA,EAG7E,EAAE,SAAS,mBAA0B,MAAM,YAAkB,MAAM,kBAAkB;AAAA,EACrF,EAAE,SAAS,cAAyB,MAAM,YAAkB,MAAM,YAAY;AAAA,EAC9E,EAAE,SAAS,aAAyB,MAAM,cAAkB,MAAM,WAAW;AAAA,EAC7E,EAAE,SAAS,cAAyB,MAAM,cAAkB,MAAM,YAAY;AAAA;AAAA,EAG9E,EAAE,SAAS,WAAyB,MAAM,WAAkB,MAAM,OAAO;AAAA,EACzE,EAAE,SAAS,WAAyB,MAAM,WAAkB,MAAM,OAAO;AAAA,EACzE,EAAE,SAAS,oBAAyB,MAAM,WAAkB,MAAM,kBAAkB;AAAA,EACpF,EAAE,SAAS,kBAAyB,MAAM,WAAkB,MAAM,gBAAgB;AAAA,EAClF,EAAE,SAAS,eAAyB,MAAM,WAAkB,MAAM,aAAa;AAAA,EAC/E,EAAE,SAAS,YAAyB,MAAM,WAAkB,MAAM,QAAQ;AAAA,EAC1E,EAAE,SAAS,mBAAyB,MAAM,WAAkB,MAAM,UAAU;AAAA,EAC5E,EAAE,SAAS,WAAyB,MAAM,WAAkB,MAAM,YAAY;AAAA,EAC9E,EAAE,SAAS,gBAAyB,MAAM,WAAkB,MAAM,WAAW;AAAA,EAC7E,EAAE,SAAS,sBAAyB,MAAM,WAAkB,MAAM,oBAAoB;AAAA,EACtF,EAAE,SAAS,WAAyB,MAAM,WAAkB,MAAM,SAAS;AAAA,EAC3E,EAAE,SAAS,WAAyB,MAAM,WAAkB,MAAM,SAAS;AAAA;AAAA,EAG3E,EAAE,SAAS,0CAA0C,MAAM,eAAe,MAAM,UAAU;AAC5F;AAMA,IAAM,qBAAqB;AAAA;AAAA,EAEzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AACF;AAUO,SAAS,YAAgC;AAC9C,QAAM,UAAU,kBAAkB;AAElC,QAAM,QACJ,QAAQ,iBAAiB,QACzB,QAAQ,aACR,QAAQ,YACR,QAAQ,kBAAkB,SAAS,KACnC,QAAQ,eAAe,KACtB,QAAQ,eAAe,KAAK,QAAQ;AAEvC,MAAI,UAAmB;AACvB,MAAI,OAAO;AACT,QAAI,QAAQ,iBAAiB,MAAM;AAEjC,YAAM,KAAK,UAAU,aAAa;AAClC,YAAM,QAAQ,aAAa,KAAK,OAAK,EAAE,QAAQ,KAAK,EAAE,CAAC;AACvD,gBAAU,OAAO,QAAQ;AAAA,IAC3B,WAAW,QAAQ,UAAU;AAC3B,gBAAU;AAAA,IACZ,WAAW,QAAQ,aAAa,QAAQ,kBAAkB,SAAS,GAAG;AACpE,gBAAU;AAAA,IACZ,OAAO;AACL,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,SAAS,QAAQ;AACnC;AAMA,SAAS,oBAAgC;AACvC,SAAO;AAAA,IACL,cAAc,mBAAmB;AAAA,IACjC,WAAW,gBAAgB;AAAA,IAC3B,UAAU,eAAe;AAAA,IACzB,mBAAmB,wBAAwB;AAAA,IAC3C,GAAG,WAAW;AAAA,IACd,kBAAkB,uBAAuB;AAAA,IACzC,gBAAgB,qBAAqB;AAAA,EACvC;AACF;AAGA,SAAS,qBAAoC;AAC3C,QAAM,KAAK,UAAU,aAAa;AAClC,MAAI,CAAC,GAAI,QAAO;AAChB,aAAW,EAAE,SAAS,KAAK,KAAK,cAAc;AAC5C,QAAI,QAAQ,KAAK,EAAE,EAAG,QAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAGA,SAAS,kBAA2B;AAClC,SAAO,CAAC,CAAE,UAAkB;AAC9B;AAGA,SAAS,iBAA0B;AACjC,QAAM,IAAI;AACV,QAAM,IAAI;AAGV,MAAI,SAAS,KAAK,EAAE,SAAS,KAAK,CAAC,EAAE,OAAQ,QAAO;AAGpD,MAAI,iBAAiB,KAAK,EAAE,SAAS,EAAG,QAAO;AAI/C,MAAI;AACF,QAAI,aAAa,eAAe,YAAY,EAAE,aAAa;AAEzD,WAAK,CAAC,EAAE,WAAW,EAAE,QAAQ,WAAW,MAAM,CAAC,kBAAkB,KAAK,EAAE,SAAS,GAAG;AAClF,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAAoC;AAE5C,SAAO;AACT;AAGA,SAAS,0BAAoC;AAC3C,QAAM,IAAI;AACV,SAAO,mBAAmB,OAAO,SAAO;AACtC,QAAI;AAAE,aAAO,OAAO,KAAK,EAAE,GAAG,MAAM;AAAA,IAAU,QACxC;AAAE,aAAO;AAAA,IAAM;AAAA,EACvB,CAAC;AACH;AAMA,SAAS,aAA0D;AACjE,MAAI,eAAe;AACnB,MAAI,WAAW;AAEf,QAAM,aAA6C;AAAA,IACjD,CAAC,iCAAiC,MAAM,KAAK,UAAU,WAAW,WAAW,CAAC;AAAA,IAC9E,CAAC,iCAAiC,MAAM,KAAK,UAAU,WAAW,WAAW,CAAC;AAAA,IAC9E,CAAC,gCAAgC,MAAM,KAAK,UAAU,WAAW,UAAU,CAAC;AAAA,IAC5E,CAAC,2CAA2C,MAAM,KAAK,UAAU,WAAW,qBAAqB,CAAC;AAAA,IAClG,CAAC,iCAAiC,MAAM,KAAK,UAAU,WAAW,WAAW,CAAC;AAAA,IAC9E,CAAC,yCAAyC,MAAM,kBAAkB,UAAU,SAAS;AAAA,IACrF,CAAC,+CAA+C,MAAM,yBAAyB,UAAU,QAAQ;AAAA,IACjG,CAAC,oCAAoC,MAAM,KAAK,UAAU,iBAAiB;AAAA,EAC7E;AAEA,aAAW,CAAC,MAAM,QAAQ,KAAK,YAAY;AACzC,QAAI;AACF,YAAM,MAAM,SAAS;AACrB,UAAI,QAAQ,UAAa,QAAQ,KAAM;AAGvC,UAAI,OAAO,QAAQ,YAAY;AAC7B,cAAM,MAAM,SAAS,UAAU,SAAS,KAAK,GAAG;AAChD,YAAI,CAAC,iBAAiB,GAAG,EAAG;AAAA,MAC9B;AAGA,UAAI,KAAK,SAAS,aAAa,KAAK,OAAO,QAAQ,YAAY;AAC7D,cAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,cAAM,YAAY,MAAM,CAAC;AACzB,cAAM,OAAO,MAAM,MAAM,SAAS,CAAC;AACnC,YAAI,aAAa,MAAM;AACrB,gBAAM,QAAQ,UAAU,SAAS;AACjC,cAAI,CAAC,MAAO;AACZ,gBAAM,IAAI,OAAO,yBAAyB,OAAO,IAAI;AACrD,cAAI,GAAG,KAAK;AACV,kBAAM,KAAK,SAAS,UAAU,SAAS,KAAK,EAAE,GAAG;AACjD,gBAAI,CAAC,iBAAiB,EAAE,EAAG;AAAA,UAC7B;AAAA,QACF;AAAA,MACF;AAGA,UAAI,OAAO,QAAQ,YAAY;AAC7B,YAAI,IAAI,aAAa,SAAS,UAAU,UAAU;AAChD,cAAI;AACF,kBAAM,SAAS,SAAS,UAAU,SAAS,KAAK,GAAG;AACnD,kBAAM,SAAS,IAAI,SAAS;AAC5B,gBAAI,WAAW,QAAQ;AAAE;AAAgB,yBAAW;AAAA,YAAK;AAAA,UAC3D,QAAQ;AAAE;AAAgB,uBAAW;AAAA,UAAK;AAAA,QAC5C;AAAA,MACF;AAAA,IACF,QAAQ;AAAA,IAA0B;AAAA,EACpC;AAGA,MAAI;AACF,UAAM,IAAI,SAAS,UAAU,SAAS,KAAK,SAAS,UAAU,QAAQ;AACtE,QAAI,CAAC,iBAAiB,CAAC,EAAG;AAAA,EAC5B,QAAQ;AAAA,EAAa;AAErB,SAAO,EAAE,cAAc,SAAS;AAClC;AAGA,SAAS,yBAAkC;AACzC,QAAM,QAAQ,UAAU;AACxB,SAAO,CAAC,SAAS,MAAM,WAAW;AACpC;AAGA,SAAS,uBAAgC;AACvC,MAAI,kBAAkB,KAAK,UAAU,SAAS,EAAG,QAAO;AACxD,SAAO,CAAC,UAAU,WAAW,UAAU,QAAQ,WAAW;AAC5D;AAMA,SAAS,iBAAiB,KAAsB;AAC9C,SAAO,6CAA6C,KAAK,GAAG,KAC1D,QAAQ,mCACR,0CAA0C,KAAK,GAAG;AACtD;AAEA,SAAS,KAAK,OAAe,MAAc;AACzC,SAAO,OAAO,yBAAyB,OAAO,IAAI;AACpD;AAEA,SAAS,UAAU,MAA6B;AAC9C,MAAI;AAAE,WAAQ,OAAe,IAAI,GAAG,aAAa;AAAA,EAAK,QAChD;AAAE,WAAO;AAAA,EAAK;AACtB;;;AC3WO,IAAM,iBAAiB,OAGxB;AAAA,EACJ,aACG,sDAAsD,KAAK,SAAS,QAAQ,MAC1E,SAAS,aAAa,WAAW,SAAS,aAAa,aAC1D,SAAS,aAAa;AAAA,EACxB,mBAAmB;AAAA,IACjB,OAAO,UAAU,aAChB,cAAc,UAAU,OAAO,YAC/B,iBAAiB,UAAU,OAAO,eAClC,aAAa,UAAU,OAAO;AAAA,EACjC;AACF;AACO,IAAM,WAAW,MAAe;AACrC,MAAI;AAEF,QAAI,OAAO,WAAW,eAAe,OAAO,aAAa,YAAa,QAAO;AAG7E,UAAM,KAAK,OAAO,cAAc,cAAc,UAAU,YAAY;AACpE,QAAI,cAAc,KAAK,EAAE,EAAG,QAAO;AACnC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACxBO,IAAM,gBAAyC;AAAA,EACpD,UAAU;AAAA,EACV,SAAS;AAAA,EACT,cAAc;AAAA,EACd,aAAa;AAAA,EACb,aAAa;AAAA,EACb,cAAc,CAAC;AAAA,EACf,cAAc,CAAC;AACjB;AAEO,IAAM,cAAc,CAAC,aAA8B,CAAC,MAA+B;AACxF,QAAM,EAAE,YAAY,IAAI,eAAe;AAEvC,QAAM,UAAU,CAAC,cAAc,QAAS,WAAW,WAAW,CAAC,CAAC,WAAW;AAE3E,MAAI;AACJ,MAAI,WAAW,UAAU;AACvB,UAAM,UAAU,WAAW,SAAS,KAAK;AACzC,eAAW,WAAW;AAAA,EACxB,WAAW,WAAW,WAAW,kBAAkB;AACjD,eAAW,WAAW;AAAA,EACxB,OAAO;AACL,eAAW;AAAA,EACb;AAGA,SAAO,EAAE,GAAG,eAAe,GAAG,YAAY,UAAU,QAAQ;AAC9D;;;AC9BO,SAAS,eAAe,iBAAkC;AAC/D,QAAM,MAA8B,CAAC;AACrC,QAAM,OAAO,CAAC,gBAAgB,cAAc,cAAc,YAAY,aAAa;AAEnF,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,gBAAgB,IAAI,GAAG;AACnC,QAAI,CAAC,IAAK;AAEV,UAAM,UAAU,cAAc,GAAG;AACjC,QAAI,SAAS;AACX,UAAI,GAAG,IAAI;AAAA,IACb;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,OAAuB;AAC5C,MAAI,UAAU;AACd,MAAI,WAAW;AAEf,SAAO,YAAY,UAAU;AAC3B,eAAW;AACX,QAAI;AACF,gBAAU,mBAAmB,OAAO;AAAA,IACtC,QAAQ;AACN,aAAO,QAAQ,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,SAAO,QAAQ,KAAK;AACtB;;;AC/BO,IAAM,aAAa,CAAC,gBAA4D;AACrF,MAAI,CAAC,YAAa,QAAO;AAGzB,QAAM,gBAAgB,YAAY,MAAM,GAAG;AAC3C,QAAM,WAAmC,CAAC;AAE1C,aAAW,kBAAkB,eAAe;AAC1C,UAAM,eAAe,eAAe,MAAM,GAAG,EAAE,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;AACpE,QAAI,aAAa,WAAW,KAAK,aAAa,CAAC,MAAM,MAAM,aAAa,CAAC,MAAM,GAAI;AAEnF,aAAS,aAAa,CAAC,CAAC,IAAI,aAAa,CAAC;AAAA,EAC5C;AAEA,SAAO,OAAO,KAAK,QAAQ,EAAE,WAAW,IAAI,SAAY;AAC1D;;;ACfO,IAAM,cAAc,CAAC,gBAAiC;AAC3D,MAAI,YAAa,QAAO;AAExB,QAAM,UAAU;AAAA,IACd,EAAE,OAAO,SAAS,MAAM,aAAa,aAAa,GAAG,MAAM,cAAc;AAAA,IACzE,EAAE,OAAO,SAAS,MAAM,aAAa,aAAa,GAAG,MAAM,cAAc;AAAA,IACzE,EAAE,OAAO,SAAS,cAAc,0BAA0B,GAAG,aAAa,SAAS,GAAG,MAAM,oBAAoB;AAAA,EAClH;AAGA,QAAM,WAAW,QAAQ,OAAO,CAAC,EAAE,MAAM,MAAM,KAAK;AAEpD,MAAI,SAAS,SAAS,GAAG;AACvB,YAAQ,KAAK,uEAAuE,SAAS,IAAI,CAAC,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,EAClI;AAGA,SAAO,SAAS,CAAC,GAAG,SAAS,SAAS;AACxC;;;AChBA,IAAM,iBAAiB,CAAC,MAAc,YAA6B;AAEjE,QAAM,UAAU,QAAQ,QAAQ,qBAAqB,MAAM,EAAE,QAAQ,OAAO,IAAI;AAChF,SAAO,IAAI,OAAO,IAAI,OAAO,GAAG,EAAE,KAAK,IAAI;AAC7C;AAEO,IAAM,kBAAkB,CAAC,MAAc,WAA6C;AAEzF,MAAI,OAAO,aAAa,KAAK,CAAC,YAAY,eAAe,MAAM,OAAO,CAAC,EAAG,QAAO;AAEjF,MAAI,OAAO,aAAa,UAAU,CAAC,OAAO,aAAa,KAAK,CAAC,YAAY,eAAe,MAAM,OAAO,CAAC,EAAG,QAAO;AAChH,SAAO;AACT;;;ACHA,IAAM,oBAAN,MAAM,kBAAiB;AAAA,EAiBb,YAAY,aAA8B,CAAC,GAAG;AAdtD,SAAQ,uBAAuB;AAE/B,SAAQ,WAA0B;AAahC,SAAK,SAAS,YAAY,UAAU;AAGpC,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,EAAE,YAAY,IAAI,eAAe;AAGvC,QAAI,eAAe,KAAK,OAAO,WAAW,KAAK,OAAO,UAAU;AAC9D,cAAQ,IAAI;AAAA,kDAAqE,KAAK,OAAO,QAAQ,EAAE;AAGvG,aAAO,sBAAsB,EAAE,UAAU,KAAK,OAAO,UAAU,cAAc,KAAK,OAAO,aAAa;AACtG,aAAO,qBAAqB,CAAC;AAC7B,aAAO,qBAAqB;AAE5B,YAAM,cAAc,SAAS,cAAc,QAAQ;AACnD,kBAAY,MAAM;AAClB,kBAAY,UAAU,MAAM;AAE1B,eAAO,qBAAqB;AAAA,MAC9B;AACA,eAAS,KAAK,YAAY,WAAW;AAAA,IACvC;AAGA,SAAK,iBAAiB;AAAA,EACxB;AAAA,EAtCA,OAAc,YAAY,aAA8B,CAAC,GAAqB;AAE5E,QAAI,CAAC,SAAS,EAAG,QAAO,IAAI,kBAAiB,UAAU;AAEvD,QAAI,CAAC,kBAAiB,UAAU;AAC9B,wBAAiB,WAAW,IAAI,kBAAiB,UAAU;AAAA,IAC7D;AACA,WAAO,kBAAiB;AAAA,EAC1B;AAAA,EAgCA,MAAc,sBAAsB,iBAAyB,UAAqD;AAEhH,QAAI,UAAU,aAAa,KAAK,OAAO,cAAc,eAAe,GAAG;AACrE,eAAS,IAAI;AACb;AAAA,IACF;AAGA,UAAM,KAAK,OAAO,cAAc;AAAA,MAC9B,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,WAAW;AAAA,IACb,CAAC,EACE,KAAK,CAAC,EAAE,GAAG,MAAM,SAAS,EAAE,CAAC,EAC7B,MAAM,CAAC,QAAe;AACrB,cAAQ,MAAM,oCAAoC,IAAI,OAAO;AAC7D,eAAS,KAAK;AAAA,IAChB,CAAC;AAAA,EACL;AAAA;AAAA;AAAA,EAIA,MAAc,KAAK,MAA4B;AAC7C,UAAM,EAAE,aAAa,kBAAkB,IAAI,eAAe;AAC1D,QAAK,eAAe,CAAC,KAAK,OAAO,WAAY,kBAAmB;AAEhE,UAAM,EAAE,OAAO,QAAQ,IAAI,UAAU;AAErC,QAAI,SAAS,YAAY,QAAS;AAElC,UAAM,YAAY,IAAI,IAAI,KAAK,OAAO,WAAW,WAAW,KAAK,OAAO,QAAQ,GAAG,SAAS,QAAQ,KAAK,SAAS,IAAI;AAGtH,cAAU,SAAS;AACnB,QAAI,KAAK,KAAM,WAAU,WAAW,KAAK;AAEzC,UAAM,WAAW,UAAU,KAAK,QAAQ,OAAO,EAAE;AAGjD,QAAI,WAA+B,KAAK;AACxC,QAAI;AACF,UAAI,CAAC,YAAY,SAAS,YAAY,SAAS,aAAa,QAAQ;AAClE,cAAM,cAAc,IAAI,IAAI,SAAS,QAAQ;AAC7C,YAAI,YAAY,aAAa,UAAU,SAAU,YAAW,YAAY;AAAA,MAC1E;AAAA,IACF,QAAQ;AAAA,IAAC;AAGT,UAAM,OAAmB;AAAA,MACvB,GAAG;AAAA,MACH,GAAG;AAAA,QACD;AAAA,UACE,GAAG,KAAK;AAAA,UACR,GAAG,KAAK,OAAO;AAAA,UACf,GAAG;AAAA,UACH,GAAG,KAAK;AAAA,QACV;AAAA,MACF;AAAA,MACA,OAAO,KAAK,OAAO;AAAA,IACrB;AAEA,QAAI,KAAK,OAAO,OAAO,KAAK,KAAK,GAAG,EAAE,SAAS,EAAG,MAAK,KAAK,KAAK;AAEjE,QAAI,KAAK,OAAO;AACd,UAAI,aAAa;AAAA,cAAiC,KAAK,IAAI;AAAA,wBAA2B,QAAQ;AAC9F,UAAI,KAAK,SAAS,OAAO,KAAK,KAAK,KAAK,EAAE,SAAS,EAAG,eAAc;AAAA,SAAY,KAAK,UAAU,KAAK,OAAO,MAAM,CAAC,CAAC;AACnH,UAAI,SAAU,eAAc;AAAA,YAAe,QAAQ;AACnD,UAAI,KAAK,OAAO,YAAa,eAAc;AAAA,eAAkB,KAAK,OAAO,WAAW;AACpF,UAAI,KAAK,OAAO,OAAO,KAAK,KAAK,GAAG,EAAE,SAAS,EAAG,eAAc;AAAA,OAAU,KAAK,GAAG;AAElF,cAAQ,IAAI,UAAU;AAAA,IACxB;AAGA,UAAM,kBAAkB,KAAK,UAAU,IAAI;AAG3C,UAAM,QAAQ,IAAI,YAAY,EAAE,OAAO,eAAe;AACtD,UAAM,MAAM,OAAO,aAAa,GAAG,KAAK;AACxC,UAAM,gBAAgB,KAAK,GAAG;AAE9B,UAAM,mBAAmB;AACzB,UAAM,iBAAiB,cAAc,UAAU;AAE/C,UAAM,aAAa,CAAC,YAAqB;AACvC,YAAM,UAAU,GAAG,KAAK,IAAI,IAAI,UAAU,SAAS,gBAAgB;AACnE,UAAI,OAAO,oBAAoB;AAC7B,eAAO,mBAAmB,SAAS,OAAO;AAAA,MAC5C,OAAO;AACL,eAAO,oBAAoB,KAAK,CAAC,SAAS,OAAO,CAAC;AAAA,MACpD;AAAA,IACF;AAEA,QAAI,gBAAgB;AAElB,YAAM,MAAM,IAAI,MAAM,GAAG,CAAC;AAE1B,UAAI,SAAS,MAAM,WAAW,IAAI;AAElC,UAAI,UAAU,MAAM,KAAK,sBAAsB,iBAAiB,UAAU;AAG1E,UAAI,MAAM,GAAG,KAAK,OAAO,YAAY,SAAS,aAAa;AAAA,IAC7D,MAAO,OAAM,KAAK,sBAAsB,iBAAiB,UAAU;AAAA,EACrE;AAAA;AAAA,EAGQ,cAAc,EAAE,MAAM,MAAM,GAAkB,aAAsB,OAAO;AACjF,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,WAAW,YAAY,IAAI;AAGjC,UAAM,iBAAyC,CAAC;AAChD,UAAM,WAAW,SAAS,iBAAiB,4CAA4C;AAEvF,eAAW,MAAM,MAAM,KAAK,QAAQ,GAAG;AACrC,YAAM,cAAc,GAAG,aAAa,mBAAmB,KAAK,GAAG,aAAa,mBAAmB;AAC/F,UAAI,CAAC,YAAa;AAClB,YAAM,cAAc,WAAW,WAAW;AAC1C,aAAO,OAAO,gBAAgB,WAAW;AAAA,IAC3C;AAGA,UAAM,gBAAgB,SACnB,cAAc,2BAA2B,GACxC,aAAa,SAAS;AAC1B,QAAI,eAAe;AACjB,aAAO,OAAO,gBAAgB,WAAW,aAAa,CAAC;AAAA,IACzD;AAGA,QAAI,OAAO;AACT,aAAO,OAAO,gBAAgB,KAAK;AAAA,IACrC;AAEA,UAAM,YAAY,OAAO,KAAK,cAAc,EAAE,SAAS,IAAI,iBAAiB;AAG5E,QAAI,CAAC,KAAK,OAAO,eAAe,KAAK,aAAa,SAAU;AAG5D,QAAI,cAAc,CAAC,gBAAgB,UAAU,KAAK,MAAM,EAAG;AAE3D,SAAK,WAAW;AAEhB,UAAM,MAAM,eAAe,IAAI,gBAAgB,SAAS,MAAM,CAAC;AAC/D,SAAK,KAAK,EAAE,MAAM,YAAY,MAAM,UAAU,OAAO,WAAW,IAAI,CAAC;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAa,MAAM,WAAmB,aAAkC,UAAsB;AAC5F,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,EAAE,aAAa,kBAAkB,IAAI,eAAe;AAC1D,QAAK,eAAe,CAAC,KAAK,OAAO,WAAY,kBAAmB;AAEhE,UAAM,OAAO,YAAY,OAAO,gBAAgB,WAAW,cAAc,MAAS;AAClF,UAAM,QAAQ,OAAO,gBAAgB,WAAW,cAAc;AAE9D,SAAK,KAAK,EAAE,MAAM,WAAW,MAAM,MAAM,CAAC;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,KAAK,aAAkC,OAAmB;AACrE,QAAI,CAAC,SAAS,EAAG;AAEjB,UAAM,OAAsB,CAAC;AAE7B,QAAI,OAAO,gBAAgB,UAAU;AACnC,WAAK,OAAO;AACZ,WAAK,QAAQ;AAAA,IACf,WAAW,OAAO,gBAAgB,UAAU;AAC1C,WAAK,QAAQ;AAAA,IACf;AAEA,SAAK,cAAc,IAAI;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,mBAAmB;AACzB,QAAI,CAAC,SAAS,KAAK,KAAK,qBAAsB;AAC9C,SAAK,uBAAuB;AAE5B,UAAM,iBAAiB,MAAM;AAE3B,YAAM,cAAc,SACjB,cAAc,6BAA6B,GAC1C,aAAa,SAAS;AAC1B,YAAM,cACJ,SAAS,MAAM,aAAa,gBAAgB,KAC5C,SAAS,MAAM,aAAa,gBAAgB;AAG9C,UAAI,gBAAgB,WAAW,gBAAgB,SAAS;AACtD,aAAK,WAAW;AAChB;AAAA,MACF;AAGA,UAAI,CAAC,KAAK,OAAO,eAAe,gBAAgB,UAAU,gBAAgB,QAAQ;AAChF,aAAK,WAAW;AAChB;AAAA,MACF;AAEA,WAAK,cAAc,CAAC,GAAG,IAAI;AAAA,IAC7B;AAGA,UAAM,eAAe,MAAM;AACzB,UAAI,SAAS,oBAAoB,UAAW,gBAAe;AAAA,IAC7D;AACA,aAAS,iBAAiB,oBAAoB,YAAY;AAG1D,UAAM,WAAW,QAAQ,UAAU,KAAK,OAAO;AAC/C,YAAQ,YAAY,IAAI,SAAS;AAC/B,eAAS,GAAG,IAAI;AAChB,4BAAsB,MAAM;AAC1B,uBAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAGA,WAAO,iBAAiB,YAAY,cAAc;AAGlD,WAAO,iBAAiB,cAAc,cAAc;AAGpD,UAAM,UAAyB,CAAC,OAAc;AAC5C,YAAM,aAAa;AACnB,UAAI,WAAW,SAAS,cAAc,WAAW,WAAW,EAAG;AAE/D,YAAM,SAAS,WAAW;AAC1B,UAAI,CAAC,OAAQ;AAGb,YAAM,oBAAoB,CAAC,CAAC,OAAO,QAAQ,WAAW;AAEtD,UAAI,KAAqB;AACzB,UAAI,QAAQ;AAEZ,aAAO,IAAI;AACT,cAAM,YAAY,GAAG,aAAa,cAAc,KAAK,GAAG,aAAa,cAAc;AACnF,YAAI,WAAW;AACb,gBAAM,YAAY,GAAG,aAAa,oBAAoB,KAAK,GAAG,aAAa,oBAAoB;AAC/F,gBAAM,QAAQ,YAAY,WAAW,SAAS,IAAI;AAClD,gBAAM,OAAO,GAAG,aAAa,mBAAmB,KAAK,GAAG,aAAa,mBAAmB,KAAK;AAE7F,cAAK,QAAQ,CAAC,gBAAgB,MAAM,KAAK,MAAM,KAAM,CAAC,gBAAgB,SAAS,UAAU,KAAK,MAAM,GAAG;AACrG;AAAA,UACF;AAEA,eAAK,MAAM,WAAW,QAAQ,OAAO,KAAK;AAC1C;AAAA,QACF;AAEA,aAAK,GAAG;AACR;AAGA,YAAI,CAAC,qBAAqB,SAAS,EAAG;AAAA,MACxC;AAAA,IACF;AAEA,aAAS,iBAAiB,SAAS,OAAO;AAG1C,QAAI,SAAS,oBAAoB,UAAW,gBAAe;AAAA,EAC7D;AACF;AApVM,kBACW,WAAoC;AADrD,IAAM,mBAAN;AAsVO,IAAM,YAAY,CAAC,aAA8B,CAAC,MAAM;AAC7D,mBAAiB,YAAY,UAAU;AACzC;AAEO,IAAM,QAAQ,OAAO,WAAmB,aAAkC,UAAsB;AACrG,QAAM,WAAW,iBAAiB,YAAY;AAC9C,QAAM,SAAS,MAAM,WAAW,aAAa,KAAK;AACpD;AAEO,IAAM,OAAO,OAAO,aAAkC,UAAsB;AACjF,QAAM,WAAW,iBAAiB,YAAY;AAC9C,QAAM,SAAS,KAAK,aAAa,KAAK;AACxC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,28 +1,27 @@
|
|
|
1
|
-
type UtmParams = Record<string, string | string[]>;
|
|
2
1
|
export type BaseProps = Record<string, string>;
|
|
3
|
-
type
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
r?: string;
|
|
7
|
-
p?: BaseProps;
|
|
2
|
+
export type ViewArguments = {
|
|
3
|
+
path?: string;
|
|
4
|
+
props?: BaseProps;
|
|
8
5
|
};
|
|
9
6
|
export type Event = {
|
|
10
7
|
type: string;
|
|
11
8
|
path?: string;
|
|
12
9
|
props?: BaseProps;
|
|
13
|
-
utm?:
|
|
10
|
+
utm?: Record<string, string>;
|
|
14
11
|
referrer?: string;
|
|
15
12
|
};
|
|
13
|
+
type MinimizedEvent = {
|
|
14
|
+
t: string;
|
|
15
|
+
h?: boolean;
|
|
16
|
+
r?: string;
|
|
17
|
+
p?: BaseProps;
|
|
18
|
+
};
|
|
16
19
|
export type BodyToSend = {
|
|
17
20
|
u: string;
|
|
18
21
|
e: [MinimizedEvent];
|
|
19
|
-
qs?:
|
|
22
|
+
qs?: Record<string, string>;
|
|
20
23
|
debug?: boolean;
|
|
21
24
|
};
|
|
22
|
-
export type ViewArguments = {
|
|
23
|
-
path?: string;
|
|
24
|
-
props?: BaseProps;
|
|
25
|
-
};
|
|
26
25
|
type BaseAnalyticsConfig = {
|
|
27
26
|
collectorUrl?: string;
|
|
28
27
|
/**
|
package/package.json
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "onedollarstats",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "A lightweight, zero-dependency analytics tracker for frontend apps",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
11
13
|
},
|
|
12
14
|
"files": [
|
|
13
|
-
"dist"
|
|
15
|
+
"dist/index.js",
|
|
16
|
+
"dist/index.js.map",
|
|
17
|
+
"dist/index.d.ts",
|
|
18
|
+
"dist/types.d.ts"
|
|
14
19
|
],
|
|
15
20
|
"keywords": [
|
|
16
21
|
"analytics",
|
|
@@ -24,10 +29,29 @@
|
|
|
24
29
|
],
|
|
25
30
|
"author": "",
|
|
26
31
|
"license": "MIT",
|
|
27
|
-
"packageManager": "pnpm@10.16.1",
|
|
28
32
|
"devDependencies": {
|
|
33
|
+
"@types/jsdom": "^27.0.0",
|
|
29
34
|
"esbuild": "^0.25.10",
|
|
35
|
+
"jsdom": "^26.0.0",
|
|
36
|
+
"puppeteer": "^24.1.1",
|
|
37
|
+
"tsx": "^4.19.2",
|
|
30
38
|
"typescript": "^5.9.2",
|
|
31
39
|
"vitest": "^4.0.18"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "tsc -p tsconfig.build.json --emitDeclarationOnly && rm -rf dist/utils dist/script.d.ts dist/debug-modal.d.ts dist/globals.d.ts && esbuild src/index.ts --bundle --outfile=dist/index.js --platform=browser --format=esm --target=es2020 --sourcemap --define:DEBUG_SCRIPT_URL='\"https://assets.onedollarstats.com/stonks-debug.js\"'",
|
|
43
|
+
"bundle": "esbuild src/script.ts --bundle --minify --format=iife --outfile=build/stonks.js --define:DEBUG_SCRIPT_URL='\"https://assets.onedollarstats.com/stonks-debug.js\"'",
|
|
44
|
+
"bundle:dev": "esbuild src/script.ts --bundle --minify --format=iife --outfile=build/stonks.js --define:DEBUG_SCRIPT_URL='\"https://assets.onedollarstats.com/stonks-debug-dev.js\"'",
|
|
45
|
+
"bundle:test": "esbuild src/script.ts --bundle --format=cjs --outfile=build/stonks.js --define:DEBUG_SCRIPT_URL='\"https://assets.onedollarstats.com/stonks-debug.js\"'",
|
|
46
|
+
"bundle:debug": "esbuild src/debug-modal.ts --bundle --minify --format=iife --outfile=build/stonks-debug.js",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"test:units": "vitest run --project 'Unit Tests'",
|
|
49
|
+
"test:units:modal": "vitest run --project 'Unit Tests' src/utils/create-modal.test.ts",
|
|
50
|
+
"test:e2e:script": "vitest run --project 'Script E2E Tests'",
|
|
51
|
+
"test:e2e:package": "vitest run --project 'Package E2E Tests'",
|
|
52
|
+
"test:e2e": "vitest run --project 'Script E2E Tests' --project 'Package E2E Tests'",
|
|
53
|
+
"test:build&e2e:script": "pnpm bundle:test && FORCE_BUILD=1 COPY_TRACKER=1 vitest run --project 'Script E2E Tests'",
|
|
54
|
+
"test:build&e2e:package": "pnpm build && FORCE_BUILD=1 COPY_PACKAGE=1 vitest run --project 'Package E2E Tests'",
|
|
55
|
+
"test:all": "pnpm bundle:test && pnpm build && FORCE_BUILD=1 COPY_TRACKER=1 COPY_PACKAGE=1 vitest run"
|
|
32
56
|
}
|
|
33
|
-
}
|
|
57
|
+
}
|
package/dist/utils/bot.d.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bot & crawler detection — standalone module.
|
|
3
|
-
*
|
|
4
|
-
* Usage:
|
|
5
|
-
* import { detectBot } from '@aspect/fingerprint/bot'
|
|
6
|
-
* const result = detectBot()
|
|
7
|
-
* if (result.isBot) console.log(result.botKind, result.signals)
|
|
8
|
-
*
|
|
9
|
-
* Detects:
|
|
10
|
-
* - Known search engine crawlers (Googlebot, Bingbot, Yandex, Baidu, etc.)
|
|
11
|
-
* - Social media crawlers (Facebook, Twitter, LinkedIn, etc.)
|
|
12
|
-
* - Headless browsers (Puppeteer, Playwright, Selenium, PhantomJS)
|
|
13
|
-
* - General automation tools via navigator.webdriver and injected globals
|
|
14
|
-
* - API tampering / lie detection (prototype spoofing, proxy wrapping)
|
|
15
|
-
*
|
|
16
|
-
* Zero dependencies — can be imported and used independently of the
|
|
17
|
-
* fingerprinting library.
|
|
18
|
-
*/
|
|
19
|
-
export type BotKind = 'search_engine' | 'social_crawler' | 'headless' | 'automation' | 'library' | 'unknown_bot' | 'human';
|
|
20
|
-
export interface BotSignals {
|
|
21
|
-
/** navigator.userAgent matched a known bot pattern. */
|
|
22
|
-
userAgentBot: string | null;
|
|
23
|
-
/** navigator.webdriver is true. */
|
|
24
|
-
webdriver: boolean;
|
|
25
|
-
/** Headless browser indicators detected. */
|
|
26
|
-
headless: boolean;
|
|
27
|
-
/** Automation globals found on window. */
|
|
28
|
-
automationGlobals: string[];
|
|
29
|
-
/** Number of API lies (toString/proxy tampering) detected. */
|
|
30
|
-
liesDetected: number;
|
|
31
|
-
/** Proxy wrapping detected on native functions. */
|
|
32
|
-
hasProxy: boolean;
|
|
33
|
-
/** navigator.languages is empty or missing. */
|
|
34
|
-
missingLanguages: boolean;
|
|
35
|
-
/** navigator.plugins is empty (non-mobile). */
|
|
36
|
-
missingPlugins: boolean;
|
|
37
|
-
}
|
|
38
|
-
export interface BotDetectionResult {
|
|
39
|
-
/** True when any bot signal fires. */
|
|
40
|
-
isBot: boolean;
|
|
41
|
-
/** Classified category of the detected bot. */
|
|
42
|
-
botKind: BotKind;
|
|
43
|
-
/** Individual signal results for debugging / logging. */
|
|
44
|
-
signals: BotSignals;
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Detect whether the current browser context is a bot or crawler.
|
|
48
|
-
* Synchronous and lightweight — no async APIs needed.
|
|
49
|
-
*/
|
|
50
|
-
export declare function detectBot(): BotDetectionResult;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const createDebugModal: (debugUrl: string, analyticsUrl: string) => (message: string, success: boolean) => void;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const parseUtmParams: (urlSearchParams: URLSearchParams) => Record<string, string>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const parseProps: (propsString: string) => Record<string, string> | undefined;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const resolvePath: (pathOrProps?: string) => string;
|