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