@manototh/do11y 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/do11y.js +297 -123
- package/dist/do11y.min.js +1 -1
- package/examples/do11y-config.example.js +26 -10
- package/package.json +1 -1
package/dist/do11y.js
CHANGED
|
@@ -1,6 +1,72 @@
|
|
|
1
1
|
(function() {
|
|
2
2
|
//#region src/do11y.ts
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* OTel semantic convention attribute keys.
|
|
5
|
+
* Standard attrs from https://opentelemetry.io/docs/specs/semconv/.
|
|
6
|
+
* Custom do11y attrs use the `browser.do11y.*` namespace.
|
|
7
|
+
*/
|
|
8
|
+
const ATTR_SESSION_ID = "session.id";
|
|
9
|
+
const ATTR_URL_PATH = "url.path";
|
|
10
|
+
const ATTR_URL_FRAGMENT = "url.fragment";
|
|
11
|
+
const ATTR_URL_QUERY = "url.query";
|
|
12
|
+
const ATTR_DEVICE_TYPE = "device.type";
|
|
13
|
+
const ATTR_BROWSER_FAMILY = "browser.family";
|
|
14
|
+
const ATTR_BROWSER_LANGUAGE = "browser.language";
|
|
15
|
+
const ATTR_DO11Y_SESSION_PAGE_COUNT = "browser.do11y.session_page_count";
|
|
16
|
+
const ATTR_DO11Y_PAGE_TITLE = "browser.do11y.page_title";
|
|
17
|
+
const ATTR_DO11Y_VIEWPORT_CATEGORY = "browser.do11y.viewport_category";
|
|
18
|
+
const ATTR_DO11Y_TIMEZONE_OFFSET = "browser.do11y.timezone_offset";
|
|
19
|
+
const ATTR_DO11Y_REFERRER_CATEGORY = "browser.do11y.referrer_category";
|
|
20
|
+
const ATTR_DO11Y_AI_PLATFORM = "browser.do11y.ai_platform";
|
|
21
|
+
const ATTR_DO11Y_DO11Y_VERSION = "browser.do11y.version";
|
|
22
|
+
const ATTR_DO11Y_IS_FIRST_PAGE = "browser.do11y.is_first_page";
|
|
23
|
+
const ATTR_DO11Y_PREVIOUS_PATH = "browser.do11y.previous_path";
|
|
24
|
+
const ATTR_DO11Y_REFERRER_DOMAIN = "browser.do11y.referrer_domain";
|
|
25
|
+
const ATTR_DO11Y_LINK_TYPE = "browser.do11y.link.type";
|
|
26
|
+
const ATTR_DO11Y_LINK_TARGET_URL = "browser.do11y.link.target_url";
|
|
27
|
+
const ATTR_DO11Y_LINK_TARGET_DOMAIN = "browser.do11y.link.target_domain";
|
|
28
|
+
const ATTR_DO11Y_LINK_TEXT = "browser.do11y.link.text";
|
|
29
|
+
const ATTR_DO11Y_LINK_CONTEXT = "browser.do11y.link.context";
|
|
30
|
+
const ATTR_DO11Y_LINK_SECTION = "browser.do11y.link.section";
|
|
31
|
+
const ATTR_DO11Y_LINK_INDEX = "browser.do11y.link.index";
|
|
32
|
+
const ATTR_DO11Y_SCROLL_THRESHOLD = "browser.do11y.scroll.threshold";
|
|
33
|
+
const ATTR_DO11Y_SCROLL_PERCENT = "browser.do11y.scroll.percent";
|
|
34
|
+
const ATTR_DO11Y_TOTAL_TIME_SECONDS = "browser.do11y.page_exit.total_time_seconds";
|
|
35
|
+
const ATTR_DO11Y_ACTIVE_TIME_SECONDS = "browser.do11y.page_exit.active_time_seconds";
|
|
36
|
+
const ATTR_DO11Y_ENGAGEMENT_RATIO = "browser.do11y.page_exit.engagement_ratio";
|
|
37
|
+
const ATTR_DO11Y_MAX_SCROLL_DEPTH = "browser.do11y.page_exit.max_scroll_depth";
|
|
38
|
+
const ATTR_DO11Y_SEARCH_TRIGGER = "browser.do11y.search.trigger";
|
|
39
|
+
const ATTR_DO11Y_CODE_LANGUAGE = "browser.do11y.code.language";
|
|
40
|
+
const ATTR_DO11Y_CODE_SECTION = "browser.do11y.code.section";
|
|
41
|
+
const ATTR_DO11Y_CODE_INDEX = "browser.do11y.code.index";
|
|
42
|
+
const ATTR_DO11Y_SECTION_HEADING = "browser.do11y.section.heading";
|
|
43
|
+
const ATTR_DO11Y_SECTION_HEADING_LEVEL = "browser.do11y.section.heading_level";
|
|
44
|
+
const ATTR_DO11Y_SECTION_VISIBLE_SECONDS = "browser.do11y.section.visible_seconds";
|
|
45
|
+
const ATTR_DO11Y_TAB_LABEL = "browser.do11y.tab.label";
|
|
46
|
+
const ATTR_DO11Y_TAB_GROUP = "browser.do11y.tab.group";
|
|
47
|
+
const ATTR_DO11Y_TAB_IS_DEFAULT = "browser.do11y.tab.is_default";
|
|
48
|
+
const ATTR_DO11Y_TOC_HEADING = "browser.do11y.toc.heading";
|
|
49
|
+
const ATTR_DO11Y_TOC_HEADING_LEVEL = "browser.do11y.toc.heading_level";
|
|
50
|
+
const ATTR_DO11Y_TOC_POSITION = "browser.do11y.toc.position";
|
|
51
|
+
const ATTR_DO11Y_FEEDBACK_RATING = "browser.do11y.feedback.rating";
|
|
52
|
+
const ATTR_DO11Y_EXPAND_SUMMARY = "browser.do11y.expand.summary";
|
|
53
|
+
const ATTR_DO11Y_EXPAND_ACTION = "browser.do11y.expand.action";
|
|
54
|
+
const ATTR_DO11Y_EXPAND_SECTION = "browser.do11y.expand.section";
|
|
55
|
+
/**
|
|
56
|
+
* OTel event names for do11y events (browser.do11y.* namespace).
|
|
57
|
+
*/
|
|
58
|
+
const EVENT_PAGE_VIEW = "browser.do11y.page_view";
|
|
59
|
+
const EVENT_PAGE_EXIT = "browser.do11y.page_exit";
|
|
60
|
+
const EVENT_SCROLL_DEPTH = "browser.do11y.scroll_depth";
|
|
61
|
+
const EVENT_LINK_CLICK = "browser.do11y.link_click";
|
|
62
|
+
const EVENT_SEARCH_OPENED = "browser.do11y.search_opened";
|
|
63
|
+
const EVENT_CODE_COPIED = "browser.do11y.code_copied";
|
|
64
|
+
const EVENT_SECTION_VISIBLE = "browser.do11y.section_visible";
|
|
65
|
+
const EVENT_TAB_SWITCH = "browser.do11y.tab_switch";
|
|
66
|
+
const EVENT_TOC_CLICK = "browser.do11y.toc_click";
|
|
67
|
+
const EVENT_FEEDBACK = "browser.do11y.feedback";
|
|
68
|
+
const EVENT_EXPAND_COLLAPSE = "browser.do11y.expand_collapse";
|
|
69
|
+
const VERSION = "0.1.0";
|
|
4
70
|
const _alreadyLoaded = !!window.__do11yInitialized;
|
|
5
71
|
window.__do11yInitialized = true;
|
|
6
72
|
const config = {
|
|
@@ -8,8 +74,14 @@
|
|
|
8
74
|
supabaseUrl: "",
|
|
9
75
|
supabaseKey: "",
|
|
10
76
|
supabaseTable: "do11y_events",
|
|
11
|
-
|
|
12
|
-
|
|
77
|
+
endpoint: "",
|
|
78
|
+
headers: {},
|
|
79
|
+
bodyTransform: void 0,
|
|
80
|
+
otelSdkEndpoint: "",
|
|
81
|
+
otelSdkHeaders: {},
|
|
82
|
+
otelSdkServiceName: "do11y",
|
|
83
|
+
otelSdkResourceAttributes: {},
|
|
84
|
+
otelSdkCdnUrl: "https://esm.sh/",
|
|
13
85
|
debug: false,
|
|
14
86
|
flushInterval: 5e3,
|
|
15
87
|
maxBatchSize: 10,
|
|
@@ -42,7 +114,8 @@
|
|
|
42
114
|
codeBlockSelector: null,
|
|
43
115
|
navigationSelector: null,
|
|
44
116
|
footerSelector: null,
|
|
45
|
-
contentSelector: null
|
|
117
|
+
contentSelector: null,
|
|
118
|
+
useOtelBrowserInstrumentations: false
|
|
46
119
|
};
|
|
47
120
|
const FRAMEWORK_PRESETS = {
|
|
48
121
|
mintlify: {
|
|
@@ -175,12 +248,6 @@
|
|
|
175
248
|
return null;
|
|
176
249
|
}
|
|
177
250
|
}
|
|
178
|
-
/** Paths that are not documentation pages (for example tracking pixels). */
|
|
179
|
-
const ENGAGEMENT_EXCLUDED_PATH_PREFIXES = ["/pixel/"];
|
|
180
|
-
function isEngagementExcludedPath(path) {
|
|
181
|
-
const p = path ?? window.location.pathname;
|
|
182
|
-
return ENGAGEMENT_EXCLUDED_PATH_PREFIXES.some((prefix) => p.startsWith(prefix));
|
|
183
|
-
}
|
|
184
251
|
function getElementClassName(el) {
|
|
185
252
|
if (typeof el.className === "string") return el.className;
|
|
186
253
|
const svgClass = el.className;
|
|
@@ -307,11 +374,11 @@
|
|
|
307
374
|
}
|
|
308
375
|
function getBrowserContext() {
|
|
309
376
|
return {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
377
|
+
[ATTR_DO11Y_VIEWPORT_CATEGORY]: categorizeViewport(),
|
|
378
|
+
[ATTR_BROWSER_FAMILY]: getBrowserFamily(),
|
|
379
|
+
[ATTR_DEVICE_TYPE]: getDeviceType(),
|
|
380
|
+
[ATTR_BROWSER_LANGUAGE]: (navigator.language || "").split("-")[0] || "unknown",
|
|
381
|
+
[ATTR_DO11Y_TIMEZONE_OFFSET]: (/* @__PURE__ */ new Date()).getTimezoneOffset() / 60
|
|
315
382
|
};
|
|
316
383
|
}
|
|
317
384
|
function categorizeViewport() {
|
|
@@ -460,38 +527,47 @@
|
|
|
460
527
|
}
|
|
461
528
|
function getPageInfo() {
|
|
462
529
|
return {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
530
|
+
[ATTR_URL_PATH]: window.location.pathname,
|
|
531
|
+
[ATTR_URL_FRAGMENT]: window.location.hash || null,
|
|
532
|
+
[ATTR_URL_QUERY]: window.location.search ? "has_params" : null,
|
|
533
|
+
[ATTR_DO11Y_PAGE_TITLE]: sanitizeText(document.title, 150)
|
|
467
534
|
};
|
|
468
535
|
}
|
|
469
536
|
let eventQueue = [];
|
|
470
537
|
let flushTimeout = null;
|
|
471
538
|
const lastEventTime = {};
|
|
472
539
|
let isDisabled = false;
|
|
473
|
-
function queueEvent(
|
|
540
|
+
function queueEvent(eventName, eventData) {
|
|
474
541
|
if (isDisabled) return;
|
|
475
542
|
const now = Date.now();
|
|
476
|
-
if (config.rateLimitMs > 0 && lastEventTime[
|
|
477
|
-
if (now - lastEventTime[
|
|
478
|
-
if (config.debug) console.log("[Do11y] Rate limited:",
|
|
543
|
+
if (config.rateLimitMs > 0 && lastEventTime[eventName]) {
|
|
544
|
+
if (now - lastEventTime[eventName] < config.rateLimitMs) {
|
|
545
|
+
if (config.debug) console.log("[Do11y] Rate limited:", eventName);
|
|
479
546
|
return;
|
|
480
547
|
}
|
|
481
548
|
}
|
|
482
|
-
lastEventTime[
|
|
549
|
+
lastEventTime[eventName] = now;
|
|
483
550
|
const session = getSession();
|
|
484
551
|
const event = {
|
|
485
552
|
_time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
553
|
+
eventName,
|
|
554
|
+
[ATTR_DO11Y_DO11Y_VERSION]: VERSION,
|
|
555
|
+
[ATTR_SESSION_ID]: session.id,
|
|
556
|
+
[ATTR_DO11Y_SESSION_PAGE_COUNT]: session.pageCount,
|
|
490
557
|
...getPageInfo(),
|
|
491
558
|
...getBrowserContext(),
|
|
492
559
|
...eventData
|
|
493
560
|
};
|
|
494
|
-
if (config.debug) console.log("[Do11y] Event queued:", event);
|
|
561
|
+
if (config.debug) console.log("[Do11y] Event queued:", eventName, event);
|
|
562
|
+
if (config.destination === "otlp" && _otelLogger) {
|
|
563
|
+
_otelLogger.emit({
|
|
564
|
+
eventName,
|
|
565
|
+
severityNumber: 9,
|
|
566
|
+
attributes: event,
|
|
567
|
+
body: ""
|
|
568
|
+
});
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
495
571
|
eventQueue.push(event);
|
|
496
572
|
if (eventQueue.length > 100) {
|
|
497
573
|
eventQueue = eventQueue.slice(-100);
|
|
@@ -504,6 +580,7 @@
|
|
|
504
580
|
if (flushTimeout) return;
|
|
505
581
|
flushTimeout = setTimeout(flush, config.flushInterval);
|
|
506
582
|
}
|
|
583
|
+
let _otelLogger = null;
|
|
507
584
|
function validateSupabaseUrl(url) {
|
|
508
585
|
try {
|
|
509
586
|
const parsed = new URL(url);
|
|
@@ -514,7 +591,7 @@
|
|
|
514
591
|
return false;
|
|
515
592
|
}
|
|
516
593
|
}
|
|
517
|
-
function
|
|
594
|
+
function validateEndpoint(url) {
|
|
518
595
|
try {
|
|
519
596
|
const parsed = new URL(url);
|
|
520
597
|
if (parsed.protocol !== "https:") return false;
|
|
@@ -547,37 +624,90 @@
|
|
|
547
624
|
return true;
|
|
548
625
|
}
|
|
549
626
|
if (config.destination === "http") {
|
|
550
|
-
if (!config.
|
|
627
|
+
if (!config.endpoint) {
|
|
551
628
|
if (config.debug) console.warn("[Do11y] No HTTP endpoint configured");
|
|
552
629
|
return false;
|
|
553
630
|
}
|
|
554
|
-
if (!
|
|
631
|
+
if (!validateEndpoint(config.endpoint)) {
|
|
555
632
|
if (config.debug) console.warn("[Do11y] Invalid HTTP endpoint. Must be HTTPS and not a private address.");
|
|
556
633
|
return false;
|
|
557
634
|
}
|
|
558
635
|
return true;
|
|
559
636
|
}
|
|
637
|
+
if (config.destination === "otlp") {
|
|
638
|
+
if (!config.otelSdkEndpoint) {
|
|
639
|
+
if (config.debug) console.warn("[Do11y] No OTLP endpoint configured");
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
initOtelSdk().catch((err) => {
|
|
643
|
+
if (config.debug) console.warn("[Do11y] OTel SDK initialization failed:", err);
|
|
644
|
+
});
|
|
645
|
+
return true;
|
|
646
|
+
}
|
|
560
647
|
if (config.debug) console.warn("[Do11y] Unknown destination:", config.destination);
|
|
561
648
|
return false;
|
|
562
649
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
650
|
+
/**
|
|
651
|
+
* Dynamically import the OTel Browser SDK and set up the LoggerProvider.
|
|
652
|
+
* Only called when destination is 'otlp'.
|
|
653
|
+
*/
|
|
654
|
+
async function initOtelSdk() {
|
|
655
|
+
if (_otelLogger) return;
|
|
656
|
+
const cdnBase = config.otelSdkCdnUrl.replace(/\/+$/, "") + "/";
|
|
657
|
+
const apiLogs = await import(
|
|
658
|
+
/* @vite-ignore */
|
|
659
|
+
`${cdnBase}@opentelemetry/api-logs`
|
|
660
|
+
);
|
|
661
|
+
const sdkLogs = await import(
|
|
662
|
+
/* @vite-ignore */
|
|
663
|
+
`${cdnBase}@opentelemetry/sdk-logs`
|
|
664
|
+
);
|
|
665
|
+
const otlpExporter = await import(
|
|
666
|
+
/* @vite-ignore */
|
|
667
|
+
`${cdnBase}@opentelemetry/exporter-logs-otlp-http`
|
|
668
|
+
);
|
|
669
|
+
const resourceAttrs = {
|
|
670
|
+
"service.name": config.otelSdkServiceName || "do11y",
|
|
671
|
+
"service.version": VERSION,
|
|
672
|
+
"telemetry.sdk.name": "do11y",
|
|
673
|
+
"telemetry.sdk.language": "webjs",
|
|
674
|
+
"telemetry.sdk.version": VERSION,
|
|
675
|
+
...config.otelSdkResourceAttributes
|
|
573
676
|
};
|
|
677
|
+
const loggerProvider = new sdkLogs.LoggerProvider({
|
|
678
|
+
resource: { attributes: resourceAttrs },
|
|
679
|
+
processors: [new sdkLogs.BatchLogRecordProcessor({ exporter: new otlpExporter.OTLPLogExporter({
|
|
680
|
+
url: config.otelSdkEndpoint.replace(/\/$/, "") + "/v1/logs",
|
|
681
|
+
headers: config.otelSdkHeaders
|
|
682
|
+
}) })]
|
|
683
|
+
});
|
|
684
|
+
apiLogs.logs.setGlobalLoggerProvider(loggerProvider);
|
|
685
|
+
_otelLogger = loggerProvider.getLogger("do11y");
|
|
686
|
+
if (config.debug) console.log("[Do11y] OTel SDK initialized with endpoint:", config.otelSdkEndpoint);
|
|
687
|
+
}
|
|
688
|
+
function buildRequest(events) {
|
|
689
|
+
if (config.destination === "supabase") {
|
|
690
|
+
const url = config.supabaseUrl.replace(/\/$/, "") + "/rest/v1/" + config.supabaseTable;
|
|
691
|
+
const bodyTransform = config.bodyTransform ?? ((evts) => evts.map((e) => ({ payload: e })));
|
|
692
|
+
return {
|
|
693
|
+
url,
|
|
694
|
+
headers: {
|
|
695
|
+
"apikey": config.supabaseKey,
|
|
696
|
+
"Authorization": "Bearer " + config.supabaseKey,
|
|
697
|
+
"Content-Type": "application/json",
|
|
698
|
+
"Prefer": "return=minimal"
|
|
699
|
+
},
|
|
700
|
+
body: JSON.stringify(bodyTransform(events))
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
const bodyTransform = config.bodyTransform ?? ((evts) => evts);
|
|
574
704
|
return {
|
|
575
|
-
url: config.
|
|
705
|
+
url: config.endpoint,
|
|
576
706
|
headers: {
|
|
577
707
|
"Content-Type": "application/json",
|
|
578
|
-
...config.
|
|
708
|
+
...config.headers
|
|
579
709
|
},
|
|
580
|
-
body: JSON.stringify(events)
|
|
710
|
+
body: JSON.stringify(bodyTransform(events))
|
|
581
711
|
};
|
|
582
712
|
}
|
|
583
713
|
function flush(retriesLeft) {
|
|
@@ -592,12 +722,25 @@
|
|
|
592
722
|
eventQueue = [];
|
|
593
723
|
sendEvents(buildRequest(events), events, retries);
|
|
594
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Check whether a request URL is cross-origin relative to the current page.
|
|
727
|
+
*/
|
|
728
|
+
function isCrossOrigin(url) {
|
|
729
|
+
try {
|
|
730
|
+
return new URL(url).origin !== window.location.origin;
|
|
731
|
+
} catch {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
595
735
|
function sendEvents(req, events, retriesLeft) {
|
|
736
|
+
const crossOrigin = isCrossOrigin(req.url);
|
|
737
|
+
if (config.debug && crossOrigin) console.log("[Do11y] Cross-origin request to", new URL(req.url).origin, "- requires CORS headers on the server");
|
|
596
738
|
fetch(req.url, {
|
|
597
739
|
method: "POST",
|
|
598
740
|
headers: req.headers,
|
|
599
741
|
body: req.body,
|
|
600
|
-
keepalive: true
|
|
742
|
+
keepalive: true,
|
|
743
|
+
mode: crossOrigin ? "cors" : "same-origin"
|
|
601
744
|
}).then((response) => {
|
|
602
745
|
if (response.ok) {
|
|
603
746
|
if (config.debug) console.log("[Do11y] Flushed", events.length, "events");
|
|
@@ -612,19 +755,29 @@
|
|
|
612
755
|
return;
|
|
613
756
|
}
|
|
614
757
|
if (config.debug) response.text().then((text) => {
|
|
615
|
-
|
|
758
|
+
const msg = `[Do11y] Ingest failed: ${response.status}`;
|
|
759
|
+
if (response.status === 0 && response.type === "opaque") console.error(msg, "- CORS error: server did not return Access-Control-Allow-Origin");
|
|
760
|
+
else console.error(msg, text);
|
|
616
761
|
}).catch(() => {});
|
|
617
762
|
}).catch((err) => {
|
|
618
763
|
if (retriesLeft > 0) {
|
|
619
|
-
if (config.debug)
|
|
764
|
+
if (config.debug) {
|
|
765
|
+
const hint = crossOrigin ? " (this may be a CORS issue — try using an OTel Collector proxy)" : "";
|
|
766
|
+
console.log("[Do11y] Network error, retrying:", err.message + hint);
|
|
767
|
+
}
|
|
620
768
|
eventQueue = events.concat(eventQueue);
|
|
621
769
|
setTimeout(() => {
|
|
622
770
|
flush(retriesLeft - 1);
|
|
623
771
|
}, config.retryDelay * (config.maxRetries - retriesLeft + 1));
|
|
624
|
-
} else if (config.debug) console.error("[Do11y] Failed to send events:", err);
|
|
772
|
+
} else if (config.debug) console.error("[Do11y] Failed to send events:", err.message);
|
|
625
773
|
});
|
|
626
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Synchronous flush used on `beforeunload`. For OTLP mode the SDK
|
|
777
|
+
* handles flush on its own; for HTTP/Supabase we use fetch with keepalive.
|
|
778
|
+
*/
|
|
627
779
|
function flushSync() {
|
|
780
|
+
if (config.destination === "otlp") return;
|
|
628
781
|
if (eventQueue.length === 0) return;
|
|
629
782
|
if (!validateConfig()) return;
|
|
630
783
|
const events = eventQueue;
|
|
@@ -649,12 +802,12 @@
|
|
|
649
802
|
session.aiPlatform = referrerInfo.aiPlatform;
|
|
650
803
|
saveSession(session);
|
|
651
804
|
}
|
|
652
|
-
queueEvent(
|
|
653
|
-
referrerDomain,
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
805
|
+
queueEvent(EVENT_PAGE_VIEW, {
|
|
806
|
+
[ATTR_DO11Y_REFERRER_DOMAIN]: referrerDomain,
|
|
807
|
+
[ATTR_DO11Y_REFERRER_CATEGORY]: referrerInfo.referrerCategory,
|
|
808
|
+
[ATTR_DO11Y_AI_PLATFORM]: referrerInfo.aiPlatform,
|
|
809
|
+
[ATTR_DO11Y_IS_FIRST_PAGE]: session.pageCount === 1,
|
|
810
|
+
[ATTR_DO11Y_PREVIOUS_PATH]: session.pageSequence.length > 1 ? session.pageSequence[session.pageSequence.length - 2].path : null
|
|
658
811
|
});
|
|
659
812
|
}
|
|
660
813
|
function setupLinkTracking() {
|
|
@@ -679,14 +832,14 @@
|
|
|
679
832
|
} catch {}
|
|
680
833
|
if (linkType === "internal" && !config.trackInternalLinks) return;
|
|
681
834
|
if (linkType === "external" && !config.trackOutboundLinks) return;
|
|
682
|
-
queueEvent(
|
|
683
|
-
linkType,
|
|
684
|
-
|
|
685
|
-
targetDomain,
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
835
|
+
queueEvent(EVENT_LINK_CLICK, {
|
|
836
|
+
[ATTR_DO11Y_LINK_TYPE]: linkType,
|
|
837
|
+
[ATTR_DO11Y_LINK_TARGET_URL]: href,
|
|
838
|
+
[ATTR_DO11Y_LINK_TARGET_DOMAIN]: targetDomain,
|
|
839
|
+
[ATTR_DO11Y_LINK_TEXT]: sanitizeText(link.textContent, 100),
|
|
840
|
+
[ATTR_DO11Y_LINK_CONTEXT]: getLinkContext(link),
|
|
841
|
+
[ATTR_DO11Y_LINK_SECTION]: sanitizeText(getNearestHeading(link), 100),
|
|
842
|
+
[ATTR_DO11Y_LINK_INDEX]: getLinkIndex(link, href)
|
|
690
843
|
});
|
|
691
844
|
flush();
|
|
692
845
|
}, true);
|
|
@@ -774,7 +927,6 @@
|
|
|
774
927
|
* the content without scrolling.
|
|
775
928
|
*/
|
|
776
929
|
function checkScrollDepth() {
|
|
777
|
-
if (isEngagementExcludedPath()) return;
|
|
778
930
|
let scrollTop;
|
|
779
931
|
let totalHeight;
|
|
780
932
|
let viewportHeight;
|
|
@@ -792,9 +944,9 @@
|
|
|
792
944
|
config.scrollThresholds.forEach((threshold) => {
|
|
793
945
|
if (!trackedScrollDepths.has(threshold)) {
|
|
794
946
|
trackedScrollDepths.add(threshold);
|
|
795
|
-
queueEvent(
|
|
796
|
-
threshold,
|
|
797
|
-
|
|
947
|
+
queueEvent(EVENT_SCROLL_DEPTH, {
|
|
948
|
+
[ATTR_DO11Y_SCROLL_THRESHOLD]: threshold,
|
|
949
|
+
[ATTR_DO11Y_SCROLL_PERCENT]: 100
|
|
798
950
|
});
|
|
799
951
|
}
|
|
800
952
|
});
|
|
@@ -804,9 +956,9 @@
|
|
|
804
956
|
config.scrollThresholds.forEach((threshold) => {
|
|
805
957
|
if (scrollPercent >= threshold && !trackedScrollDepths.has(threshold)) {
|
|
806
958
|
trackedScrollDepths.add(threshold);
|
|
807
|
-
queueEvent(
|
|
808
|
-
threshold,
|
|
809
|
-
scrollPercent
|
|
959
|
+
queueEvent(EVENT_SCROLL_DEPTH, {
|
|
960
|
+
[ATTR_DO11Y_SCROLL_THRESHOLD]: threshold,
|
|
961
|
+
[ATTR_DO11Y_SCROLL_PERCENT]: scrollPercent
|
|
810
962
|
});
|
|
811
963
|
}
|
|
812
964
|
});
|
|
@@ -816,7 +968,6 @@
|
|
|
816
968
|
let totalActiveTime = 0;
|
|
817
969
|
let isPageVisible = true;
|
|
818
970
|
function emitPageExit() {
|
|
819
|
-
if (isEngagementExcludedPath()) return;
|
|
820
971
|
if (isPageVisible) totalActiveTime += Date.now() - lastActivityTime;
|
|
821
972
|
const totalTime = Date.now() - pageLoadTime;
|
|
822
973
|
const engagementRatio = totalTime > 0 ? totalActiveTime / totalTime : 0;
|
|
@@ -826,13 +977,13 @@
|
|
|
826
977
|
});
|
|
827
978
|
flushVisibleSections();
|
|
828
979
|
const session = getSession();
|
|
829
|
-
queueEvent(
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
980
|
+
queueEvent(EVENT_PAGE_EXIT, {
|
|
981
|
+
[ATTR_DO11Y_TOTAL_TIME_SECONDS]: Math.round(totalTime / 1e3),
|
|
982
|
+
[ATTR_DO11Y_ACTIVE_TIME_SECONDS]: Math.round(totalActiveTime / 1e3),
|
|
983
|
+
[ATTR_DO11Y_ENGAGEMENT_RATIO]: Math.round(engagementRatio * 100) / 100,
|
|
984
|
+
[ATTR_DO11Y_MAX_SCROLL_DEPTH]: maxScroll,
|
|
985
|
+
[ATTR_DO11Y_REFERRER_CATEGORY]: session.referrerCategory,
|
|
986
|
+
[ATTR_DO11Y_AI_PLATFORM]: session.aiPlatform
|
|
836
987
|
});
|
|
837
988
|
}
|
|
838
989
|
function setupEngagementTracking() {
|
|
@@ -854,10 +1005,10 @@
|
|
|
854
1005
|
}
|
|
855
1006
|
function setupSearchTracking() {
|
|
856
1007
|
document.addEventListener("click", (e) => {
|
|
857
|
-
if (e.target.closest(config.searchSelector)) queueEvent(
|
|
1008
|
+
if (e.target.closest(config.searchSelector)) queueEvent(EVENT_SEARCH_OPENED, {});
|
|
858
1009
|
}, true);
|
|
859
1010
|
document.addEventListener("keydown", (e) => {
|
|
860
|
-
if ((e.metaKey || e.ctrlKey) && e.key === "k") queueEvent(
|
|
1011
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "k") queueEvent(EVENT_SEARCH_OPENED, { [ATTR_DO11Y_SEARCH_TRIGGER]: "keyboard" });
|
|
861
1012
|
});
|
|
862
1013
|
}
|
|
863
1014
|
function getCodeBlockIndex(codeBlock) {
|
|
@@ -873,10 +1024,11 @@
|
|
|
873
1024
|
const copyButton = e.target.closest(config.copyButtonSelector);
|
|
874
1025
|
if (copyButton) {
|
|
875
1026
|
const codeBlock = copyButton.closest("[class*=\"language-\"], [language]") ?? copyButton.closest(config.codeBlockSelector) ?? copyButton.closest(".expressive-code")?.querySelector("pre") ?? copyButton.closest("div, section")?.querySelector("pre") ?? copyButton.parentElement?.querySelector("pre") ?? null;
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
1027
|
+
const language = extractCodeLanguage((codeBlock ? codeBlock.tagName === "PRE" ? codeBlock.querySelector("code") : codeBlock.querySelector("code[class*=\"language-\"], code[language]") ?? codeBlock.querySelector("code") : null) ?? codeBlock ?? copyButton);
|
|
1028
|
+
queueEvent(EVENT_CODE_COPIED, {
|
|
1029
|
+
[ATTR_DO11Y_CODE_LANGUAGE]: language,
|
|
1030
|
+
[ATTR_DO11Y_CODE_SECTION]: sanitizeText(getNearestHeading(codeBlock ?? copyButton), 100),
|
|
1031
|
+
[ATTR_DO11Y_CODE_INDEX]: getCodeBlockIndex(codeBlock)
|
|
880
1032
|
});
|
|
881
1033
|
}
|
|
882
1034
|
}, true);
|
|
@@ -900,10 +1052,11 @@
|
|
|
900
1052
|
if (sectionTimers[id] && !sectionTimers[id].reported) {
|
|
901
1053
|
const elapsed = Date.now() - sectionTimers[id].start;
|
|
902
1054
|
if (elapsed >= threshold) {
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1055
|
+
const heading = entry.target.textContent?.trim() ?? "";
|
|
1056
|
+
queueEvent(EVENT_SECTION_VISIBLE, {
|
|
1057
|
+
[ATTR_DO11Y_SECTION_HEADING]: sanitizeText(heading, 100),
|
|
1058
|
+
[ATTR_DO11Y_SECTION_HEADING_LEVEL]: parseInt(entry.target.tagName.charAt(1), 10),
|
|
1059
|
+
[ATTR_DO11Y_SECTION_VISIBLE_SECONDS]: Math.round(elapsed / 1e3)
|
|
907
1060
|
});
|
|
908
1061
|
sectionTimers[id].reported = true;
|
|
909
1062
|
}
|
|
@@ -932,10 +1085,10 @@
|
|
|
932
1085
|
if (elapsed >= threshold) {
|
|
933
1086
|
const escapedId = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(id) : id.replace(/["\\]/g, "\\$&");
|
|
934
1087
|
const el = document.querySelector("[data-do11y-section-id=\"" + escapedId + "\"]");
|
|
935
|
-
if (el) queueEvent(
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
1088
|
+
if (el) queueEvent(EVENT_SECTION_VISIBLE, {
|
|
1089
|
+
[ATTR_DO11Y_SECTION_HEADING]: sanitizeText(el.textContent?.trim() ?? "", 100),
|
|
1090
|
+
[ATTR_DO11Y_SECTION_HEADING_LEVEL]: parseInt(el.tagName.charAt(1), 10),
|
|
1091
|
+
[ATTR_DO11Y_SECTION_VISIBLE_SECONDS]: Math.round(elapsed / 1e3)
|
|
939
1092
|
});
|
|
940
1093
|
}
|
|
941
1094
|
}
|
|
@@ -953,10 +1106,11 @@
|
|
|
953
1106
|
if (tab.getAttribute("aria-selected") === "true" || tab.classList.contains("active") || tab.classList.contains("is-active")) return;
|
|
954
1107
|
const label = sanitizeText(tab.textContent, 50);
|
|
955
1108
|
if (!label) return;
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1109
|
+
const section = sanitizeText(getNearestHeading(tab), 100);
|
|
1110
|
+
queueEvent(EVENT_TAB_SWITCH, {
|
|
1111
|
+
[ATTR_DO11Y_TAB_LABEL]: label,
|
|
1112
|
+
[ATTR_DO11Y_TAB_GROUP]: section,
|
|
1113
|
+
[ATTR_DO11Y_TAB_IS_DEFAULT]: false
|
|
960
1114
|
});
|
|
961
1115
|
});
|
|
962
1116
|
}
|
|
@@ -983,10 +1137,10 @@
|
|
|
983
1137
|
tocPosition = i + 1;
|
|
984
1138
|
break;
|
|
985
1139
|
}
|
|
986
|
-
queueEvent(
|
|
987
|
-
|
|
988
|
-
headingLevel,
|
|
989
|
-
tocPosition
|
|
1140
|
+
queueEvent(EVENT_TOC_CLICK, {
|
|
1141
|
+
[ATTR_DO11Y_TOC_HEADING]: headingText,
|
|
1142
|
+
[ATTR_DO11Y_TOC_HEADING_LEVEL]: headingLevel,
|
|
1143
|
+
[ATTR_DO11Y_TOC_POSITION]: tocPosition
|
|
990
1144
|
});
|
|
991
1145
|
}, true);
|
|
992
1146
|
}
|
|
@@ -1006,7 +1160,7 @@
|
|
|
1006
1160
|
else if (/\byes\b|👍|thumbs.?up|helpful/i.test(buttonText + " " + ariaLabel + " " + titleAttr)) rating = "yes";
|
|
1007
1161
|
else if (/\bno\b|👎|thumbs.?down|not.?helpful/i.test(buttonText + " " + ariaLabel + " " + titleAttr)) rating = "no";
|
|
1008
1162
|
if (!rating) return;
|
|
1009
|
-
queueEvent(
|
|
1163
|
+
queueEvent(EVENT_FEEDBACK, { [ATTR_DO11Y_FEEDBACK_RATING]: rating });
|
|
1010
1164
|
});
|
|
1011
1165
|
}
|
|
1012
1166
|
function setupExpandCollapseTracking() {
|
|
@@ -1015,10 +1169,11 @@
|
|
|
1015
1169
|
const details = e.target;
|
|
1016
1170
|
if (details.tagName !== "DETAILS") return;
|
|
1017
1171
|
const summary = details.querySelector("summary");
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1172
|
+
const label = sanitizeText(summary ? summary.textContent : "", 100);
|
|
1173
|
+
queueEvent(EVENT_EXPAND_COLLAPSE, {
|
|
1174
|
+
[ATTR_DO11Y_EXPAND_SUMMARY]: label,
|
|
1175
|
+
[ATTR_DO11Y_EXPAND_ACTION]: details.open ? "expand" : "collapse",
|
|
1176
|
+
[ATTR_DO11Y_EXPAND_SECTION]: sanitizeText(getNearestHeading(details), 100)
|
|
1022
1177
|
});
|
|
1023
1178
|
}, true);
|
|
1024
1179
|
document.addEventListener("click", (e) => {
|
|
@@ -1027,10 +1182,10 @@
|
|
|
1027
1182
|
if (trigger.closest("details")) return;
|
|
1028
1183
|
if (trigger.closest("nav, [role=\"navigation\"], header")) return;
|
|
1029
1184
|
const wasExpanded = trigger.getAttribute("aria-expanded") === "true";
|
|
1030
|
-
queueEvent(
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1185
|
+
queueEvent(EVENT_EXPAND_COLLAPSE, {
|
|
1186
|
+
[ATTR_DO11Y_EXPAND_SUMMARY]: sanitizeText(trigger.textContent, 100),
|
|
1187
|
+
[ATTR_DO11Y_EXPAND_ACTION]: wasExpanded ? "collapse" : "expand",
|
|
1188
|
+
[ATTR_DO11Y_EXPAND_SECTION]: sanitizeText(getNearestHeading(trigger), 100)
|
|
1034
1189
|
});
|
|
1035
1190
|
});
|
|
1036
1191
|
}
|
|
@@ -1042,7 +1197,7 @@
|
|
|
1042
1197
|
const metaDestination = document.querySelector("meta[name=\"do11y-destination\"]");
|
|
1043
1198
|
if (metaDestination) {
|
|
1044
1199
|
const dest = metaDestination.getAttribute("content");
|
|
1045
|
-
if (dest === "supabase" || dest === "http") config.destination = dest;
|
|
1200
|
+
if (dest === "supabase" || dest === "http" || dest === "otlp") config.destination = dest;
|
|
1046
1201
|
}
|
|
1047
1202
|
const metaUrl = document.querySelector("meta[name=\"do11y-url\"]");
|
|
1048
1203
|
if (metaUrl) config.supabaseUrl = metaUrl.getAttribute("content") ?? config.supabaseUrl;
|
|
@@ -1050,8 +1205,15 @@
|
|
|
1050
1205
|
if (metaKey) config.supabaseKey = metaKey.getAttribute("content") ?? config.supabaseKey;
|
|
1051
1206
|
const metaTable = document.querySelector("meta[name=\"do11y-table\"]");
|
|
1052
1207
|
if (metaTable) config.supabaseTable = metaTable.getAttribute("content") ?? config.supabaseTable;
|
|
1053
|
-
const
|
|
1054
|
-
if (
|
|
1208
|
+
const metaEndpoint = document.querySelector("meta[name=\"do11y-endpoint\"]");
|
|
1209
|
+
if (metaEndpoint) config.endpoint = metaEndpoint.getAttribute("content") ?? config.endpoint;
|
|
1210
|
+
const metaOtlpEndpoint = document.querySelector("meta[name=\"do11y-otlp-endpoint\"]");
|
|
1211
|
+
if (metaOtlpEndpoint) config.otelSdkEndpoint = metaOtlpEndpoint.getAttribute("content") ?? config.otelSdkEndpoint;
|
|
1212
|
+
const metaOtlpHeaders = document.querySelector("meta[name=\"do11y-otlp-headers\"]");
|
|
1213
|
+
if (metaOtlpHeaders) try {
|
|
1214
|
+
const parsed = JSON.parse(metaOtlpHeaders.getAttribute("content") ?? "{}");
|
|
1215
|
+
if (typeof parsed === "object" && parsed !== null) config.otelSdkHeaders = parsed;
|
|
1216
|
+
} catch {}
|
|
1055
1217
|
const metaDebug = document.querySelector("meta[name=\"do11y-debug\"]");
|
|
1056
1218
|
if (metaDebug && metaDebug.getAttribute("content") === "true") config.debug = true;
|
|
1057
1219
|
const metaDomains = document.querySelector("meta[name=\"do11y-domains\"]");
|
|
@@ -1061,23 +1223,30 @@
|
|
|
1061
1223
|
}
|
|
1062
1224
|
const metaFramework = document.querySelector("meta[name=\"do11y-framework\"]");
|
|
1063
1225
|
if (metaFramework) config.framework = metaFramework.getAttribute("content") ?? config.framework;
|
|
1226
|
+
const metaUseOtelInstrumentations = document.querySelector("meta[name=\"do11y-use-otel-instrumentations\"]");
|
|
1227
|
+
if (metaUseOtelInstrumentations && metaUseOtelInstrumentations.getAttribute("content") === "true") config.useOtelBrowserInstrumentations = true;
|
|
1064
1228
|
applyFrameworkSelectors();
|
|
1065
|
-
if (config.debug)
|
|
1066
|
-
destination: config.destination
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1229
|
+
if (config.debug) {
|
|
1230
|
+
const hasCreds = config.destination === "supabase" ? !!config.supabaseKey : config.destination === "otlp" ? !!config.otelSdkEndpoint : !!config.endpoint;
|
|
1231
|
+
console.log("[Do11y] Initializing with config:", {
|
|
1232
|
+
destination: config.destination,
|
|
1233
|
+
hasCredentials: hasCreds,
|
|
1234
|
+
framework: config.framework,
|
|
1235
|
+
allowedDomains: config.allowedDomains,
|
|
1236
|
+
respectDNT: config.respectDNT
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1072
1239
|
if (shouldDisableTracking()) {
|
|
1073
1240
|
isDisabled = true;
|
|
1074
1241
|
if (config.debug) console.log("[Do11y] Tracking disabled");
|
|
1075
1242
|
return;
|
|
1076
1243
|
}
|
|
1077
|
-
if (!(config.destination === "supabase" ? !!config.supabaseKey : !!config.
|
|
1244
|
+
if (!(config.destination === "supabase" ? !!config.supabaseKey : config.destination === "otlp" ? !!config.otelSdkEndpoint : !!config.endpoint)) {
|
|
1078
1245
|
if (config.debug) {
|
|
1079
1246
|
console.warn("[Do11y] No destination configured. Events will not be sent.");
|
|
1080
|
-
console.warn("[Do11y] Add <meta name=\"do11y-url\"> and <meta name=\"do11y-key\"> to enable.");
|
|
1247
|
+
if (config.destination === "supabase") console.warn("[Do11y] Add <meta name=\"do11y-url\"> and <meta name=\"do11y-key\"> to enable.");
|
|
1248
|
+
else if (config.destination === "otlp") console.warn("[Do11y] Add <meta name=\"do11y-otlp-endpoint\"> to enable.");
|
|
1249
|
+
else console.warn("[Do11y] Add <meta name=\"do11y-endpoint\"> to enable.");
|
|
1081
1250
|
}
|
|
1082
1251
|
}
|
|
1083
1252
|
trackPageView();
|
|
@@ -1148,13 +1317,18 @@
|
|
|
1148
1317
|
window.Do11y = window.Do11y ?? {
|
|
1149
1318
|
getConfig: () => ({
|
|
1150
1319
|
destination: config.destination,
|
|
1151
|
-
hasCredentials: config.destination === "supabase" ? !!config.supabaseKey : !!config.
|
|
1320
|
+
hasCredentials: config.destination === "supabase" ? !!config.supabaseKey : config.destination === "otlp" ? !!config.otelSdkEndpoint : !!config.endpoint,
|
|
1152
1321
|
isDisabled,
|
|
1153
1322
|
allowedDomains: config.allowedDomains,
|
|
1154
1323
|
respectDNT: config.respectDNT
|
|
1155
1324
|
}),
|
|
1156
1325
|
flush,
|
|
1157
|
-
isEnabled: () =>
|
|
1326
|
+
isEnabled: () => {
|
|
1327
|
+
if (isDisabled) return false;
|
|
1328
|
+
if (config.destination === "supabase") return !!config.supabaseKey;
|
|
1329
|
+
if (config.destination === "otlp") return !!config.otelSdkEndpoint;
|
|
1330
|
+
return !!config.endpoint;
|
|
1331
|
+
},
|
|
1158
1332
|
getQueueSize: () => eventQueue.length,
|
|
1159
1333
|
version: VERSION
|
|
1160
1334
|
};
|
package/dist/do11y.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){let e=`0.0.4`,t=!!window.__do11yInitialized;window.__do11yInitialized=!0;let n={destination:`supabase`,supabaseUrl:``,supabaseKey:``,supabaseTable:`do11y_events`,httpEndpoint:``,httpHeaders:{},debug:!1,flushInterval:5e3,maxBatchSize:10,trackOutboundLinks:!0,trackInternalLinks:!0,trackScrollDepth:!0,scrollThresholds:[25,50,75,90],allowedDomains:null,respectDNT:!0,maxRetries:2,retryDelay:1e3,rateLimitMs:100,framework:`mintlify`,trackSectionVisibility:!0,sectionVisibleThreshold:3,trackTabSwitches:!0,trackTocClicks:!0,trackExpandCollapse:!0,trackFeedback:!0,tabContainerSelector:null,tocSelector:null,feedbackSelector:null,searchSelector:null,copyButtonSelector:null,codeBlockSelector:null,navigationSelector:null,footerSelector:null,contentSelector:null},r={mintlify:{searchSelector:`#search-bar-entry, #search-bar-entry-mobile, [class*="search"]`,copyButtonSelector:`button[class*="copy"], button[aria-label*="copy" i]`,codeBlockSelector:`pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], #navbar, #sidebar, [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, article, [role="main"], [class*="content"]`,tabContainerSelector:`[role="tablist"], [class*="tab"]`,tocSelector:`#table-of-contents, [data-testid="table-of-contents"], [class*="table-of-contents"], [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},docusaurus:{searchSelector:`.DocSearch, .DocSearch-Button`,copyButtonSelector:`button.clean-btn[aria-label*="copy" i], button[class*="copyButton"]`,codeBlockSelector:`pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], .navbar, .sidebar, [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, article, [role="main"], [class*="content"]`,tabContainerSelector:`.tabs[role="tablist"], [class*="tabs"]`,tocSelector:`.table-of-contents, [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},nextra:{searchSelector:`.nextra-search input, input[placeholder*="search" i], button[aria-label*="search" i]`,copyButtonSelector:`button[class*="copy"], button[aria-label*="copy" i], button[title*="copy" i]`,codeBlockSelector:`pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, article, [role="main"], [class*="content"]`,tabContainerSelector:`[role="tablist"], [class*="tab"]`,tocSelector:`.nextra-toc, [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},"mkdocs-material":{searchSelector:`.md-search__input`,copyButtonSelector:`.md-clipboard, .md-code__button[title="Copy to clipboard"]`,codeBlockSelector:`pre, code, [class*="code"]`,navigationSelector:`nav, [role="navigation"], .md-nav, .md-sidebar`,footerSelector:`footer, [role="contentinfo"], .md-footer`,contentSelector:`main, article, [role="main"], .md-content`,tabContainerSelector:`.tabbed-labels, .md-typeset .tabbed-set`,tocSelector:`.md-sidebar--secondary .md-nav, [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},vitepress:{searchSelector:`.VPNavBarSearch button, .VPNavBarSearchButton, #local-search`,copyButtonSelector:`button.copy, .vp-code-copy, button.copy[title*="Copy"]`,codeBlockSelector:`div[class*="language-"], pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], .VPNav, .VPSidebar, [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], .VPFooter, [class*="footer"]`,contentSelector:`main, article, [role="main"], .VPContent, [class*="content"]`,tabContainerSelector:`.vp-code-group .tabs, [role="tablist"]`,tocSelector:`.VPDocAsideOutline, .VPLocalNavOutlineDropdown, a.outline-link`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},starlight:{searchSelector:`site-search button[data-open-modal], sl-doc-search .DocSearch-Button, button[aria-label*="search" i]`,copyButtonSelector:`.expressive-code .copy button, .copy button[data-code]`,codeBlockSelector:`.expressive-code pre, pre`,navigationSelector:`nav, [role="navigation"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, .sl-markdown-content, [role="main"]`,tabContainerSelector:`starlight-tabs [role="tablist"], [role="tablist"]`,tocSelector:`.right-sidebar-panel, starlight-toc, mobile-starlight-toc`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`}},i=[`searchSelector`,`copyButtonSelector`,`codeBlockSelector`,`navigationSelector`,`footerSelector`,`contentSelector`,`tabContainerSelector`,`tocSelector`,`feedbackSelector`];function a(){let e=r[n.framework];e?i.forEach(t=>{n[t]||(n[t]=e[t])}):n.framework!==`custom`&&n.debug&&console.warn(`[Do11y] Unknown framework "${n.framework}". Falling back to generic selectors. Supported: `+Object.keys(r).join(`, `)+`, custom`);let t=r.mintlify;t&&i.forEach(e=>{n[e]||(n[e]=t[e])})}function o(){if(n.respectDNT&&(navigator.doNotTrack===`1`||navigator.doNotTrack===`yes`||window.doNotTrack===`1`))return n.debug&&console.log(`[Do11y] Disabled: Do Not Track is enabled`),!0;if(n.allowedDomains&&n.allowedDomains.length>0){let e=window.location.hostname;if(!n.allowedDomains.some(t=>e===t||e.endsWith(`.`+t)))return n.debug&&console.log(`[Do11y] Disabled: Domain not allowed:`,e),!0}return!1}function s(e){if(!e||typeof e!=`string`)return null;try{return document.querySelector(e),e}catch{return n.debug&&console.warn(`[Do11y] Invalid CSS selector rejected:`,e),null}}let c=[`/pixel/`];function l(e){let t=e??window.location.pathname;return c.some(e=>t.startsWith(e))}function u(e){if(typeof e.className==`string`)return e.className;let t=e.className;return t&&typeof t.baseVal==`string`?t.baseVal:``}function d(e){let t=e.match(/(?:^|\s)language-([\w-]+)(?:\s|$)/);return t?t[1]:null}function f(e){if(!e)return`unknown`;let t=e;for(let e=0;t&&e<12;e++,t=t.parentElement){for(let e of[`language`,`data-language`,`data-lang`,`data-code-lang`]){let n=t.getAttribute(e);if(n)return n}let e=d(u(t));if(e)return e;let n=t.querySelector(`:scope > span.lang`)?.textContent?.trim();if(n)return n;let r=t.querySelector(`[data-language], [data-lang], [data-code-lang], [class*="language-"], [language]`);if(r){let e=r.getAttribute(`language`)??r.getAttribute(`data-language`)??r.getAttribute(`data-lang`)??r.getAttribute(`data-code-lang`)??d(u(r));if(e)return e}}return`unknown`}function ee(e){if(e.startsWith(`#`))return e;let t=e.indexOf(`#`);if(t===-1)return null;let n=e.slice(0,t);return!n||n===window.location.pathname||n===`${window.location.pathname}${window.location.search}`?e.slice(t):null}function te(e){let t=s(n.tocSelector)??`.table-of-contents, .VPDocAsideOutline, .VPLocalNavOutlineDropdown, [class*="toc"], [class*="TableOfContents"], [class*="page-outline"], .right-sidebar-panel, starlight-toc`,r=e.closest(t);return r?((r===e||r.tagName===`A`)&&(r=e.closest(`.VPDocAsideOutline, .VPLocalNavOutlineDropdown, nav, aside, .right-sidebar-panel, starlight-toc`)??r.parentElement),r):null}function p(e,t){if(!e||typeof e!=`string`)return null;let n=t??100,r=e;return r=r.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,`[email]`),r=r.replace(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g,`[phone]`),r=r.replace(/\b\d{3}-\d{2}-\d{4}\b/g,`[redacted]`),r=r.replace(/\b(?:\d[ -]?){13,19}\b/g,`[card]`),r=r.replace(/eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,`[token]`),r=r.replace(/\bxa[a-z]{2}-[A-Za-z0-9_-]{20,}/g,`[token]`),r=r.replace(/\b[0-9a-fA-F]{32,}\b/g,`[redacted]`),r.trim().substring(0,n)}function m(){if(window.crypto&&typeof window.crypto.randomUUID==`function`)return window.crypto.randomUUID();if(window.crypto&&typeof window.crypto.getRandomValues==`function`){let e=new Uint8Array(16);window.crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t=Array.from(e,e=>e.toString(16).padStart(2,`0`)).join(``);return t.slice(0,8)+`-`+t.slice(8,12)+`-`+t.slice(12,16)+`-`+t.slice(16,20)+`-`+t.slice(20)}return`no-crypto-00-0000-0000-000000000000`}function ne(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&t.id.length>0&&typeof t.startTime==`string`&&Array.isArray(t.pageSequence)&&typeof t.pageCount==`number`}function h(){let e=null;try{let t=sessionStorage.getItem(`do11y_session`);if(t){let n=JSON.parse(t);ne(n)&&(e=n)}}catch{}return e||(e={id:m(),startTime:new Date().toISOString(),pageSequence:[],pageCount:0,referrerCategory:null,aiPlatform:null},g(e)),e}function g(e){try{sessionStorage.setItem(`do11y_session`,JSON.stringify(e))}catch{}}function re(e){let t=h();return t.pageCount++,t.pageSequence.push({path:e,timestamp:new Date().toISOString(),index:t.pageCount}),t.pageSequence.length>50&&(t.pageSequence=t.pageSequence.slice(-50)),g(t),t}function ie(){return{viewportCategory:ae(),browserFamily:_(),deviceType:v(),language:(navigator.language||``).split(`-`)[0]||`unknown`,timezoneOffset:new Date().getTimezoneOffset()/60}}function ae(){let e=window.innerWidth;return e<640?`mobile`:e<1024?`tablet`:e<1440?`desktop`:`large-desktop`}function _(){let e=navigator.userAgent;return e.includes(`Firefox`)?`Firefox`:e.includes(`Edg`)?`Edge`:e.includes(`Chrome`)?`Chrome`:e.includes(`Safari`)?`Safari`:`Other`}function v(){let e=navigator.userAgent;return/Mobile|Android|iPhone|iPad/.test(e)?/iPad|Tablet/.test(e)?`tablet`:`mobile`:`desktop`}let y=[{match:`chatgpt`,platform:`ChatGPT`},{match:`chat.com`,platform:`ChatGPT`},{match:`openai`,platform:`ChatGPT`},{match:`perplexity`,platform:`Perplexity`},{match:`claude.ai`,platform:`Claude`},{match:`anthropic`,platform:`Claude`},{match:`gemini`,platform:`Gemini`},{match:`copilot`,platform:`Copilot`},{match:`deepseek`,platform:`DeepSeek`},{match:`meta.ai`,platform:`Meta AI`},{match:`grok`,platform:`Grok`},{match:`x.ai`,platform:`Grok`},{match:`mistral`,platform:`Mistral`},{match:`you.com`,platform:`You.com`},{match:`phind`,platform:`Phind`}];function b(e){if(!e||e===`direct`)return{referrerCategory:`direct`,aiPlatform:null};if(e===`internal`)return{referrerCategory:`internal`,aiPlatform:null};if(e===`unknown`)return{referrerCategory:`unknown`,aiPlatform:null};let t=e.toLowerCase();for(let e of y)if(t.indexOf(e.match)!==-1)return{referrerCategory:`ai`,aiPlatform:e.platform};return/google\.|bing\.|baidu\.|yandex\.|duckduckgo\.|yahoo\./.test(t)?{referrerCategory:`search-engine`,aiPlatform:null}:/github\.|gitlab\.|bitbucket\./.test(t)?{referrerCategory:`code-host`,aiPlatform:null}:/stackoverflow\.|stackexchange\.|reddit\.|news\.ycombinator\./.test(t)?{referrerCategory:`community`,aiPlatform:null}:/twitter\.|x\.com|linkedin\.|facebook\.|threads\.net/.test(t)?{referrerCategory:`social`,aiPlatform:null}:{referrerCategory:`other`,aiPlatform:null}}function x(){try{if(!document.referrer)return`direct`;let e=new URL(document.referrer);return e.hostname===window.location.hostname?`internal`:e.hostname}catch{return`unknown`}}function S(){return{path:window.location.pathname,hash:window.location.hash||null,search:window.location.search?`has_params`:null,title:p(document.title,150)}}let C=[],w=null,T={},E=!1;function D(t,r){if(E)return;let i=Date.now();if(n.rateLimitMs>0&&T[t]&&i-T[t]<n.rateLimitMs){n.debug&&console.log(`[Do11y] Rate limited:`,t);return}T[t]=i;let a=h(),o={_time:new Date().toISOString(),eventType:t,do11y_version:e,sessionId:a.id,sessionPageCount:a.pageCount,...S(),...ie(),...r};n.debug&&console.log(`[Do11y] Event queued:`,o),C.push(o),C.length>100&&(C=C.slice(-100),n.debug&&console.warn(`[Do11y] Event queue capped at 100 events`)),C.length>=n.maxBatchSize?A():oe()}function oe(){w||=setTimeout(A,n.flushInterval)}function se(e){try{let t=new URL(e);return!(t.protocol!==`https:`||!t.hostname.endsWith(`.supabase.co`))}catch{return!1}}function ce(e){try{let t=new URL(e);if(t.protocol!==`https:`)return!1;let n=t.hostname;return!(n===`localhost`||n===`127.0.0.1`||n===`::1`||/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(n))}catch{return!1}}function O(){return n.destination===`supabase`?n.supabaseUrl?se(n.supabaseUrl)?!n.supabaseKey||typeof n.supabaseKey!=`string`||n.supabaseKey.length<10?(n.debug&&console.warn(`[Do11y] Invalid or missing Supabase publishable key`),!1):/^[a-zA-Z0-9_-]+$/.test(n.supabaseTable)?!0:(n.debug&&console.warn(`[Do11y] Invalid table name`),!1):(n.debug&&console.warn(`[Do11y] Invalid Supabase URL. Must be https://<project>.supabase.co`),!1):(n.debug&&console.warn(`[Do11y] No Supabase URL configured`),!1):n.destination===`http`?n.httpEndpoint?ce(n.httpEndpoint)?!0:(n.debug&&console.warn(`[Do11y] Invalid HTTP endpoint. Must be HTTPS and not a private address.`),!1):(n.debug&&console.warn(`[Do11y] No HTTP endpoint configured`),!1):(n.debug&&console.warn(`[Do11y] Unknown destination:`,n.destination),!1)}function k(e){return n.destination===`supabase`?{url:n.supabaseUrl.replace(/\/$/,``)+`/rest/v1/`+n.supabaseTable,headers:{apikey:n.supabaseKey,Authorization:`Bearer `+n.supabaseKey,"Content-Type":`application/json`,Prefer:`return=minimal`},body:JSON.stringify(e.map(e=>({payload:e})))}:{url:n.httpEndpoint,headers:{"Content-Type":`application/json`,...n.httpHeaders},body:JSON.stringify(e)}}function A(e){if(w&&=(clearTimeout(w),null),C.length===0||!O())return;let t=typeof e==`number`?e:n.maxRetries,r=C.slice();C=[],le(k(r),r,t)}function le(e,t,r){fetch(e.url,{method:`POST`,headers:e.headers,body:e.body,keepalive:!0}).then(e=>{if(e.ok){n.debug&&console.log(`[Do11y] Flushed`,t.length,`events`);return}if(r>0&&(e.status>=500||e.status===429)){n.debug&&console.log(`[Do11y] Retrying after error:`,e.status),C=t.concat(C),setTimeout(()=>{A(r-1)},n.retryDelay*(n.maxRetries-r+1));return}n.debug&&e.text().then(t=>{console.error(`[Do11y] Ingest failed:`,e.status,t)}).catch(()=>{})}).catch(e=>{r>0?(n.debug&&console.log(`[Do11y] Network error, retrying:`,e.message),C=t.concat(C),setTimeout(()=>{A(r-1)},n.retryDelay*(n.maxRetries-r+1))):n.debug&&console.error(`[Do11y] Failed to send events:`,e)})}function j(){if(C.length===0||!O())return;let e=C;C=[];let t=k(e);try{fetch(t.url,{method:`POST`,headers:t.headers,body:t.body,keepalive:!0})}catch{}n.debug&&console.log(`[Do11y] Sync flushed`,e.length,`events`)}function M(){let e=re(window.location.pathname),t=x(),n=b(t);e.pageCount===1&&(e.referrerCategory=n.referrerCategory,e.aiPlatform=n.aiPlatform,g(e)),D(`page_view`,{referrerDomain:t,referrerCategory:n.referrerCategory,aiPlatform:n.aiPlatform,isFirstPage:e.pageCount===1,previousPath:e.pageSequence.length>1?e.pageSequence[e.pageSequence.length-2].path:null})}function N(){document.addEventListener(`click`,e=>{let t=e.target.closest(`a`);if(!t)return;let r=t.getAttribute(`href`);if(!r)return;let i=`other`,a=null;try{if(r.startsWith(`#`))i=`anchor`;else if(r.startsWith(`/`)||r.startsWith(`./`)||r.startsWith(`../`))i=`internal`;else if(r.startsWith(`http`)){let e=new URL(r);e.hostname===window.location.hostname?i=`internal`:(i=`external`,a=e.hostname)}else r.startsWith(`mailto:`)&&(i=`email`)}catch{}i===`internal`&&!n.trackInternalLinks||i===`external`&&!n.trackOutboundLinks||(D(`link_click`,{linkType:i,targetUrl:r,targetDomain:a,linkText:p(t.textContent,100),linkContext:P(t),linkSection:p(F(t),100),linkIndex:I(t,r)}),A())},!0)}function P(e){return e.closest(n.navigationSelector)?`navigation`:e.closest(n.footerSelector)?`footer`:e.closest(n.contentSelector)?`content`:`other`}function F(e){let t=e;for(;t&&t!==document.body;){let e=t.previousElementSibling;for(;e;){if(/^H[1-6]$/.test(e.tagName))return e.textContent?.trim().substring(0,100)??null;let t=e.querySelectorAll(`h1, h2, h3, h4, h5, h6`);if(t.length>0)return t[t.length-1].textContent?.trim().substring(0,100)??null;e=e.previousElementSibling}t=t.parentElement}return null}function I(e,t){if(typeof CSS>`u`||typeof CSS.escape!=`function`)return 1;try{let n=document.querySelectorAll(`a[href="`+CSS.escape(t)+`"]`);for(let t=0;t<n.length;t++)if(n[t]===e)return t+1}catch{}return 1}let L=new Set,R=null;function z(e){let t=e;for(;t&&t!==document.body&&t!==document.documentElement;){let e=window.getComputedStyle(t).overflowY;if((e===`auto`||e===`scroll`)&&t.scrollHeight>t.clientHeight)return t;t=t.parentElement}return null}function B(){if(!n.trackScrollDepth)return;if(n.contentSelector){let e=document.querySelector(n.contentSelector);e&&(R=z(e))}let e=!1;function t(){e||=(window.requestAnimationFrame(()=>{V(),e=!1}),!0)}if(window.addEventListener(`scroll`,t),R&&(R.addEventListener(`scroll`,t),n.debug)){let e=R;console.log(`[do11y] Using container-based scroll tracking:`,e.className||e.tagName)}V()}function V(){if(l())return;let e,t,r;R&&R.scrollHeight>R.clientHeight?(e=R.scrollTop,t=R.scrollHeight,r=R.clientHeight):(e=window.scrollY||document.documentElement.scrollTop,t=document.documentElement.scrollHeight,r=window.innerHeight);let i=t-r;if(i<=0){n.scrollThresholds.forEach(e=>{L.has(e)||(L.add(e),D(`scroll_depth`,{threshold:e,scrollPercent:100}))});return}let a=Math.round(e/i*100);n.scrollThresholds.forEach(e=>{a>=e&&!L.has(e)&&(L.add(e),D(`scroll_depth`,{threshold:e,scrollPercent:a}))})}let H=Date.now(),U=Date.now(),W=0,G=!0;function K(){if(l())return;G&&(W+=Date.now()-U);let e=Date.now()-H,t=e>0?W/e:0,n=0;L.forEach(e=>{e>n&&(n=e)}),X();let r=h();D(`page_exit`,{totalTimeSeconds:Math.round(e/1e3),activeTimeSeconds:Math.round(W/1e3),engagementRatio:Math.round(t*100)/100,maxScrollDepth:n,referrerCategory:r.referrerCategory,aiPlatform:r.aiPlatform})}function ue(){document.addEventListener(`visibilitychange`,()=>{document.hidden?G&&=(W+=Date.now()-U,!1):(U=Date.now(),G=!0)}),window.addEventListener(`beforeunload`,()=>{K(),ve()})}function de(){document.addEventListener(`click`,e=>{e.target.closest(n.searchSelector)&&D(`search_opened`,{})},!0),document.addEventListener(`keydown`,e=>{(e.metaKey||e.ctrlKey)&&e.key===`k`&&D(`search_opened`,{trigger:`keyboard`})})}function fe(e){if(!e)return 1;try{let t=document.querySelectorAll(n.codeBlockSelector);for(let n=0;n<t.length;n++)if(t[n]===e)return n+1}catch{}return 1}function pe(){document.addEventListener(`click`,e=>{let t=e.target.closest(n.copyButtonSelector);if(t){let e=t.closest(`[class*="language-"], [language]`)??t.closest(n.codeBlockSelector)??t.closest(`.expressive-code`)?.querySelector(`pre`)??t.closest(`div, section`)?.querySelector(`pre`)??t.parentElement?.querySelector(`pre`)??null;D(`code_copied`,{language:f((e?e.tagName===`PRE`?e.querySelector(`code`):e.querySelector(`code[class*="language-"], code[language]`)??e.querySelector(`code`):null)??e??t),codeSection:p(F(e??t),100),codeBlockIndex:fe(e)})}},!0)}let q=null,J={};function me(){if(!n.trackSectionVisibility||typeof IntersectionObserver>`u`)return;let e=n.sectionVisibleThreshold*1e3;q=new IntersectionObserver(t=>{t.forEach(t=>{let n=t.target.getAttribute(`data-do11y-section-id`);if(n)if(t.isIntersecting)J[n]||(J[n]={start:Date.now(),reported:!1});else{if(J[n]&&!J[n].reported){let r=Date.now()-J[n].start;r>=e&&(D(`section_visible`,{heading:p(t.target.textContent?.trim()??``,100),headingLevel:parseInt(t.target.tagName.charAt(1),10),visibleSeconds:Math.round(r/1e3)}),J[n].reported=!0)}delete J[n]}})},{threshold:.5}),Y()}function Y(){q&&document.querySelectorAll(`h2, h3`).forEach((e,t)=>{e.setAttribute(`data-do11y-section-id`,`section-`+t),q.observe(e)})}function X(){if(!q)return;let e=Date.now(),t=n.sectionVisibleThreshold*1e3;Object.keys(J).forEach(n=>{let r=J[n];if(r&&!r.reported){let i=e-r.start;if(i>=t){let e=typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(n):n.replace(/["\\]/g,`\\$&`),t=document.querySelector(`[data-do11y-section-id="`+e+`"]`);t&&D(`section_visible`,{heading:p(t.textContent?.trim()??``,100),headingLevel:parseInt(t.tagName.charAt(1),10),visibleSeconds:Math.round(i/1e3)})}}}),J={}}function he(){n.trackTabSwitches&&document.addEventListener(`click`,e=>{let t=`[role="tab"], .tabs button, .tabs a, .tabbed-labels label`,r=s(n.tabContainerSelector);r&&(t+=`, `+r+` button, `+r+` a, `+r+` label`);let i=e.target.closest(t);if(!i||i.getAttribute(`aria-selected`)===`true`||i.classList.contains(`active`)||i.classList.contains(`is-active`))return;let a=p(i.textContent,50);a&&D(`tab_switch`,{tabLabel:a,tabGroup:p(F(i),100),isDefault:!1})})}function Z(){n.trackTocClicks&&document.addEventListener(`click`,e=>{let t=e.target.closest(`a`);if(!t)return;let n=te(t);if(!n)return;let r=t.getAttribute(`href`),i=r?ee(r):null;if(!i)return;let a=p(t.textContent,100),o=null;try{let e=i.slice(1),t=document.getElementById(e);t&&/^H[1-6]$/.test(t.tagName)&&(o=parseInt(t.tagName.charAt(1),10))}catch{}let s=n.querySelectorAll(`a[href*="#"]`),c=1;for(let e=0;e<s.length;e++)if(s[e]===t){c=e+1;break}D(`toc_click`,{heading:a,headingLevel:o,tocPosition:c})},!0)}function ge(){n.trackFeedback&&document.addEventListener(`click`,e=>{let t=e.target.closest(`button, [role="button"], a`);if(!t||!t.closest(s(n.feedbackSelector)??`[class*="feedback"], [class*="helpful"], [class*="rating"], [class*="was-this"], [data-feedback]`))return;let r=(t.textContent??``).trim().toLowerCase(),i=(t.getAttribute(`aria-label`)??``).toLowerCase(),a=(t.getAttribute(`title`)??``).toLowerCase(),o=t.getAttribute(`data-value`)??t.getAttribute(`data-md-value`)??t.getAttribute(`data-feedback`),c=o&&/^[\w\s.,!?-]{1,50}$/.test(o)?o:null,l=null;c?l=c:/\byes\b|👍|thumbs.?up|helpful/i.test(r+` `+i+` `+a)?l=`yes`:/\bno\b|👎|thumbs.?down|not.?helpful/i.test(r+` `+i+` `+a)&&(l=`no`),l&&D(`feedback`,{rating:l})})}function _e(){n.trackExpandCollapse&&(document.addEventListener(`toggle`,e=>{let t=e.target;if(t.tagName!==`DETAILS`)return;let n=t.querySelector(`summary`);D(`expand_collapse`,{summary:p(n?n.textContent:``,100),action:t.open?`expand`:`collapse`,section:p(F(t),100)})},!0),document.addEventListener(`click`,e=>{let t=e.target.closest(`[aria-expanded], [class*="accordion"] button, [class*="collapsible"] button`);if(!t||t.closest(`details`)||t.closest(`nav, [role="navigation"], header`))return;let n=t.getAttribute(`aria-expanded`)===`true`;D(`expand_collapse`,{summary:p(t.textContent,100),action:n?`collapse`:`expand`,section:p(F(t),100)})}))}let Q=null;function $(){if(window.Do11yConfig&&typeof window.Do11yConfig==`object`)for(let e in window.Do11yConfig)Object.prototype.hasOwnProperty.call(window.Do11yConfig,e)&&Object.prototype.hasOwnProperty.call(n,e)&&(n[e]=window.Do11yConfig[e]);let e=document.querySelector(`meta[name="do11y-destination"]`);if(e){let t=e.getAttribute(`content`);(t===`supabase`||t===`http`)&&(n.destination=t)}let t=document.querySelector(`meta[name="do11y-url"]`);t&&(n.supabaseUrl=t.getAttribute(`content`)??n.supabaseUrl);let r=document.querySelector(`meta[name="do11y-key"]`);r&&(n.supabaseKey=r.getAttribute(`content`)??n.supabaseKey);let i=document.querySelector(`meta[name="do11y-table"]`);i&&(n.supabaseTable=i.getAttribute(`content`)??n.supabaseTable);let s=document.querySelector(`meta[name="do11y-http-endpoint"]`);s&&(n.httpEndpoint=s.getAttribute(`content`)??n.httpEndpoint);let c=document.querySelector(`meta[name="do11y-debug"]`);c&&c.getAttribute(`content`)===`true`&&(n.debug=!0);let l=document.querySelector(`meta[name="do11y-domains"]`);if(l){let e=l.getAttribute(`content`);e&&(n.allowedDomains=e.split(`,`).map(e=>e.trim()))}let u=document.querySelector(`meta[name="do11y-framework"]`);if(u&&(n.framework=u.getAttribute(`content`)??n.framework),a(),n.debug&&console.log(`[Do11y] Initializing with config:`,{destination:n.destination,hasCredentials:n.destination===`supabase`?!!n.supabaseKey:!!n.httpEndpoint,framework:n.framework,allowedDomains:n.allowedDomains,respectDNT:n.respectDNT}),o()){E=!0,n.debug&&console.log(`[Do11y] Tracking disabled`);return}(n.destination===`supabase`?n.supabaseKey:n.httpEndpoint)||n.debug&&(console.warn(`[Do11y] No destination configured. Events will not be sent.`),console.warn(`[Do11y] Add <meta name="do11y-url"> and <meta name="do11y-key"> to enable.`)),M(),N(),B(),ue(),de(),pe(),me(),he(),Z(),ge(),_e();let d=window.location.pathname;Q=new MutationObserver(()=>{window.location.pathname!==d&&(d=window.location.pathname,K(),L=new Set,H=Date.now(),U=Date.now(),W=0,G=!0,M(),Y(),V())}),Q.observe(document.body,{childList:!0,subtree:!0}),window.addEventListener(`popstate`,()=>{window.location.pathname!==d&&(d=window.location.pathname,K(),L=new Set,H=Date.now(),U=Date.now(),W=0,G=!0,M(),Y(),V())}),Object.freeze(n),n.debug&&console.log(`[Do11y] Initialized successfully`)}function ve(){Q&&=(Q.disconnect(),null),q&&=(X(),q.disconnect(),null),w&&=(clearTimeout(w),null),j()}t||(document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,$):$()),window.Do11y=window.Do11y??{getConfig:()=>({destination:n.destination,hasCredentials:n.destination===`supabase`?!!n.supabaseKey:!!n.httpEndpoint,isDisabled:E,allowedDomains:n.allowedDomains,respectDNT:n.respectDNT}),flush:A,isEnabled:()=>!E&&(n.destination===`supabase`?!!n.supabaseKey:!!n.httpEndpoint),getQueueSize:()=>C.length,version:e}})();
|
|
1
|
+
(function(){let e=`browser.do11y.referrer_category`,t=`browser.do11y.ai_platform`,n=`browser.do11y.scroll.threshold`,r=`browser.do11y.scroll.percent`,i=`browser.do11y.section.heading`,a=`browser.do11y.section.heading_level`,o=`browser.do11y.section.visible_seconds`,s=`browser.do11y.expand.summary`,c=`browser.do11y.expand.action`,l=`browser.do11y.expand.section`,u=`browser.do11y.scroll_depth`,d=`browser.do11y.search_opened`,f=`browser.do11y.section_visible`,p=`browser.do11y.expand_collapse`,m=`0.1.0`,ee=!!window.__do11yInitialized;window.__do11yInitialized=!0;let h={destination:`supabase`,supabaseUrl:``,supabaseKey:``,supabaseTable:`do11y_events`,endpoint:``,headers:{},bodyTransform:void 0,otelSdkEndpoint:``,otelSdkHeaders:{},otelSdkServiceName:`do11y`,otelSdkResourceAttributes:{},otelSdkCdnUrl:`https://esm.sh/`,debug:!1,flushInterval:5e3,maxBatchSize:10,trackOutboundLinks:!0,trackInternalLinks:!0,trackScrollDepth:!0,scrollThresholds:[25,50,75,90],allowedDomains:null,respectDNT:!0,maxRetries:2,retryDelay:1e3,rateLimitMs:100,framework:`mintlify`,trackSectionVisibility:!0,sectionVisibleThreshold:3,trackTabSwitches:!0,trackTocClicks:!0,trackExpandCollapse:!0,trackFeedback:!0,tabContainerSelector:null,tocSelector:null,feedbackSelector:null,searchSelector:null,copyButtonSelector:null,codeBlockSelector:null,navigationSelector:null,footerSelector:null,contentSelector:null,useOtelBrowserInstrumentations:!1},g={mintlify:{searchSelector:`#search-bar-entry, #search-bar-entry-mobile, [class*="search"]`,copyButtonSelector:`button[class*="copy"], button[aria-label*="copy" i]`,codeBlockSelector:`pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], #navbar, #sidebar, [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, article, [role="main"], [class*="content"]`,tabContainerSelector:`[role="tablist"], [class*="tab"]`,tocSelector:`#table-of-contents, [data-testid="table-of-contents"], [class*="table-of-contents"], [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},docusaurus:{searchSelector:`.DocSearch, .DocSearch-Button`,copyButtonSelector:`button.clean-btn[aria-label*="copy" i], button[class*="copyButton"]`,codeBlockSelector:`pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], .navbar, .sidebar, [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, article, [role="main"], [class*="content"]`,tabContainerSelector:`.tabs[role="tablist"], [class*="tabs"]`,tocSelector:`.table-of-contents, [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},nextra:{searchSelector:`.nextra-search input, input[placeholder*="search" i], button[aria-label*="search" i]`,copyButtonSelector:`button[class*="copy"], button[aria-label*="copy" i], button[title*="copy" i]`,codeBlockSelector:`pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, article, [role="main"], [class*="content"]`,tabContainerSelector:`[role="tablist"], [class*="tab"]`,tocSelector:`.nextra-toc, [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},"mkdocs-material":{searchSelector:`.md-search__input`,copyButtonSelector:`.md-clipboard, .md-code__button[title="Copy to clipboard"]`,codeBlockSelector:`pre, code, [class*="code"]`,navigationSelector:`nav, [role="navigation"], .md-nav, .md-sidebar`,footerSelector:`footer, [role="contentinfo"], .md-footer`,contentSelector:`main, article, [role="main"], .md-content`,tabContainerSelector:`.tabbed-labels, .md-typeset .tabbed-set`,tocSelector:`.md-sidebar--secondary .md-nav, [class*="toc"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},vitepress:{searchSelector:`.VPNavBarSearch button, .VPNavBarSearchButton, #local-search`,copyButtonSelector:`button.copy, .vp-code-copy, button.copy[title*="Copy"]`,codeBlockSelector:`div[class*="language-"], pre, [class*="code"]`,navigationSelector:`nav, [role="navigation"], .VPNav, .VPSidebar, [class*="nav"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], .VPFooter, [class*="footer"]`,contentSelector:`main, article, [role="main"], .VPContent, [class*="content"]`,tabContainerSelector:`.vp-code-group .tabs, [role="tablist"]`,tocSelector:`.VPDocAsideOutline, .VPLocalNavOutlineDropdown, a.outline-link`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`},starlight:{searchSelector:`site-search button[data-open-modal], sl-doc-search .DocSearch-Button, button[aria-label*="search" i]`,copyButtonSelector:`.expressive-code .copy button, .copy button[data-code]`,codeBlockSelector:`.expressive-code pre, pre`,navigationSelector:`nav, [role="navigation"], [class*="sidebar"]`,footerSelector:`footer, [role="contentinfo"], [class*="footer"]`,contentSelector:`main, .sl-markdown-content, [role="main"]`,tabContainerSelector:`starlight-tabs [role="tablist"], [role="tablist"]`,tocSelector:`.right-sidebar-panel, starlight-toc, mobile-starlight-toc`,feedbackSelector:`[class*="feedback"], [class*="helpful"]`}},_=[`searchSelector`,`copyButtonSelector`,`codeBlockSelector`,`navigationSelector`,`footerSelector`,`contentSelector`,`tabContainerSelector`,`tocSelector`,`feedbackSelector`];function te(){let e=g[h.framework];e?_.forEach(t=>{h[t]||(h[t]=e[t])}):h.framework!==`custom`&&h.debug&&console.warn(`[Do11y] Unknown framework "${h.framework}". Falling back to generic selectors. Supported: `+Object.keys(g).join(`, `)+`, custom`);let t=g.mintlify;t&&_.forEach(e=>{h[e]||(h[e]=t[e])})}function v(){if(h.respectDNT&&(navigator.doNotTrack===`1`||navigator.doNotTrack===`yes`||window.doNotTrack===`1`))return h.debug&&console.log(`[Do11y] Disabled: Do Not Track is enabled`),!0;if(h.allowedDomains&&h.allowedDomains.length>0){let e=window.location.hostname;if(!h.allowedDomains.some(t=>e===t||e.endsWith(`.`+t)))return h.debug&&console.log(`[Do11y] Disabled: Domain not allowed:`,e),!0}return!1}function y(e){if(!e||typeof e!=`string`)return null;try{return document.querySelector(e),e}catch{return h.debug&&console.warn(`[Do11y] Invalid CSS selector rejected:`,e),null}}function b(e){if(typeof e.className==`string`)return e.className;let t=e.className;return t&&typeof t.baseVal==`string`?t.baseVal:``}function x(e){let t=e.match(/(?:^|\s)language-([\w-]+)(?:\s|$)/);return t?t[1]:null}function ne(e){if(!e)return`unknown`;let t=e;for(let e=0;t&&e<12;e++,t=t.parentElement){for(let e of[`language`,`data-language`,`data-lang`,`data-code-lang`]){let n=t.getAttribute(e);if(n)return n}let e=x(b(t));if(e)return e;let n=t.querySelector(`:scope > span.lang`)?.textContent?.trim();if(n)return n;let r=t.querySelector(`[data-language], [data-lang], [data-code-lang], [class*="language-"], [language]`);if(r){let e=r.getAttribute(`language`)??r.getAttribute(`data-language`)??r.getAttribute(`data-lang`)??r.getAttribute(`data-code-lang`)??x(b(r));if(e)return e}}return`unknown`}function re(e){if(e.startsWith(`#`))return e;let t=e.indexOf(`#`);if(t===-1)return null;let n=e.slice(0,t);return!n||n===window.location.pathname||n===`${window.location.pathname}${window.location.search}`?e.slice(t):null}function ie(e){let t=y(h.tocSelector)??`.table-of-contents, .VPDocAsideOutline, .VPLocalNavOutlineDropdown, [class*="toc"], [class*="TableOfContents"], [class*="page-outline"], .right-sidebar-panel, starlight-toc`,n=e.closest(t);return n?((n===e||n.tagName===`A`)&&(n=e.closest(`.VPDocAsideOutline, .VPLocalNavOutlineDropdown, nav, aside, .right-sidebar-panel, starlight-toc`)??n.parentElement),n):null}function S(e,t){if(!e||typeof e!=`string`)return null;let n=t??100,r=e;return r=r.replace(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,`[email]`),r=r.replace(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g,`[phone]`),r=r.replace(/\b\d{3}-\d{2}-\d{4}\b/g,`[redacted]`),r=r.replace(/\b(?:\d[ -]?){13,19}\b/g,`[card]`),r=r.replace(/eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,`[token]`),r=r.replace(/\bxa[a-z]{2}-[A-Za-z0-9_-]{20,}/g,`[token]`),r=r.replace(/\b[0-9a-fA-F]{32,}\b/g,`[redacted]`),r.trim().substring(0,n)}function ae(){if(window.crypto&&typeof window.crypto.randomUUID==`function`)return window.crypto.randomUUID();if(window.crypto&&typeof window.crypto.getRandomValues==`function`){let e=new Uint8Array(16);window.crypto.getRandomValues(e),e[6]=e[6]&15|64,e[8]=e[8]&63|128;let t=Array.from(e,e=>e.toString(16).padStart(2,`0`)).join(``);return t.slice(0,8)+`-`+t.slice(8,12)+`-`+t.slice(12,16)+`-`+t.slice(16,20)+`-`+t.slice(20)}return`no-crypto-00-0000-0000-000000000000`}function oe(e){if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.id==`string`&&t.id.length>0&&typeof t.startTime==`string`&&Array.isArray(t.pageSequence)&&typeof t.pageCount==`number`}function C(){let e=null;try{let t=sessionStorage.getItem(`do11y_session`);if(t){let n=JSON.parse(t);oe(n)&&(e=n)}}catch{}return e||(e={id:ae(),startTime:new Date().toISOString(),pageSequence:[],pageCount:0,referrerCategory:null,aiPlatform:null},w(e)),e}function w(e){try{sessionStorage.setItem(`do11y_session`,JSON.stringify(e))}catch{}}function T(e){let t=C();return t.pageCount++,t.pageSequence.push({path:e,timestamp:new Date().toISOString(),index:t.pageCount}),t.pageSequence.length>50&&(t.pageSequence=t.pageSequence.slice(-50)),w(t),t}function se(){return{"browser.do11y.viewport_category":ce(),"browser.family":le(),"device.type":ue(),"browser.language":(navigator.language||``).split(`-`)[0]||`unknown`,"browser.do11y.timezone_offset":new Date().getTimezoneOffset()/60}}function ce(){let e=window.innerWidth;return e<640?`mobile`:e<1024?`tablet`:e<1440?`desktop`:`large-desktop`}function le(){let e=navigator.userAgent;return e.includes(`Firefox`)?`Firefox`:e.includes(`Edg`)?`Edge`:e.includes(`Chrome`)?`Chrome`:e.includes(`Safari`)?`Safari`:`Other`}function ue(){let e=navigator.userAgent;return/Mobile|Android|iPhone|iPad/.test(e)?/iPad|Tablet/.test(e)?`tablet`:`mobile`:`desktop`}let de=[{match:`chatgpt`,platform:`ChatGPT`},{match:`chat.com`,platform:`ChatGPT`},{match:`openai`,platform:`ChatGPT`},{match:`perplexity`,platform:`Perplexity`},{match:`claude.ai`,platform:`Claude`},{match:`anthropic`,platform:`Claude`},{match:`gemini`,platform:`Gemini`},{match:`copilot`,platform:`Copilot`},{match:`deepseek`,platform:`DeepSeek`},{match:`meta.ai`,platform:`Meta AI`},{match:`grok`,platform:`Grok`},{match:`x.ai`,platform:`Grok`},{match:`mistral`,platform:`Mistral`},{match:`you.com`,platform:`You.com`},{match:`phind`,platform:`Phind`}];function fe(e){if(!e||e===`direct`)return{referrerCategory:`direct`,aiPlatform:null};if(e===`internal`)return{referrerCategory:`internal`,aiPlatform:null};if(e===`unknown`)return{referrerCategory:`unknown`,aiPlatform:null};let t=e.toLowerCase();for(let e of de)if(t.indexOf(e.match)!==-1)return{referrerCategory:`ai`,aiPlatform:e.platform};return/google\.|bing\.|baidu\.|yandex\.|duckduckgo\.|yahoo\./.test(t)?{referrerCategory:`search-engine`,aiPlatform:null}:/github\.|gitlab\.|bitbucket\./.test(t)?{referrerCategory:`code-host`,aiPlatform:null}:/stackoverflow\.|stackexchange\.|reddit\.|news\.ycombinator\./.test(t)?{referrerCategory:`community`,aiPlatform:null}:/twitter\.|x\.com|linkedin\.|facebook\.|threads\.net/.test(t)?{referrerCategory:`social`,aiPlatform:null}:{referrerCategory:`other`,aiPlatform:null}}function pe(){try{if(!document.referrer)return`direct`;let e=new URL(document.referrer);return e.hostname===window.location.hostname?`internal`:e.hostname}catch{return`unknown`}}function me(){return{"url.path":window.location.pathname,"url.fragment":window.location.hash||null,"url.query":window.location.search?`has_params`:null,"browser.do11y.page_title":S(document.title,150)}}let E=[],D=null,O={},k=!1;function A(e,t){if(k)return;let n=Date.now();if(h.rateLimitMs>0&&O[e]&&n-O[e]<h.rateLimitMs){h.debug&&console.log(`[Do11y] Rate limited:`,e);return}O[e]=n;let r=C(),i={_time:new Date().toISOString(),eventName:e,"browser.do11y.version":m,"session.id":r.id,"browser.do11y.session_page_count":r.pageCount,...me(),...se(),...t};if(h.debug&&console.log(`[Do11y] Event queued:`,e,i),h.destination===`otlp`&&j){j.emit({eventName:e,severityNumber:9,attributes:i,body:``});return}E.push(i),E.length>100&&(E=E.slice(-100),h.debug&&console.warn(`[Do11y] Event queue capped at 100 events`)),E.length>=h.maxBatchSize?F():he()}function he(){D||=setTimeout(F,h.flushInterval)}let j=null;function ge(e){try{let t=new URL(e);return!(t.protocol!==`https:`||!t.hostname.endsWith(`.supabase.co`))}catch{return!1}}function _e(e){try{let t=new URL(e);if(t.protocol!==`https:`)return!1;let n=t.hostname;return!(n===`localhost`||n===`127.0.0.1`||n===`::1`||/^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.)/.test(n))}catch{return!1}}function M(){return h.destination===`supabase`?h.supabaseUrl?ge(h.supabaseUrl)?!h.supabaseKey||typeof h.supabaseKey!=`string`||h.supabaseKey.length<10?(h.debug&&console.warn(`[Do11y] Invalid or missing Supabase publishable key`),!1):/^[a-zA-Z0-9_-]+$/.test(h.supabaseTable)?!0:(h.debug&&console.warn(`[Do11y] Invalid table name`),!1):(h.debug&&console.warn(`[Do11y] Invalid Supabase URL. Must be https://<project>.supabase.co`),!1):(h.debug&&console.warn(`[Do11y] No Supabase URL configured`),!1):h.destination===`http`?h.endpoint?_e(h.endpoint)?!0:(h.debug&&console.warn(`[Do11y] Invalid HTTP endpoint. Must be HTTPS and not a private address.`),!1):(h.debug&&console.warn(`[Do11y] No HTTP endpoint configured`),!1):h.destination===`otlp`?h.otelSdkEndpoint?(N().catch(e=>{h.debug&&console.warn(`[Do11y] OTel SDK initialization failed:`,e)}),!0):(h.debug&&console.warn(`[Do11y] No OTLP endpoint configured`),!1):(h.debug&&console.warn(`[Do11y] Unknown destination:`,h.destination),!1)}async function N(){if(j)return;let e=h.otelSdkCdnUrl.replace(/\/+$/,``)+`/`,t=await import(`${e}@opentelemetry/api-logs`),n=await import(`${e}@opentelemetry/sdk-logs`),r=await import(`${e}@opentelemetry/exporter-logs-otlp-http`),i={"service.name":h.otelSdkServiceName||`do11y`,"service.version":m,"telemetry.sdk.name":`do11y`,"telemetry.sdk.language":`webjs`,"telemetry.sdk.version":m,...h.otelSdkResourceAttributes},a=new n.LoggerProvider({resource:{attributes:i},processors:[new n.BatchLogRecordProcessor({exporter:new r.OTLPLogExporter({url:h.otelSdkEndpoint.replace(/\/$/,``)+`/v1/logs`,headers:h.otelSdkHeaders})})]});t.logs.setGlobalLoggerProvider(a),j=a.getLogger(`do11y`),h.debug&&console.log(`[Do11y] OTel SDK initialized with endpoint:`,h.otelSdkEndpoint)}function P(e){if(h.destination===`supabase`){let t=h.supabaseUrl.replace(/\/$/,``)+`/rest/v1/`+h.supabaseTable,n=h.bodyTransform??(e=>e.map(e=>({payload:e})));return{url:t,headers:{apikey:h.supabaseKey,Authorization:`Bearer `+h.supabaseKey,"Content-Type":`application/json`,Prefer:`return=minimal`},body:JSON.stringify(n(e))}}let t=h.bodyTransform??(e=>e);return{url:h.endpoint,headers:{"Content-Type":`application/json`,...h.headers},body:JSON.stringify(t(e))}}function F(e){if(D&&=(clearTimeout(D),null),E.length===0||!M())return;let t=typeof e==`number`?e:h.maxRetries,n=E.slice();E=[],ve(P(n),n,t)}function I(e){try{return new URL(e).origin!==window.location.origin}catch{return!1}}function ve(e,t,n){let r=I(e.url);h.debug&&r&&console.log(`[Do11y] Cross-origin request to`,new URL(e.url).origin,`- requires CORS headers on the server`),fetch(e.url,{method:`POST`,headers:e.headers,body:e.body,keepalive:!0,mode:r?`cors`:`same-origin`}).then(e=>{if(e.ok){h.debug&&console.log(`[Do11y] Flushed`,t.length,`events`);return}if(n>0&&(e.status>=500||e.status===429)){h.debug&&console.log(`[Do11y] Retrying after error:`,e.status),E=t.concat(E),setTimeout(()=>{F(n-1)},h.retryDelay*(h.maxRetries-n+1));return}h.debug&&e.text().then(t=>{let n=`[Do11y] Ingest failed: ${e.status}`;e.status===0&&e.type===`opaque`?console.error(n,`- CORS error: server did not return Access-Control-Allow-Origin`):console.error(n,t)}).catch(()=>{})}).catch(e=>{if(n>0){if(h.debug){let t=r?` (this may be a CORS issue — try using an OTel Collector proxy)`:``;console.log(`[Do11y] Network error, retrying:`,e.message+t)}E=t.concat(E),setTimeout(()=>{F(n-1)},h.retryDelay*(h.maxRetries-n+1))}else h.debug&&console.error(`[Do11y] Failed to send events:`,e.message)})}function ye(){if(h.destination===`otlp`||E.length===0||!M())return;let e=E;E=[];let t=P(e);try{fetch(t.url,{method:`POST`,headers:t.headers,body:t.body,keepalive:!0})}catch{}h.debug&&console.log(`[Do11y] Sync flushed`,e.length,`events`)}function L(){let n=T(window.location.pathname),r=pe(),i=fe(r);n.pageCount===1&&(n.referrerCategory=i.referrerCategory,n.aiPlatform=i.aiPlatform,w(n)),A(`browser.do11y.page_view`,{"browser.do11y.referrer_domain":r,[e]:i.referrerCategory,[t]:i.aiPlatform,"browser.do11y.is_first_page":n.pageCount===1,"browser.do11y.previous_path":n.pageSequence.length>1?n.pageSequence[n.pageSequence.length-2].path:null})}function be(){document.addEventListener(`click`,e=>{let t=e.target.closest(`a`);if(!t)return;let n=t.getAttribute(`href`);if(!n)return;let r=`other`,i=null;try{if(n.startsWith(`#`))r=`anchor`;else if(n.startsWith(`/`)||n.startsWith(`./`)||n.startsWith(`../`))r=`internal`;else if(n.startsWith(`http`)){let e=new URL(n);e.hostname===window.location.hostname?r=`internal`:(r=`external`,i=e.hostname)}else n.startsWith(`mailto:`)&&(r=`email`)}catch{}r===`internal`&&!h.trackInternalLinks||r===`external`&&!h.trackOutboundLinks||(A(`browser.do11y.link_click`,{"browser.do11y.link.type":r,"browser.do11y.link.target_url":n,"browser.do11y.link.target_domain":i,"browser.do11y.link.text":S(t.textContent,100),"browser.do11y.link.context":xe(t),"browser.do11y.link.section":S(R(t),100),"browser.do11y.link.index":Se(t,n)}),F())},!0)}function xe(e){return e.closest(h.navigationSelector)?`navigation`:e.closest(h.footerSelector)?`footer`:e.closest(h.contentSelector)?`content`:`other`}function R(e){let t=e;for(;t&&t!==document.body;){let e=t.previousElementSibling;for(;e;){if(/^H[1-6]$/.test(e.tagName))return e.textContent?.trim().substring(0,100)??null;let t=e.querySelectorAll(`h1, h2, h3, h4, h5, h6`);if(t.length>0)return t[t.length-1].textContent?.trim().substring(0,100)??null;e=e.previousElementSibling}t=t.parentElement}return null}function Se(e,t){if(typeof CSS>`u`||typeof CSS.escape!=`function`)return 1;try{let n=document.querySelectorAll(`a[href="`+CSS.escape(t)+`"]`);for(let t=0;t<n.length;t++)if(n[t]===e)return t+1}catch{}return 1}let z=new Set,B=null;function Ce(e){let t=e;for(;t&&t!==document.body&&t!==document.documentElement;){let e=window.getComputedStyle(t).overflowY;if((e===`auto`||e===`scroll`)&&t.scrollHeight>t.clientHeight)return t;t=t.parentElement}return null}function we(){if(!h.trackScrollDepth)return;if(h.contentSelector){let e=document.querySelector(h.contentSelector);e&&(B=Ce(e))}let e=!1;function t(){e||=(window.requestAnimationFrame(()=>{V(),e=!1}),!0)}if(window.addEventListener(`scroll`,t),B&&(B.addEventListener(`scroll`,t),h.debug)){let e=B;console.log(`[do11y] Using container-based scroll tracking:`,e.className||e.tagName)}V()}function V(){let e,t,i;B&&B.scrollHeight>B.clientHeight?(e=B.scrollTop,t=B.scrollHeight,i=B.clientHeight):(e=window.scrollY||document.documentElement.scrollTop,t=document.documentElement.scrollHeight,i=window.innerHeight);let a=t-i;if(a<=0){h.scrollThresholds.forEach(e=>{z.has(e)||(z.add(e),A(u,{[n]:e,[r]:100}))});return}let o=Math.round(e/a*100);h.scrollThresholds.forEach(e=>{o>=e&&!z.has(e)&&(z.add(e),A(u,{[n]:e,[r]:o}))})}let H=Date.now(),U=Date.now(),W=0,G=!0;function K(){G&&(W+=Date.now()-U);let n=Date.now()-H,r=n>0?W/n:0,i=0;z.forEach(e=>{e>i&&(i=e)}),X();let a=C();A(`browser.do11y.page_exit`,{"browser.do11y.page_exit.total_time_seconds":Math.round(n/1e3),"browser.do11y.page_exit.active_time_seconds":Math.round(W/1e3),"browser.do11y.page_exit.engagement_ratio":Math.round(r*100)/100,"browser.do11y.page_exit.max_scroll_depth":i,[e]:a.referrerCategory,[t]:a.aiPlatform})}function Te(){document.addEventListener(`visibilitychange`,()=>{document.hidden?G&&=(W+=Date.now()-U,!1):(U=Date.now(),G=!0)}),window.addEventListener(`beforeunload`,()=>{K(),Ne()})}function Ee(){document.addEventListener(`click`,e=>{e.target.closest(h.searchSelector)&&A(d,{})},!0),document.addEventListener(`keydown`,e=>{(e.metaKey||e.ctrlKey)&&e.key===`k`&&A(d,{"browser.do11y.search.trigger":`keyboard`})})}function De(e){if(!e)return 1;try{let t=document.querySelectorAll(h.codeBlockSelector);for(let n=0;n<t.length;n++)if(t[n]===e)return n+1}catch{}return 1}function Oe(){document.addEventListener(`click`,e=>{let t=e.target.closest(h.copyButtonSelector);if(t){let e=t.closest(`[class*="language-"], [language]`)??t.closest(h.codeBlockSelector)??t.closest(`.expressive-code`)?.querySelector(`pre`)??t.closest(`div, section`)?.querySelector(`pre`)??t.parentElement?.querySelector(`pre`)??null,n=ne((e?e.tagName===`PRE`?e.querySelector(`code`):e.querySelector(`code[class*="language-"], code[language]`)??e.querySelector(`code`):null)??e??t);A(`browser.do11y.code_copied`,{"browser.do11y.code.language":n,"browser.do11y.code.section":S(R(e??t),100),"browser.do11y.code.index":De(e)})}},!0)}let q=null,J={};function ke(){if(!h.trackSectionVisibility||typeof IntersectionObserver>`u`)return;let e=h.sectionVisibleThreshold*1e3;q=new IntersectionObserver(t=>{t.forEach(t=>{let n=t.target.getAttribute(`data-do11y-section-id`);if(n)if(t.isIntersecting)J[n]||(J[n]={start:Date.now(),reported:!1});else{if(J[n]&&!J[n].reported){let r=Date.now()-J[n].start;if(r>=e){let e=t.target.textContent?.trim()??``;A(f,{[i]:S(e,100),[a]:parseInt(t.target.tagName.charAt(1),10),[o]:Math.round(r/1e3)}),J[n].reported=!0}}delete J[n]}})},{threshold:.5}),Y()}function Y(){q&&document.querySelectorAll(`h2, h3`).forEach((e,t)=>{e.setAttribute(`data-do11y-section-id`,`section-`+t),q.observe(e)})}function X(){if(!q)return;let e=Date.now(),t=h.sectionVisibleThreshold*1e3;Object.keys(J).forEach(n=>{let r=J[n];if(r&&!r.reported){let s=e-r.start;if(s>=t){let e=typeof CSS<`u`&&typeof CSS.escape==`function`?CSS.escape(n):n.replace(/["\\]/g,`\\$&`),t=document.querySelector(`[data-do11y-section-id="`+e+`"]`);t&&A(f,{[i]:S(t.textContent?.trim()??``,100),[a]:parseInt(t.tagName.charAt(1),10),[o]:Math.round(s/1e3)})}}}),J={}}function Ae(){h.trackTabSwitches&&document.addEventListener(`click`,e=>{let t=`[role="tab"], .tabs button, .tabs a, .tabbed-labels label`,n=y(h.tabContainerSelector);n&&(t+=`, `+n+` button, `+n+` a, `+n+` label`);let r=e.target.closest(t);if(!r||r.getAttribute(`aria-selected`)===`true`||r.classList.contains(`active`)||r.classList.contains(`is-active`))return;let i=S(r.textContent,50);if(!i)return;let a=S(R(r),100);A(`browser.do11y.tab_switch`,{"browser.do11y.tab.label":i,"browser.do11y.tab.group":a,"browser.do11y.tab.is_default":!1})})}function je(){h.trackTocClicks&&document.addEventListener(`click`,e=>{let t=e.target.closest(`a`);if(!t)return;let n=ie(t);if(!n)return;let r=t.getAttribute(`href`),i=r?re(r):null;if(!i)return;let a=S(t.textContent,100),o=null;try{let e=i.slice(1),t=document.getElementById(e);t&&/^H[1-6]$/.test(t.tagName)&&(o=parseInt(t.tagName.charAt(1),10))}catch{}let s=n.querySelectorAll(`a[href*="#"]`),c=1;for(let e=0;e<s.length;e++)if(s[e]===t){c=e+1;break}A(`browser.do11y.toc_click`,{"browser.do11y.toc.heading":a,"browser.do11y.toc.heading_level":o,"browser.do11y.toc.position":c})},!0)}function Me(){h.trackFeedback&&document.addEventListener(`click`,e=>{let t=e.target.closest(`button, [role="button"], a`);if(!t||!t.closest(y(h.feedbackSelector)??`[class*="feedback"], [class*="helpful"], [class*="rating"], [class*="was-this"], [data-feedback]`))return;let n=(t.textContent??``).trim().toLowerCase(),r=(t.getAttribute(`aria-label`)??``).toLowerCase(),i=(t.getAttribute(`title`)??``).toLowerCase(),a=t.getAttribute(`data-value`)??t.getAttribute(`data-md-value`)??t.getAttribute(`data-feedback`),o=a&&/^[\w\s.,!?-]{1,50}$/.test(a)?a:null,s=null;o?s=o:/\byes\b|👍|thumbs.?up|helpful/i.test(n+` `+r+` `+i)?s=`yes`:/\bno\b|👎|thumbs.?down|not.?helpful/i.test(n+` `+r+` `+i)&&(s=`no`),s&&A(`browser.do11y.feedback`,{"browser.do11y.feedback.rating":s})})}function Z(){h.trackExpandCollapse&&(document.addEventListener(`toggle`,e=>{let t=e.target;if(t.tagName!==`DETAILS`)return;let n=t.querySelector(`summary`),r=S(n?n.textContent:``,100);A(p,{[s]:r,[c]:t.open?`expand`:`collapse`,[l]:S(R(t),100)})},!0),document.addEventListener(`click`,e=>{let t=e.target.closest(`[aria-expanded], [class*="accordion"] button, [class*="collapsible"] button`);if(!t||t.closest(`details`)||t.closest(`nav, [role="navigation"], header`))return;let n=t.getAttribute(`aria-expanded`)===`true`;A(p,{[s]:S(t.textContent,100),[c]:n?`collapse`:`expand`,[l]:S(R(t),100)})}))}let Q=null;function $(){if(window.Do11yConfig&&typeof window.Do11yConfig==`object`)for(let e in window.Do11yConfig)Object.prototype.hasOwnProperty.call(window.Do11yConfig,e)&&Object.prototype.hasOwnProperty.call(h,e)&&(h[e]=window.Do11yConfig[e]);let e=document.querySelector(`meta[name="do11y-destination"]`);if(e){let t=e.getAttribute(`content`);(t===`supabase`||t===`http`||t===`otlp`)&&(h.destination=t)}let t=document.querySelector(`meta[name="do11y-url"]`);t&&(h.supabaseUrl=t.getAttribute(`content`)??h.supabaseUrl);let n=document.querySelector(`meta[name="do11y-key"]`);n&&(h.supabaseKey=n.getAttribute(`content`)??h.supabaseKey);let r=document.querySelector(`meta[name="do11y-table"]`);r&&(h.supabaseTable=r.getAttribute(`content`)??h.supabaseTable);let i=document.querySelector(`meta[name="do11y-endpoint"]`);i&&(h.endpoint=i.getAttribute(`content`)??h.endpoint);let a=document.querySelector(`meta[name="do11y-otlp-endpoint"]`);a&&(h.otelSdkEndpoint=a.getAttribute(`content`)??h.otelSdkEndpoint);let o=document.querySelector(`meta[name="do11y-otlp-headers"]`);if(o)try{let e=JSON.parse(o.getAttribute(`content`)??`{}`);typeof e==`object`&&e&&(h.otelSdkHeaders=e)}catch{}let s=document.querySelector(`meta[name="do11y-debug"]`);s&&s.getAttribute(`content`)===`true`&&(h.debug=!0);let c=document.querySelector(`meta[name="do11y-domains"]`);if(c){let e=c.getAttribute(`content`);e&&(h.allowedDomains=e.split(`,`).map(e=>e.trim()))}let l=document.querySelector(`meta[name="do11y-framework"]`);l&&(h.framework=l.getAttribute(`content`)??h.framework);let u=document.querySelector(`meta[name="do11y-use-otel-instrumentations"]`);if(u&&u.getAttribute(`content`)===`true`&&(h.useOtelBrowserInstrumentations=!0),te(),h.debug){let e=h.destination===`supabase`?!!h.supabaseKey:h.destination===`otlp`?!!h.otelSdkEndpoint:!!h.endpoint;console.log(`[Do11y] Initializing with config:`,{destination:h.destination,hasCredentials:e,framework:h.framework,allowedDomains:h.allowedDomains,respectDNT:h.respectDNT})}if(v()){k=!0,h.debug&&console.log(`[Do11y] Tracking disabled`);return}(h.destination===`supabase`?h.supabaseKey:h.destination===`otlp`?h.otelSdkEndpoint:h.endpoint)||h.debug&&(console.warn(`[Do11y] No destination configured. Events will not be sent.`),h.destination===`supabase`?console.warn(`[Do11y] Add <meta name="do11y-url"> and <meta name="do11y-key"> to enable.`):h.destination===`otlp`?console.warn(`[Do11y] Add <meta name="do11y-otlp-endpoint"> to enable.`):console.warn(`[Do11y] Add <meta name="do11y-endpoint"> to enable.`)),L(),be(),we(),Te(),Ee(),Oe(),ke(),Ae(),je(),Me(),Z();let d=window.location.pathname;Q=new MutationObserver(()=>{window.location.pathname!==d&&(d=window.location.pathname,K(),z=new Set,H=Date.now(),U=Date.now(),W=0,G=!0,L(),Y(),V())}),Q.observe(document.body,{childList:!0,subtree:!0}),window.addEventListener(`popstate`,()=>{window.location.pathname!==d&&(d=window.location.pathname,K(),z=new Set,H=Date.now(),U=Date.now(),W=0,G=!0,L(),Y(),V())}),Object.freeze(h),h.debug&&console.log(`[Do11y] Initialized successfully`)}function Ne(){Q&&=(Q.disconnect(),null),q&&=(X(),q.disconnect(),null),D&&=(clearTimeout(D),null),ye()}ee||(document.readyState===`loading`?document.addEventListener(`DOMContentLoaded`,$):$()),window.Do11y=window.Do11y??{getConfig:()=>({destination:h.destination,hasCredentials:h.destination===`supabase`?!!h.supabaseKey:h.destination===`otlp`?!!h.otelSdkEndpoint:!!h.endpoint,isDisabled:k,allowedDomains:h.allowedDomains,respectDNT:h.respectDNT}),flush:F,isEnabled:()=>k?!1:h.destination===`supabase`?!!h.supabaseKey:h.destination===`otlp`?!!h.otelSdkEndpoint:!!h.endpoint,getQueueSize:()=>E.length,version:m}})();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Do11y configuration example.
|
|
3
3
|
*
|
|
4
4
|
* Copy this file alongside do11y.js in your docs site and rename it to
|
|
5
|
-
* do11y-config.js. Set the values below to match your
|
|
5
|
+
* do11y-config.js. Set the values below to match your setup.
|
|
6
6
|
*
|
|
7
7
|
* This file must load before do11y.js. For frameworks that auto-include
|
|
8
8
|
* all .js files (like Mintlify), alphabetical ordering handles this
|
|
@@ -10,20 +10,41 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Any option from the config object in do11y.js can be set here.
|
|
12
12
|
* See the README for the full list.
|
|
13
|
+
*
|
|
14
|
+
* All events use OpenTelemetry semantic convention attribute naming.
|
|
13
15
|
*/
|
|
14
16
|
window.Do11yConfig = {
|
|
15
|
-
// Destination: 'supabase' (default) or '
|
|
17
|
+
// Destination: 'supabase' (default), 'http', or 'otlp'
|
|
16
18
|
destination: 'supabase',
|
|
17
19
|
|
|
18
|
-
//
|
|
20
|
+
// ── Supabase (default) ─────────────────────────────────────────────────
|
|
21
|
+
// Required for Supabase destination:
|
|
19
22
|
supabaseUrl: 'https://YOUR_PROJECT.supabase.co',
|
|
20
|
-
|
|
21
|
-
// Required: Supabase publishable key (starts with sb_publishable_).
|
|
22
23
|
supabaseKey: 'sb_publishable_YOUR_KEY',
|
|
23
24
|
|
|
24
25
|
// Optional: Table name (default: 'do11y_events').
|
|
25
26
|
supabaseTable: 'do11y_events',
|
|
26
27
|
|
|
28
|
+
// ── Generic HTTP ───────────────────────────────────────────────────────
|
|
29
|
+
// destination: 'http',
|
|
30
|
+
// endpoint: 'https://your-endpoint.com/events',
|
|
31
|
+
// headers: { 'Authorization': 'Bearer your-token' },
|
|
32
|
+
//
|
|
33
|
+
// Optional: transform the event array before sending.
|
|
34
|
+
// Default: sends [event, event, ...]
|
|
35
|
+
// bodyTransform: (events) => events.map(e => ({ payload: e })),
|
|
36
|
+
|
|
37
|
+
// ── OTLP (OpenTelemetry SDK) ──────────────────────────────────────────
|
|
38
|
+
// destination: 'otlp',
|
|
39
|
+
// otelSdkEndpoint: 'https://otlp.grafana.com/otlp',
|
|
40
|
+
// otelSdkHeaders: { 'Authorization': 'Bearer your-token' },
|
|
41
|
+
// otelSdkServiceName: 'my-docs',
|
|
42
|
+
//
|
|
43
|
+
// ⚠️ CORS: Cloud OTLP endpoints (Grafana, Datadoc, etc.) do not
|
|
44
|
+
// return CORS headers, so browsers block direct cross-origin POSTs.
|
|
45
|
+
// To use from a browser, run an OTel Collector or CORS proxy in front.
|
|
46
|
+
// See https://docservable.com/configuration#otlp for details.
|
|
47
|
+
|
|
27
48
|
// Documentation framework. Supported values:
|
|
28
49
|
// 'mintlify', 'docusaurus', 'nextra', 'starlight', 'mkdocs-material',
|
|
29
50
|
// 'vitepress', 'custom'
|
|
@@ -32,9 +53,4 @@ window.Do11yConfig = {
|
|
|
32
53
|
// Optional: restrict which domains may send data.
|
|
33
54
|
// Set to null to allow any domain.
|
|
34
55
|
// allowedDomains: ['docs.example.com'],
|
|
35
|
-
|
|
36
|
-
// --- Alternative: Generic HTTP destination ---
|
|
37
|
-
// destination: 'http',
|
|
38
|
-
// httpEndpoint: 'https://your-endpoint.com/events',
|
|
39
|
-
// httpHeaders: { 'Authorization': 'Bearer your-token' },
|
|
40
56
|
};
|