@manototh/do11y 0.0.3 → 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 +319 -140
- package/dist/do11y.min.js +1 -1
- package/examples/do11y-config.example.js +27 -11
- 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,12 +114,13 @@
|
|
|
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: {
|
|
49
122
|
searchSelector: "#search-bar-entry, #search-bar-entry-mobile, [class*=\"search\"]",
|
|
50
|
-
copyButtonSelector: "[class*=\"copy\"], button[aria-label*=\"copy\" i]",
|
|
123
|
+
copyButtonSelector: "button[class*=\"copy\"], button[aria-label*=\"copy\" i]",
|
|
51
124
|
codeBlockSelector: "pre, [class*=\"code\"]",
|
|
52
125
|
navigationSelector: "nav, [role=\"navigation\"], #navbar, #sidebar, [class*=\"nav\"], [class*=\"sidebar\"]",
|
|
53
126
|
footerSelector: "footer, [role=\"contentinfo\"], [class*=\"footer\"]",
|
|
@@ -78,17 +151,6 @@
|
|
|
78
151
|
tocSelector: ".nextra-toc, [class*=\"toc\"]",
|
|
79
152
|
feedbackSelector: "[class*=\"feedback\"], [class*=\"helpful\"]"
|
|
80
153
|
},
|
|
81
|
-
gitbook: {
|
|
82
|
-
searchSelector: "[data-testid*=\"search\"], button[aria-label*=\"search\" i]",
|
|
83
|
-
copyButtonSelector: "[class*=\"copy\"], button[aria-label*=\"copy\" i]",
|
|
84
|
-
codeBlockSelector: "pre, code, [class*=\"code\"]",
|
|
85
|
-
navigationSelector: "nav, [role=\"navigation\"], [class*=\"nav\"], [class*=\"sidebar\"]",
|
|
86
|
-
footerSelector: "footer, [role=\"contentinfo\"], [class*=\"footer\"]",
|
|
87
|
-
contentSelector: "main, article, [role=\"main\"], [class*=\"content\"]",
|
|
88
|
-
tabContainerSelector: "[role=\"tablist\"], [class*=\"tab\"]",
|
|
89
|
-
tocSelector: "[class*=\"table-of-contents\"], [class*=\"toc\"], [class*=\"page-outline\"]",
|
|
90
|
-
feedbackSelector: "[class*=\"feedback\"], [class*=\"helpful\"], [class*=\"rating\"]"
|
|
91
|
-
},
|
|
92
154
|
"mkdocs-material": {
|
|
93
155
|
searchSelector: ".md-search__input",
|
|
94
156
|
copyButtonSelector: ".md-clipboard, .md-code__button[title=\"Copy to clipboard\"]",
|
|
@@ -110,6 +172,17 @@
|
|
|
110
172
|
tabContainerSelector: ".vp-code-group .tabs, [role=\"tablist\"]",
|
|
111
173
|
tocSelector: ".VPDocAsideOutline, .VPLocalNavOutlineDropdown, a.outline-link",
|
|
112
174
|
feedbackSelector: "[class*=\"feedback\"], [class*=\"helpful\"]"
|
|
175
|
+
},
|
|
176
|
+
starlight: {
|
|
177
|
+
searchSelector: "site-search button[data-open-modal], sl-doc-search .DocSearch-Button, button[aria-label*=\"search\" i]",
|
|
178
|
+
copyButtonSelector: ".expressive-code .copy button, .copy button[data-code]",
|
|
179
|
+
codeBlockSelector: ".expressive-code pre, pre",
|
|
180
|
+
navigationSelector: "nav, [role=\"navigation\"], [class*=\"sidebar\"]",
|
|
181
|
+
footerSelector: "footer, [role=\"contentinfo\"], [class*=\"footer\"]",
|
|
182
|
+
contentSelector: "main, .sl-markdown-content, [role=\"main\"]",
|
|
183
|
+
tabContainerSelector: "starlight-tabs [role=\"tablist\"], [role=\"tablist\"]",
|
|
184
|
+
tocSelector: ".right-sidebar-panel, starlight-toc, mobile-starlight-toc",
|
|
185
|
+
feedbackSelector: "[class*=\"feedback\"], [class*=\"helpful\"]"
|
|
113
186
|
}
|
|
114
187
|
};
|
|
115
188
|
const SELECTOR_KEYS = [
|
|
@@ -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;
|
|
@@ -213,6 +280,11 @@
|
|
|
213
280
|
if (fromClass) return fromClass;
|
|
214
281
|
const langText = el.querySelector(":scope > span.lang")?.textContent?.trim();
|
|
215
282
|
if (langText) return langText;
|
|
283
|
+
const deepLang = el.querySelector("[data-language], [data-lang], [data-code-lang], [class*=\"language-\"], [language]");
|
|
284
|
+
if (deepLang) {
|
|
285
|
+
const dl = deepLang.getAttribute("language") ?? deepLang.getAttribute("data-language") ?? deepLang.getAttribute("data-lang") ?? deepLang.getAttribute("data-code-lang") ?? languageFromClassName(getElementClassName(deepLang));
|
|
286
|
+
if (dl) return dl;
|
|
287
|
+
}
|
|
216
288
|
}
|
|
217
289
|
return "unknown";
|
|
218
290
|
}
|
|
@@ -225,10 +297,10 @@
|
|
|
225
297
|
return null;
|
|
226
298
|
}
|
|
227
299
|
function resolveTocContainer(link) {
|
|
228
|
-
const selector = validateSelector(config.tocSelector) ?? ".table-of-contents, .VPDocAsideOutline, .VPLocalNavOutlineDropdown, [class*=\"toc\"], [class*=\"TableOfContents\"], [class*=\"page-outline\"]";
|
|
300
|
+
const selector = validateSelector(config.tocSelector) ?? ".table-of-contents, .VPDocAsideOutline, .VPLocalNavOutlineDropdown, [class*=\"toc\"], [class*=\"TableOfContents\"], [class*=\"page-outline\"], .right-sidebar-panel, starlight-toc";
|
|
229
301
|
let container = link.closest(selector);
|
|
230
302
|
if (!container) return null;
|
|
231
|
-
if (container === link || container.tagName === "A") container = link.closest(".VPDocAsideOutline, .VPLocalNavOutlineDropdown, nav, aside") ?? container.parentElement;
|
|
303
|
+
if (container === link || container.tagName === "A") container = link.closest(".VPDocAsideOutline, .VPLocalNavOutlineDropdown, nav, aside, .right-sidebar-panel, starlight-toc") ?? container.parentElement;
|
|
232
304
|
return container;
|
|
233
305
|
}
|
|
234
306
|
function sanitizeText(text, maxLength) {
|
|
@@ -302,11 +374,11 @@
|
|
|
302
374
|
}
|
|
303
375
|
function getBrowserContext() {
|
|
304
376
|
return {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
310
382
|
};
|
|
311
383
|
}
|
|
312
384
|
function categorizeViewport() {
|
|
@@ -455,38 +527,47 @@
|
|
|
455
527
|
}
|
|
456
528
|
function getPageInfo() {
|
|
457
529
|
return {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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)
|
|
462
534
|
};
|
|
463
535
|
}
|
|
464
536
|
let eventQueue = [];
|
|
465
537
|
let flushTimeout = null;
|
|
466
538
|
const lastEventTime = {};
|
|
467
539
|
let isDisabled = false;
|
|
468
|
-
function queueEvent(
|
|
540
|
+
function queueEvent(eventName, eventData) {
|
|
469
541
|
if (isDisabled) return;
|
|
470
542
|
const now = Date.now();
|
|
471
|
-
if (config.rateLimitMs > 0 && lastEventTime[
|
|
472
|
-
if (now - lastEventTime[
|
|
473
|
-
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);
|
|
474
546
|
return;
|
|
475
547
|
}
|
|
476
548
|
}
|
|
477
|
-
lastEventTime[
|
|
549
|
+
lastEventTime[eventName] = now;
|
|
478
550
|
const session = getSession();
|
|
479
551
|
const event = {
|
|
480
552
|
_time: (/* @__PURE__ */ new Date()).toISOString(),
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
553
|
+
eventName,
|
|
554
|
+
[ATTR_DO11Y_DO11Y_VERSION]: VERSION,
|
|
555
|
+
[ATTR_SESSION_ID]: session.id,
|
|
556
|
+
[ATTR_DO11Y_SESSION_PAGE_COUNT]: session.pageCount,
|
|
485
557
|
...getPageInfo(),
|
|
486
558
|
...getBrowserContext(),
|
|
487
559
|
...eventData
|
|
488
560
|
};
|
|
489
|
-
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
|
+
}
|
|
490
571
|
eventQueue.push(event);
|
|
491
572
|
if (eventQueue.length > 100) {
|
|
492
573
|
eventQueue = eventQueue.slice(-100);
|
|
@@ -499,6 +580,7 @@
|
|
|
499
580
|
if (flushTimeout) return;
|
|
500
581
|
flushTimeout = setTimeout(flush, config.flushInterval);
|
|
501
582
|
}
|
|
583
|
+
let _otelLogger = null;
|
|
502
584
|
function validateSupabaseUrl(url) {
|
|
503
585
|
try {
|
|
504
586
|
const parsed = new URL(url);
|
|
@@ -509,7 +591,7 @@
|
|
|
509
591
|
return false;
|
|
510
592
|
}
|
|
511
593
|
}
|
|
512
|
-
function
|
|
594
|
+
function validateEndpoint(url) {
|
|
513
595
|
try {
|
|
514
596
|
const parsed = new URL(url);
|
|
515
597
|
if (parsed.protocol !== "https:") return false;
|
|
@@ -542,37 +624,90 @@
|
|
|
542
624
|
return true;
|
|
543
625
|
}
|
|
544
626
|
if (config.destination === "http") {
|
|
545
|
-
if (!config.
|
|
627
|
+
if (!config.endpoint) {
|
|
546
628
|
if (config.debug) console.warn("[Do11y] No HTTP endpoint configured");
|
|
547
629
|
return false;
|
|
548
630
|
}
|
|
549
|
-
if (!
|
|
631
|
+
if (!validateEndpoint(config.endpoint)) {
|
|
550
632
|
if (config.debug) console.warn("[Do11y] Invalid HTTP endpoint. Must be HTTPS and not a private address.");
|
|
551
633
|
return false;
|
|
552
634
|
}
|
|
553
635
|
return true;
|
|
554
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
|
+
}
|
|
555
647
|
if (config.debug) console.warn("[Do11y] Unknown destination:", config.destination);
|
|
556
648
|
return false;
|
|
557
649
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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
|
|
568
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);
|
|
569
704
|
return {
|
|
570
|
-
url: config.
|
|
705
|
+
url: config.endpoint,
|
|
571
706
|
headers: {
|
|
572
707
|
"Content-Type": "application/json",
|
|
573
|
-
...config.
|
|
708
|
+
...config.headers
|
|
574
709
|
},
|
|
575
|
-
body: JSON.stringify(events)
|
|
710
|
+
body: JSON.stringify(bodyTransform(events))
|
|
576
711
|
};
|
|
577
712
|
}
|
|
578
713
|
function flush(retriesLeft) {
|
|
@@ -587,12 +722,25 @@
|
|
|
587
722
|
eventQueue = [];
|
|
588
723
|
sendEvents(buildRequest(events), events, retries);
|
|
589
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
|
+
}
|
|
590
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");
|
|
591
738
|
fetch(req.url, {
|
|
592
739
|
method: "POST",
|
|
593
740
|
headers: req.headers,
|
|
594
741
|
body: req.body,
|
|
595
|
-
keepalive: true
|
|
742
|
+
keepalive: true,
|
|
743
|
+
mode: crossOrigin ? "cors" : "same-origin"
|
|
596
744
|
}).then((response) => {
|
|
597
745
|
if (response.ok) {
|
|
598
746
|
if (config.debug) console.log("[Do11y] Flushed", events.length, "events");
|
|
@@ -607,19 +755,29 @@
|
|
|
607
755
|
return;
|
|
608
756
|
}
|
|
609
757
|
if (config.debug) response.text().then((text) => {
|
|
610
|
-
|
|
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);
|
|
611
761
|
}).catch(() => {});
|
|
612
762
|
}).catch((err) => {
|
|
613
763
|
if (retriesLeft > 0) {
|
|
614
|
-
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
|
+
}
|
|
615
768
|
eventQueue = events.concat(eventQueue);
|
|
616
769
|
setTimeout(() => {
|
|
617
770
|
flush(retriesLeft - 1);
|
|
618
771
|
}, config.retryDelay * (config.maxRetries - retriesLeft + 1));
|
|
619
|
-
} 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);
|
|
620
773
|
});
|
|
621
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
|
+
*/
|
|
622
779
|
function flushSync() {
|
|
780
|
+
if (config.destination === "otlp") return;
|
|
623
781
|
if (eventQueue.length === 0) return;
|
|
624
782
|
if (!validateConfig()) return;
|
|
625
783
|
const events = eventQueue;
|
|
@@ -644,12 +802,12 @@
|
|
|
644
802
|
session.aiPlatform = referrerInfo.aiPlatform;
|
|
645
803
|
saveSession(session);
|
|
646
804
|
}
|
|
647
|
-
queueEvent(
|
|
648
|
-
referrerDomain,
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
|
653
811
|
});
|
|
654
812
|
}
|
|
655
813
|
function setupLinkTracking() {
|
|
@@ -674,14 +832,14 @@
|
|
|
674
832
|
} catch {}
|
|
675
833
|
if (linkType === "internal" && !config.trackInternalLinks) return;
|
|
676
834
|
if (linkType === "external" && !config.trackOutboundLinks) return;
|
|
677
|
-
queueEvent(
|
|
678
|
-
linkType,
|
|
679
|
-
|
|
680
|
-
targetDomain,
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
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)
|
|
685
843
|
});
|
|
686
844
|
flush();
|
|
687
845
|
}, true);
|
|
@@ -728,7 +886,7 @@
|
|
|
728
886
|
/**
|
|
729
887
|
* Track scroll depth.
|
|
730
888
|
*
|
|
731
|
-
* Some frameworks (
|
|
889
|
+
* Some frameworks (MkDocs Material) use container-based
|
|
732
890
|
* scrolling where the window itself never scrolls. We detect the scrollable
|
|
733
891
|
* container by walking up from the content element and listen on it in
|
|
734
892
|
* addition to the window.
|
|
@@ -769,7 +927,6 @@
|
|
|
769
927
|
* the content without scrolling.
|
|
770
928
|
*/
|
|
771
929
|
function checkScrollDepth() {
|
|
772
|
-
if (isEngagementExcludedPath()) return;
|
|
773
930
|
let scrollTop;
|
|
774
931
|
let totalHeight;
|
|
775
932
|
let viewportHeight;
|
|
@@ -787,9 +944,9 @@
|
|
|
787
944
|
config.scrollThresholds.forEach((threshold) => {
|
|
788
945
|
if (!trackedScrollDepths.has(threshold)) {
|
|
789
946
|
trackedScrollDepths.add(threshold);
|
|
790
|
-
queueEvent(
|
|
791
|
-
threshold,
|
|
792
|
-
|
|
947
|
+
queueEvent(EVENT_SCROLL_DEPTH, {
|
|
948
|
+
[ATTR_DO11Y_SCROLL_THRESHOLD]: threshold,
|
|
949
|
+
[ATTR_DO11Y_SCROLL_PERCENT]: 100
|
|
793
950
|
});
|
|
794
951
|
}
|
|
795
952
|
});
|
|
@@ -799,9 +956,9 @@
|
|
|
799
956
|
config.scrollThresholds.forEach((threshold) => {
|
|
800
957
|
if (scrollPercent >= threshold && !trackedScrollDepths.has(threshold)) {
|
|
801
958
|
trackedScrollDepths.add(threshold);
|
|
802
|
-
queueEvent(
|
|
803
|
-
threshold,
|
|
804
|
-
scrollPercent
|
|
959
|
+
queueEvent(EVENT_SCROLL_DEPTH, {
|
|
960
|
+
[ATTR_DO11Y_SCROLL_THRESHOLD]: threshold,
|
|
961
|
+
[ATTR_DO11Y_SCROLL_PERCENT]: scrollPercent
|
|
805
962
|
});
|
|
806
963
|
}
|
|
807
964
|
});
|
|
@@ -811,7 +968,6 @@
|
|
|
811
968
|
let totalActiveTime = 0;
|
|
812
969
|
let isPageVisible = true;
|
|
813
970
|
function emitPageExit() {
|
|
814
|
-
if (isEngagementExcludedPath()) return;
|
|
815
971
|
if (isPageVisible) totalActiveTime += Date.now() - lastActivityTime;
|
|
816
972
|
const totalTime = Date.now() - pageLoadTime;
|
|
817
973
|
const engagementRatio = totalTime > 0 ? totalActiveTime / totalTime : 0;
|
|
@@ -821,13 +977,13 @@
|
|
|
821
977
|
});
|
|
822
978
|
flushVisibleSections();
|
|
823
979
|
const session = getSession();
|
|
824
|
-
queueEvent(
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
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
|
|
831
987
|
});
|
|
832
988
|
}
|
|
833
989
|
function setupEngagementTracking() {
|
|
@@ -849,10 +1005,10 @@
|
|
|
849
1005
|
}
|
|
850
1006
|
function setupSearchTracking() {
|
|
851
1007
|
document.addEventListener("click", (e) => {
|
|
852
|
-
if (e.target.closest(config.searchSelector)) queueEvent(
|
|
853
|
-
});
|
|
1008
|
+
if (e.target.closest(config.searchSelector)) queueEvent(EVENT_SEARCH_OPENED, {});
|
|
1009
|
+
}, true);
|
|
854
1010
|
document.addEventListener("keydown", (e) => {
|
|
855
|
-
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" });
|
|
856
1012
|
});
|
|
857
1013
|
}
|
|
858
1014
|
function getCodeBlockIndex(codeBlock) {
|
|
@@ -867,11 +1023,12 @@
|
|
|
867
1023
|
document.addEventListener("click", (e) => {
|
|
868
1024
|
const copyButton = e.target.closest(config.copyButtonSelector);
|
|
869
1025
|
if (copyButton) {
|
|
870
|
-
const codeBlock = copyButton.closest("[class*=\"language-\"]") ?? copyButton.closest(config.codeBlockSelector) ?? copyButton.closest("div, section")?.querySelector("pre") ?? copyButton.parentElement?.querySelector("pre") ?? null;
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
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;
|
|
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)
|
|
875
1032
|
});
|
|
876
1033
|
}
|
|
877
1034
|
}, true);
|
|
@@ -895,10 +1052,11 @@
|
|
|
895
1052
|
if (sectionTimers[id] && !sectionTimers[id].reported) {
|
|
896
1053
|
const elapsed = Date.now() - sectionTimers[id].start;
|
|
897
1054
|
if (elapsed >= threshold) {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
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)
|
|
902
1060
|
});
|
|
903
1061
|
sectionTimers[id].reported = true;
|
|
904
1062
|
}
|
|
@@ -927,10 +1085,10 @@
|
|
|
927
1085
|
if (elapsed >= threshold) {
|
|
928
1086
|
const escapedId = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(id) : id.replace(/["\\]/g, "\\$&");
|
|
929
1087
|
const el = document.querySelector("[data-do11y-section-id=\"" + escapedId + "\"]");
|
|
930
|
-
if (el) queueEvent(
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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)
|
|
934
1092
|
});
|
|
935
1093
|
}
|
|
936
1094
|
}
|
|
@@ -948,10 +1106,11 @@
|
|
|
948
1106
|
if (tab.getAttribute("aria-selected") === "true" || tab.classList.contains("active") || tab.classList.contains("is-active")) return;
|
|
949
1107
|
const label = sanitizeText(tab.textContent, 50);
|
|
950
1108
|
if (!label) return;
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
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
|
|
955
1114
|
});
|
|
956
1115
|
});
|
|
957
1116
|
}
|
|
@@ -978,10 +1137,10 @@
|
|
|
978
1137
|
tocPosition = i + 1;
|
|
979
1138
|
break;
|
|
980
1139
|
}
|
|
981
|
-
queueEvent(
|
|
982
|
-
|
|
983
|
-
headingLevel,
|
|
984
|
-
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
|
|
985
1144
|
});
|
|
986
1145
|
}, true);
|
|
987
1146
|
}
|
|
@@ -1001,7 +1160,7 @@
|
|
|
1001
1160
|
else if (/\byes\b|👍|thumbs.?up|helpful/i.test(buttonText + " " + ariaLabel + " " + titleAttr)) rating = "yes";
|
|
1002
1161
|
else if (/\bno\b|👎|thumbs.?down|not.?helpful/i.test(buttonText + " " + ariaLabel + " " + titleAttr)) rating = "no";
|
|
1003
1162
|
if (!rating) return;
|
|
1004
|
-
queueEvent(
|
|
1163
|
+
queueEvent(EVENT_FEEDBACK, { [ATTR_DO11Y_FEEDBACK_RATING]: rating });
|
|
1005
1164
|
});
|
|
1006
1165
|
}
|
|
1007
1166
|
function setupExpandCollapseTracking() {
|
|
@@ -1010,10 +1169,11 @@
|
|
|
1010
1169
|
const details = e.target;
|
|
1011
1170
|
if (details.tagName !== "DETAILS") return;
|
|
1012
1171
|
const summary = details.querySelector("summary");
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
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)
|
|
1017
1177
|
});
|
|
1018
1178
|
}, true);
|
|
1019
1179
|
document.addEventListener("click", (e) => {
|
|
@@ -1022,10 +1182,10 @@
|
|
|
1022
1182
|
if (trigger.closest("details")) return;
|
|
1023
1183
|
if (trigger.closest("nav, [role=\"navigation\"], header")) return;
|
|
1024
1184
|
const wasExpanded = trigger.getAttribute("aria-expanded") === "true";
|
|
1025
|
-
queueEvent(
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
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)
|
|
1029
1189
|
});
|
|
1030
1190
|
});
|
|
1031
1191
|
}
|
|
@@ -1037,7 +1197,7 @@
|
|
|
1037
1197
|
const metaDestination = document.querySelector("meta[name=\"do11y-destination\"]");
|
|
1038
1198
|
if (metaDestination) {
|
|
1039
1199
|
const dest = metaDestination.getAttribute("content");
|
|
1040
|
-
if (dest === "supabase" || dest === "http") config.destination = dest;
|
|
1200
|
+
if (dest === "supabase" || dest === "http" || dest === "otlp") config.destination = dest;
|
|
1041
1201
|
}
|
|
1042
1202
|
const metaUrl = document.querySelector("meta[name=\"do11y-url\"]");
|
|
1043
1203
|
if (metaUrl) config.supabaseUrl = metaUrl.getAttribute("content") ?? config.supabaseUrl;
|
|
@@ -1045,8 +1205,15 @@
|
|
|
1045
1205
|
if (metaKey) config.supabaseKey = metaKey.getAttribute("content") ?? config.supabaseKey;
|
|
1046
1206
|
const metaTable = document.querySelector("meta[name=\"do11y-table\"]");
|
|
1047
1207
|
if (metaTable) config.supabaseTable = metaTable.getAttribute("content") ?? config.supabaseTable;
|
|
1048
|
-
const
|
|
1049
|
-
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 {}
|
|
1050
1217
|
const metaDebug = document.querySelector("meta[name=\"do11y-debug\"]");
|
|
1051
1218
|
if (metaDebug && metaDebug.getAttribute("content") === "true") config.debug = true;
|
|
1052
1219
|
const metaDomains = document.querySelector("meta[name=\"do11y-domains\"]");
|
|
@@ -1056,23 +1223,30 @@
|
|
|
1056
1223
|
}
|
|
1057
1224
|
const metaFramework = document.querySelector("meta[name=\"do11y-framework\"]");
|
|
1058
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;
|
|
1059
1228
|
applyFrameworkSelectors();
|
|
1060
|
-
if (config.debug)
|
|
1061
|
-
destination: config.destination
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
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
|
+
}
|
|
1067
1239
|
if (shouldDisableTracking()) {
|
|
1068
1240
|
isDisabled = true;
|
|
1069
1241
|
if (config.debug) console.log("[Do11y] Tracking disabled");
|
|
1070
1242
|
return;
|
|
1071
1243
|
}
|
|
1072
|
-
if (!(config.destination === "supabase" ? !!config.supabaseKey : !!config.
|
|
1244
|
+
if (!(config.destination === "supabase" ? !!config.supabaseKey : config.destination === "otlp" ? !!config.otelSdkEndpoint : !!config.endpoint)) {
|
|
1073
1245
|
if (config.debug) {
|
|
1074
1246
|
console.warn("[Do11y] No destination configured. Events will not be sent.");
|
|
1075
|
-
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.");
|
|
1076
1250
|
}
|
|
1077
1251
|
}
|
|
1078
1252
|
trackPageView();
|
|
@@ -1143,13 +1317,18 @@
|
|
|
1143
1317
|
window.Do11y = window.Do11y ?? {
|
|
1144
1318
|
getConfig: () => ({
|
|
1145
1319
|
destination: config.destination,
|
|
1146
|
-
hasCredentials: config.destination === "supabase" ? !!config.supabaseKey : !!config.
|
|
1320
|
+
hasCredentials: config.destination === "supabase" ? !!config.supabaseKey : config.destination === "otlp" ? !!config.otelSdkEndpoint : !!config.endpoint,
|
|
1147
1321
|
isDisabled,
|
|
1148
1322
|
allowedDomains: config.allowedDomains,
|
|
1149
1323
|
respectDNT: config.respectDNT
|
|
1150
1324
|
}),
|
|
1151
1325
|
flush,
|
|
1152
|
-
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
|
+
},
|
|
1153
1332
|
getQueueSize: () => eventQueue.length,
|
|
1154
1333
|
version: VERSION
|
|
1155
1334
|
};
|
package/dist/do11y.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){let e=`0.0.3`,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:`[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"]`},gitbook:{searchSelector:`[data-testid*="search"], button[aria-label*="search" i]`,copyButtonSelector:`[class*="copy"], button[aria-label*="copy" i]`,codeBlockSelector:`pre, code, [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:`[class*="table-of-contents"], [class*="toc"], [class*="page-outline"]`,feedbackSelector:`[class*="feedback"], [class*="helpful"], [class*="rating"]`},"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"]`}},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}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"]`,r=e.closest(t);return r?((r===e||r.tagName===`A`)&&(r=e.closest(`.VPDocAsideOutline, .VPLocalNavOutlineDropdown, nav, aside`)??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`,{})}),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-"]`)??t.closest(n.codeBlockSelector)??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-"]`)??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,31 +10,47 @@
|
|
|
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
|
-
// 'mintlify', 'docusaurus', 'nextra', '
|
|
49
|
+
// 'mintlify', 'docusaurus', 'nextra', 'starlight', 'mkdocs-material',
|
|
29
50
|
// 'vitepress', 'custom'
|
|
30
51
|
framework: 'mintlify',
|
|
31
52
|
|
|
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
|
};
|