@tamsensedev/dataclient 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +19 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +19 -25
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +19 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -813,34 +813,20 @@ var Sender = class {
|
|
|
813
813
|
deviceId;
|
|
814
814
|
queue = [];
|
|
815
815
|
timer = null;
|
|
816
|
-
|
|
816
|
+
flushPromise = Promise.resolve();
|
|
817
817
|
add(event) {
|
|
818
818
|
this.queue.push(event);
|
|
819
|
-
|
|
819
|
+
const isRrwebSnapshot = event.event === "rrweb" && event.rrwebEvent?.type === 2;
|
|
820
|
+
if (isRrwebSnapshot || this.queue.length >= this.batchSize) {
|
|
820
821
|
this.flush();
|
|
821
822
|
}
|
|
822
823
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
this.isFlushing = true;
|
|
828
|
-
const events = this.queue.splice(0);
|
|
829
|
-
const batch = this.buildBatch(events);
|
|
830
|
-
const json = JSON.stringify(batch);
|
|
831
|
-
const url = this.buildUrl();
|
|
832
|
-
const success = await this.send(json, url);
|
|
833
|
-
if (!success) {
|
|
834
|
-
this.queue.unshift(...events);
|
|
835
|
-
}
|
|
836
|
-
this.isFlushing = false;
|
|
824
|
+
flush() {
|
|
825
|
+
this.flushPromise = this.flushPromise.then(() => this.doFlush());
|
|
837
826
|
}
|
|
838
827
|
flushSync() {
|
|
839
|
-
if (this.queue.length === 0)
|
|
840
|
-
return;
|
|
841
|
-
}
|
|
828
|
+
if (this.queue.length === 0) return;
|
|
842
829
|
const events = this.queue.splice(0);
|
|
843
|
-
if (events.length === 0) return;
|
|
844
830
|
const url = this.buildUrl();
|
|
845
831
|
let chunk = [];
|
|
846
832
|
let chunkSize = 0;
|
|
@@ -864,6 +850,17 @@ var Sender = class {
|
|
|
864
850
|
}
|
|
865
851
|
this.flushSync();
|
|
866
852
|
}
|
|
853
|
+
async doFlush() {
|
|
854
|
+
if (this.queue.length === 0) return;
|
|
855
|
+
const events = this.queue.splice(0);
|
|
856
|
+
const batch = this.buildBatch(events);
|
|
857
|
+
const json = JSON.stringify(batch);
|
|
858
|
+
const url = this.buildUrl();
|
|
859
|
+
const success = await this.send(json, url);
|
|
860
|
+
if (!success) {
|
|
861
|
+
this.queue.unshift(...events);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
867
864
|
sendBeacon(events, url) {
|
|
868
865
|
const batch = this.buildBatch(events);
|
|
869
866
|
const json = JSON.stringify(batch);
|
|
@@ -895,12 +892,9 @@ var Sender = class {
|
|
|
895
892
|
const response = await fetch(url, {
|
|
896
893
|
method: "POST",
|
|
897
894
|
headers: { "Content-Type": "application/json" },
|
|
898
|
-
body: json
|
|
899
|
-
keepalive: true
|
|
895
|
+
body: json
|
|
900
896
|
});
|
|
901
|
-
if (response.ok)
|
|
902
|
-
return true;
|
|
903
|
-
}
|
|
897
|
+
if (response.ok) return true;
|
|
904
898
|
} catch {
|
|
905
899
|
}
|
|
906
900
|
if (attempt < MAX_RETRIES) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/dom/mask.ts","../src/dom/icon.ts","../src/dom/serializer.ts","../src/dom/viewport.ts","../src/trackers/rrweb.ts","../src/trackers/action.ts","../src/trackers/mutation.ts","../src/trackers/snapshot.ts","../src/utils/identity.ts","../src/utils/sender.ts"],"sourcesContent":["import type { Config, Tracker } from './types'\nimport { ActionTracker } from './trackers/action'\nimport { MutationTracker } from './trackers/mutation'\nimport { RrwebTracker } from './trackers/rrweb'\nimport { SnapshotTracker } from './trackers/snapshot'\nimport { generateId, getDeviceId } from './utils/identity'\nimport { Sender } from './utils/sender'\n\nexport type * from './types'\n\nconst defaults: Config = {\n endpoint: 'https://my.tamsense.com/api/scenes2',\n debug: false,\n batchSize: 5,\n flushInterval: 5000,\n checkpointInterval: 30000,\n mutationDebounce: 200,\n inputDebounce: 1000,\n sessionIdKey: 'sc2_sid',\n deviceIdKey: 'sc2_did',\n apiKey: '',\n}\n\nexport class DataClient {\n private sender: Sender\n private trackers: Tracker[] = []\n private config: Config\n\n constructor(options?: Partial<Config>) {\n this.config = { ...defaults, ...options }\n\n const sessionId = generateId()\n const deviceId = getDeviceId(this.config.deviceIdKey)\n\n this.sender = new Sender(\n this.config.endpoint,\n this.config.apiKey,\n this.config.batchSize,\n sessionId,\n deviceId,\n this.config.flushInterval,\n )\n\n const snapshotTracker = new SnapshotTracker(this.config, this.sender)\n const mutationTracker = new MutationTracker(this.config, this.sender, () => snapshotTracker.markMutation())\n const actionTracker = new ActionTracker(this.config, this.sender)\n const rrwebTracker = new RrwebTracker(this.config, this.sender)\n\n this.trackers = [snapshotTracker, mutationTracker, actionTracker, rrwebTracker]\n this.trackers.forEach(t => t.start())\n\n const onLeave = () => {\n this.trackers.forEach(t => t.beforeUnload?.())\n this.sender.flushSync()\n }\n\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') onLeave()\n })\n window.addEventListener('pagehide', onLeave)\n }\n\n setUser(userId: string) {\n this.sender.add({ event: 'identify', timestamp: new Date().toISOString(), user_id: userId })\n }\n\n excludeSession(reason = '') {\n this.sender.add({ event: 'exclude', timestamp: new Date().toISOString(), reason })\n this.destroy()\n }\n\n destroy() {\n this.trackers.forEach(t => t.stop())\n this.trackers = []\n this.sender.destroy()\n }\n}\n","export const MASK_ATTR = 'dataclient-mask'\r\nexport const MASK_SELECTOR = `[${MASK_ATTR}]`\r\n\r\nexport const SKIP_TAGS = new Set(['script', 'style', 'noscript', 'svg', 'link', 'meta', 'head'])\r\n\r\nexport const RECORD_ATTRS = new Set([\r\n 'class',\r\n 'role',\r\n 'href',\r\n 'type',\r\n 'placeholder',\r\n 'disabled',\r\n 'hidden',\r\n 'aria-label',\r\n 'aria-selected',\r\n 'aria-expanded',\r\n 'aria-invalid',\r\n 'aria-busy',\r\n 'aria-checked',\r\n 'aria-hidden',\r\n 'value',\r\n 'name',\r\n 'id',\r\n 'for',\r\n 'target',\r\n 'data-state',\r\n MASK_ATTR,\r\n])\r\n\r\nexport const WATCH_ATTRS = [\r\n 'class',\r\n 'role',\r\n 'href',\r\n 'disabled',\r\n 'hidden',\r\n 'placeholder',\r\n 'aria-label',\r\n 'aria-selected',\r\n 'aria-expanded',\r\n 'aria-invalid',\r\n 'aria-busy',\r\n 'aria-checked',\r\n 'aria-hidden',\r\n 'data-state',\r\n 'value',\r\n]\r\n\r\nexport const TEXT_INPUT_TYPES = new Set(['text', 'email', 'password', 'search', 'tel', 'url', 'number'])\r\n\r\nexport const INTERACTIVE_TAGS = new Set(['button', 'a', 'input', 'select', 'textarea'])\r\nexport const INTERACTIVE_ROLES = new Set(['button', 'link', 'tab', 'menuitem', 'checkbox', 'radio', 'switch', 'option'])\r\n","import { MASK_SELECTOR } from '../constants'\r\n\r\nexport function isMasked(el: Element | Node | null): boolean {\r\n if (!el)\r\n return false\r\n const element = el.nodeType === Node.ELEMENT_NODE\r\n ? el as Element\r\n : el.parentElement\r\n return !!element?.closest(MASK_SELECTOR)\r\n}\r\n\r\nexport function maskText(text: string): string {\r\n if (!text) {\r\n return text\r\n }\r\n const visible = Math.max(1, Math.ceil(text.length * 0.2))\r\n return text.slice(0, visible) + '*'.repeat(text.length - visible)\r\n}\r\n\r\nexport function maskValue(el: Element | null, value: string): string {\r\n return isMasked(el) ? maskText(value) : value\r\n}\r\n","const ICON_CLASS_PATTERNS = [\r\n /\\bfa-([a-z0-9-]+)\\b/,\r\n /\\bmdi-([a-z0-9-]+)\\b/,\r\n /\\bbi-([a-z0-9-]+)\\b/,\r\n /\\bicon-([a-z0-9-]+)\\b/,\r\n /\\blucide-([a-z0-9-]+)\\b/,\r\n /\\bri-([a-z0-9-]+)\\b/,\r\n /\\btabler-icon-([a-z0-9-]+)\\b/,\r\n /\\bi-[a-z0-9-]+[:/]([a-z0-9-]+)\\b/,\r\n]\r\n\r\nconst MATERIAL_CLASS_PATTERN = /\\b(?:material-icons|material-symbols-[a-z]+)\\b/\r\nconst SVG_USE_HREF_PATTERN = /#(.+)/\r\n\r\nconst MAX_ICON_IMG_SIZE = 48\r\n\r\nexport function getElementIcon(el: HTMLElement): string | null {\r\n if (!el) {\r\n return null\r\n }\r\n\r\n const direct = detectIcon(el)\r\n if (direct) {\r\n return direct\r\n }\r\n\r\n for (const child of el.children) {\r\n const icon = detectIcon(child as HTMLElement)\r\n if (icon) {\r\n return icon\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction detectIcon(el: HTMLElement): string | null {\r\n if (!el?.tagName) {\r\n return null\r\n }\r\n\r\n const tag = el.tagName.toLowerCase()\r\n\r\n if (tag === 'svg') {\r\n return detectSvgIcon(el)\r\n }\r\n\r\n if (tag === 'i' || tag === 'span' || el.classList.contains('iconify')) {\r\n return detectFontIcon(el)\r\n }\r\n\r\n if (tag === 'img') {\r\n return detectImgIcon(el as HTMLImageElement)\r\n }\r\n\r\n const svg = el.querySelector('svg')\r\n if (svg) {\r\n return detectSvgIcon(svg)\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction detectSvgIcon(svg: Element): string | null {\r\n const lucide = svg.getAttribute('data-lucide')\r\n if (lucide) {\r\n return lucide\r\n }\r\n\r\n const dataIcon = svg.getAttribute('data-icon')\r\n if (dataIcon) {\r\n return dataIcon\r\n }\r\n\r\n const use = svg.querySelector('use')\r\n if (use) {\r\n const href = use.getAttribute('href') || use.getAttribute('xlink:href')\r\n if (href) {\r\n const match = href.match(SVG_USE_HREF_PATTERN)\r\n if (match) {\r\n return match[1]\r\n }\r\n }\r\n }\r\n\r\n const ariaLabel = svg.getAttribute('aria-label')\r\n if (ariaLabel) {\r\n return ariaLabel\r\n }\r\n\r\n const cls = typeof svg.className === 'string' ? svg.className : svg.getAttribute('class') || ''\r\n const fromClass = extractIconNameFromClass(cls)\r\n if (fromClass) {\r\n return fromClass\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction detectFontIcon(el: HTMLElement): string | null {\r\n const cls = typeof el.className === 'string' ? el.className : el.getAttribute('class') || ''\r\n\r\n if (MATERIAL_CLASS_PATTERN.test(cls)) {\r\n const text = el.textContent?.trim()\r\n if (text) {\r\n return text\r\n }\r\n }\r\n\r\n return extractIconNameFromClass(cls)\r\n}\r\n\r\nfunction detectImgIcon(img: HTMLImageElement): string | null {\r\n if (img.width > MAX_ICON_IMG_SIZE || img.height > MAX_ICON_IMG_SIZE) {\r\n return null\r\n }\r\n\r\n if (img.naturalWidth > MAX_ICON_IMG_SIZE || img.naturalHeight > MAX_ICON_IMG_SIZE) {\r\n return null\r\n }\r\n\r\n const alt = img.alt?.trim()\r\n if (alt) {\r\n return alt\r\n }\r\n\r\n const src = img.getAttribute('src')\r\n if (src) {\r\n const filename = src.split('/').pop()?.split('?')[0]?.split('.')[0]\r\n if (filename) {\r\n return filename\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction extractIconNameFromClass(cls: string): string | null {\r\n for (const pattern of ICON_CLASS_PATTERNS) {\r\n const match = cls.match(pattern)\r\n if (match) {\r\n return match[1]\r\n }\r\n }\r\n return null\r\n}\r\n","import type { SerializedNode } from '../types'\r\nimport { RECORD_ATTRS, SKIP_TAGS } from '../constants'\r\nimport { getElementIcon } from './icon'\r\nimport { isMasked, maskText } from './mask'\r\n\r\nlet nextId = 1\r\nconst nodeToId = new WeakMap<Node, number>()\r\nconst idToNode = new Map<number, Node>()\r\n\r\nexport function resetIds() {\r\n nextId = 1\r\n idToNode.clear()\r\n}\r\n\r\nexport function assignId(node: Node): number {\r\n const existing = nodeToId.get(node)\r\n if (existing) {\r\n return existing\r\n }\r\n const id = nextId++\r\n nodeToId.set(node, id)\r\n idToNode.set(id, node)\r\n return id\r\n}\r\n\r\nexport function getNodeId(node: Node): number | null {\r\n return nodeToId.get(node) ?? null\r\n}\r\n\r\nexport function getNodeById(id: number): Node | null {\r\n return idToNode.get(id) ?? null\r\n}\r\n\r\nexport function removeNodeId(id: number) {\r\n const node = idToNode.get(id)\r\n if (node) {\r\n nodeToId.delete(node)\r\n idToNode.delete(id)\r\n }\r\n}\r\n\r\nfunction isVisible(el: HTMLElement): boolean {\r\n if (el.hidden)\r\n return false\r\n if (el.getAttribute('aria-hidden') === 'true')\r\n return false\r\n if (!el.offsetParent && el.tagName !== 'BODY' && getComputedStyle(el).position !== 'fixed') {\r\n return false\r\n }\r\n return true\r\n}\r\n\r\nfunction getDirectText(el: HTMLElement): string {\r\n let text = ''\r\n for (const child of el.childNodes) {\r\n if (child.nodeType === Node.TEXT_NODE) {\r\n const t = child.textContent?.trim()\r\n if (t) {\r\n text += (text ? ' ' : '') + t\r\n }\r\n }\r\n }\r\n const truncated = text.slice(0, 200)\r\n return isMasked(el) ? maskText(truncated) : truncated\r\n}\r\n\r\nfunction getAttrs(el: HTMLElement): Record<string, string> | undefined {\r\n const attrs: Record<string, string> = {}\r\n let hasAttrs = false\r\n const masked = isMasked(el)\r\n\r\n for (const name of RECORD_ATTRS) {\r\n const value = el.getAttribute(name)\r\n if (value !== null && value !== '') {\r\n let v = value.slice(0, 200)\r\n if (masked && (name === 'value' || name === 'placeholder')) {\r\n v = maskText(v)\r\n }\r\n attrs[name] = v\r\n hasAttrs = true\r\n }\r\n }\r\n\r\n return hasAttrs ? attrs : undefined\r\n}\r\n\r\nfunction getRect(el: HTMLElement): SerializedNode['rect'] {\r\n const r = el.getBoundingClientRect()\r\n if (r.width === 0 && r.height === 0) {\r\n return undefined\r\n }\r\n return {\r\n x: Math.round(r.left + window.scrollX),\r\n y: Math.round(r.top + window.scrollY),\r\n w: Math.round(r.width),\r\n h: Math.round(r.height),\r\n }\r\n}\r\n\r\nexport function serializeNode(el: HTMLElement): SerializedNode | null {\r\n const tag = el.tagName?.toLowerCase()\r\n if (!tag || SKIP_TAGS.has(tag)) {\r\n return null\r\n }\r\n if (!isVisible(el)) {\r\n return null\r\n }\r\n\r\n const id = assignId(el)\r\n const text = getDirectText(el)\r\n const icon = getElementIcon(el)\r\n const attrs = getAttrs(el)\r\n const rect = getRect(el)\r\n\r\n const children: number[] = []\r\n for (const child of el.children) {\r\n const serialized = serializeNode(child as HTMLElement)\r\n if (serialized) {\r\n children.push(serialized.id)\r\n }\r\n }\r\n\r\n const node: SerializedNode = { id, tag }\r\n if (text) {\r\n node.text = text\r\n }\r\n if (icon) {\r\n node.icon = icon\r\n }\r\n if (attrs) {\r\n node.attrs = attrs\r\n }\r\n if (rect) {\r\n node.rect = rect\r\n }\r\n if (children.length > 0) {\r\n node.children = children\r\n }\r\n\r\n return node\r\n}\r\n\r\nexport function serializeTree(root: HTMLElement): SerializedNode[] {\r\n const nodes: SerializedNode[] = []\r\n\r\n function walk(el: HTMLElement) {\r\n const node = serializeNode(el)\r\n if (!node) {\r\n return\r\n }\r\n nodes.push(node)\r\n for (const child of el.children) {\r\n walk(child as HTMLElement)\r\n }\r\n }\r\n\r\n walk(root)\r\n return nodes\r\n}\r\n","import type { Viewport } from '../types'\r\n\r\nexport function getViewport(): Viewport {\r\n return {\r\n scrollX: Math.round(window.scrollX),\r\n scrollY: Math.round(window.scrollY),\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n }\r\n}\r\n","import type { Config, RrwebEvent, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { record } from 'rrweb'\r\nimport { MASK_SELECTOR } from '../constants'\r\n\r\nexport class RrwebTracker implements Tracker {\r\n private stopFn: (() => void) | null = null\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n ) {}\r\n\r\n start() {\r\n this.stopFn = record({\r\n emit: (event) => {\r\n const rrwebEvent: RrwebEvent = {\r\n event: 'rrweb',\r\n timestamp: new Date().toISOString(),\r\n rrwebEvent: event,\r\n }\r\n this.sender.add(rrwebEvent)\r\n },\r\n recordCrossOriginIframes: false,\r\n recordCanvas: false,\r\n maskTextSelector: MASK_SELECTOR,\r\n maskInputOptions: { password: true },\r\n maskTextFn: text => '*'.repeat(text.length),\r\n sampling: {\r\n mousemove: false,\r\n mouseInteraction: true,\r\n scroll: 500,\r\n input: 'last',\r\n },\r\n }) ?? null\r\n\r\n if (this.config.debug)\r\n console.log('[dataclient] rrweb recording started')\r\n }\r\n\r\n stop() {\r\n if (this.stopFn) {\r\n this.stopFn()\r\n this.stopFn = null\r\n }\r\n }\r\n\r\n static addMarker(tag: string, payload: unknown) {\r\n record.addCustomEvent(tag, payload)\r\n }\r\n}\r\n","import type { ActionEvent, Config, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { INTERACTIVE_ROLES, INTERACTIVE_TAGS, TEXT_INPUT_TYPES } from '../constants'\r\nimport { isMasked, maskText } from '../dom/mask'\r\nimport { getNodeId } from '../dom/serializer'\r\nimport { getViewport } from '../dom/viewport'\r\nimport { RrwebTracker } from './rrweb'\r\n\r\nexport class ActionTracker implements Tracker {\r\n private inputTimers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>()\r\n private pendingInputs = new Set<HTMLElement>()\r\n\r\n private handleClick = (e: Event) => this.onClick(e)\r\n private handleInput = (e: Event) => this.onInput(e)\r\n private handleChange = (e: Event) => this.onChange(e)\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n ) {}\r\n\r\n start() {\r\n document.addEventListener('click', this.handleClick, true)\r\n document.addEventListener('input', this.handleInput, true)\r\n document.addEventListener('change', this.handleChange, true)\r\n }\r\n\r\n stop() {\r\n document.removeEventListener('click', this.handleClick, true)\r\n document.removeEventListener('input', this.handleInput, true)\r\n document.removeEventListener('change', this.handleChange, true)\r\n }\r\n\r\n beforeUnload() {\r\n for (const el of this.pendingInputs) {\r\n const timer = this.inputTimers.get(el)\r\n if (timer) {\r\n clearTimeout(timer)\r\n }\r\n this.inputTimers.delete(el)\r\n this.recordInput(el)\r\n }\r\n this.pendingInputs.clear()\r\n }\r\n\r\n private onClick(e: Event) {\r\n const raw = e.target as HTMLElement\r\n if (!raw) {\r\n return\r\n }\r\n\r\n const tag = raw.tagName?.toLowerCase()\r\n if (tag === 'body' || tag === 'html') {\r\n return\r\n }\r\n\r\n const el = this.findMeaningfulElement(raw)\r\n const info = this.getElementInfo(el)\r\n\r\n const action: ActionEvent = {\r\n event: 'action',\r\n timestamp: new Date().toISOString(),\r\n type: 'click',\r\n targetId: info.targetId,\r\n tag: info.tag,\r\n text: info.text,\r\n url: location.href,\r\n state: info.state,\r\n viewport: getViewport(),\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] click: ${info.tag} \"${info.text}\"`)\r\n }\r\n\r\n this.sender.add(action)\r\n RrwebTracker.addMarker('click', { tag: info.tag, text: info.text, label: `Click: ${info.text || info.tag}` })\r\n }\r\n\r\n private onInput(e: Event) {\r\n const target = e.target as HTMLElement\r\n if (!target) {\r\n return\r\n }\r\n\r\n const tag = target.tagName?.toLowerCase()\r\n if (tag === 'input') {\r\n const type = (target as HTMLInputElement).type?.toLowerCase() || 'text'\r\n if (!TEXT_INPUT_TYPES.has(type)) {\r\n return\r\n }\r\n }\r\n else if (tag !== 'textarea') {\r\n return\r\n }\r\n\r\n this.pendingInputs.add(target)\r\n\r\n const prev = this.inputTimers.get(target)\r\n if (prev) {\r\n clearTimeout(prev)\r\n }\r\n\r\n this.inputTimers.set(target, setTimeout(() => {\r\n this.inputTimers.delete(target)\r\n this.pendingInputs.delete(target)\r\n this.recordInput(target)\r\n }, this.config.inputDebounce))\r\n }\r\n\r\n private recordInput(target: HTMLElement) {\r\n const rawValue = (target as HTMLInputElement).value || ''\r\n if (!rawValue) {\r\n return\r\n }\r\n\r\n const info = this.getElementInfo(target)\r\n const type = (target as HTMLInputElement).type?.toLowerCase() || ''\r\n const value = (info.masked || type === 'password') ? maskText(rawValue) : rawValue\r\n\r\n const action: ActionEvent = {\r\n event: 'action',\r\n timestamp: new Date().toISOString(),\r\n type: 'input',\r\n targetId: info.targetId,\r\n tag: info.tag,\r\n text: info.text,\r\n url: location.href,\r\n value,\r\n length: rawValue.length,\r\n viewport: getViewport(),\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] input: ${info.tag} \"${info.text}\" → ${value.length} chars`)\r\n }\r\n\r\n this.sender.add(action)\r\n RrwebTracker.addMarker('input', { tag: info.tag, text: info.text, label: `Input: ${info.text || info.tag}` })\r\n }\r\n\r\n private onChange(e: Event) {\r\n const target = e.target as HTMLElement\r\n if (!target) {\r\n return\r\n }\r\n\r\n const info = this.getElementInfo(target)\r\n const tag = target.tagName?.toLowerCase()\r\n\r\n const action: ActionEvent = {\r\n event: 'action',\r\n timestamp: new Date().toISOString(),\r\n type: 'change',\r\n targetId: info.targetId,\r\n tag: info.tag,\r\n text: info.text,\r\n url: location.href,\r\n viewport: getViewport(),\r\n }\r\n\r\n if (tag === 'select') {\r\n const selectValue = (target as HTMLSelectElement).value\r\n action.value = info.masked ? maskText(selectValue) : selectValue\r\n }\r\n else if (tag === 'input') {\r\n const type = (target as HTMLInputElement).type\r\n if (type === 'checkbox' || type === 'radio') {\r\n action.checked = (target as HTMLInputElement).checked\r\n }\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] change: ${info.tag} \"${info.text}\"`)\r\n }\r\n\r\n this.sender.add(action)\r\n RrwebTracker.addMarker('change', { tag: info.tag, text: info.text, label: `Change: ${info.text || info.tag}` })\r\n }\r\n\r\n private getElementInfo(el: HTMLElement) {\r\n const tag = el.tagName?.toLowerCase() || ''\r\n let text = ''\r\n\r\n if (tag === 'input' || tag === 'textarea') {\r\n text = (el as HTMLInputElement).placeholder || el.getAttribute('aria-label') || ''\r\n }\r\n else {\r\n for (const child of el.childNodes) {\r\n if (child.nodeType === Node.TEXT_NODE) {\r\n const t = child.textContent?.trim()\r\n if (t) {\r\n text += (text ? ' ' : '') + t\r\n }\r\n }\r\n }\r\n if (!text) {\r\n text = el.textContent?.trim().slice(0, 100) || ''\r\n }\r\n }\r\n\r\n const masked = isMasked(el)\r\n const finalText = masked ? maskText(text) : text\r\n\r\n return {\r\n tag,\r\n text: finalText.slice(0, 100),\r\n targetId: getNodeId(el),\r\n state: (el as HTMLButtonElement).disabled ? 'disabled' : 'enabled',\r\n masked,\r\n }\r\n }\r\n\r\n private findMeaningfulElement(el: HTMLElement): HTMLElement {\r\n let current: HTMLElement | null = el\r\n while (current && current !== document.body) {\r\n if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {\r\n return current\r\n }\r\n const role = current.getAttribute('role')\r\n if (role && INTERACTIVE_ROLES.has(role)) {\r\n return current\r\n }\r\n current = current.parentElement\r\n }\r\n return el\r\n }\r\n}\r\n","import type { AttrChange, Config, MutationAdd, MutationEvent, TextChange, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { WATCH_ATTRS } from '../constants'\r\nimport { getNodeId, removeNodeId, serializeNode } from '../dom/serializer'\r\n\r\nexport class MutationTracker implements Tracker {\r\n private observer: MutationObserver | null = null\r\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n private pendingAdds: MutationAdd[] = []\r\n private pendingRemoves: number[] = []\r\n private pendingTextChanges: TextChange[] = []\r\n private pendingAttrChanges: AttrChange[] = []\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n private onMutation: () => void,\r\n ) {}\r\n\r\n start() {\r\n this.observer = new MutationObserver(mutations => this.handleMutations(mutations))\r\n this.observer.observe(document.body, {\r\n childList: true,\r\n subtree: true,\r\n characterData: true,\r\n attributes: true,\r\n attributeFilter: WATCH_ATTRS,\r\n })\r\n }\r\n\r\n stop() {\r\n this.observer?.disconnect()\r\n this.observer = null\r\n if (this.debounceTimer) {\r\n clearTimeout(this.debounceTimer)\r\n }\r\n }\r\n\r\n beforeUnload() {\r\n if (this.debounceTimer) {\r\n clearTimeout(this.debounceTimer)\r\n this.flush()\r\n }\r\n }\r\n\r\n private flush() {\r\n this.debounceTimer = null\r\n\r\n if (\r\n this.pendingAdds.length === 0\r\n && this.pendingRemoves.length === 0\r\n && this.pendingTextChanges.length === 0\r\n && this.pendingAttrChanges.length === 0\r\n ) {\r\n return\r\n }\r\n\r\n const mutation: MutationEvent = {\r\n event: 'mutation',\r\n timestamp: new Date().toISOString(),\r\n adds: this.pendingAdds,\r\n removes: this.pendingRemoves,\r\n text_changes: this.pendingTextChanges,\r\n attr_changes: this.pendingAttrChanges,\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] mutation: +${this.pendingAdds.length} -${this.pendingRemoves.length} text:${this.pendingTextChanges.length} attr:${this.pendingAttrChanges.length}`)\r\n }\r\n\r\n this.sender.add(mutation)\r\n this.onMutation()\r\n\r\n this.pendingAdds = []\r\n this.pendingRemoves = []\r\n this.pendingTextChanges = []\r\n this.pendingAttrChanges = []\r\n }\r\n\r\n private scheduleFlush() {\r\n if (this.debounceTimer) {\r\n clearTimeout(this.debounceTimer)\r\n }\r\n this.debounceTimer = setTimeout(() => this.flush(), this.config.mutationDebounce)\r\n }\r\n\r\n private handleMutations(mutations: MutationRecord[]) {\r\n for (const m of mutations) {\r\n for (const node of m.addedNodes) {\r\n if (node.nodeType !== Node.ELEMENT_NODE) {\r\n continue\r\n }\r\n const el = node as HTMLElement\r\n const serialized = serializeNode(el)\r\n if (!serialized) {\r\n continue\r\n }\r\n\r\n const parentId = getNodeId(el.parentElement!)\r\n if (parentId === null) {\r\n continue\r\n }\r\n\r\n this.pendingAdds.push({ parentId, node: serialized })\r\n }\r\n\r\n for (const node of m.removedNodes) {\r\n if (node.nodeType !== Node.ELEMENT_NODE) {\r\n continue\r\n }\r\n const id = getNodeId(node)\r\n if (id !== null) {\r\n this.pendingRemoves.push(id)\r\n removeNodeId(id)\r\n }\r\n }\r\n\r\n if (m.type === 'characterData' && m.target.parentElement) {\r\n const parentId = getNodeId(m.target.parentElement)\r\n if (parentId !== null) {\r\n const text = m.target.textContent?.trim().slice(0, 200) || ''\r\n this.pendingTextChanges.push({ id: parentId, text })\r\n }\r\n }\r\n\r\n if (m.type === 'attributes' && m.attributeName) {\r\n const id = getNodeId(m.target)\r\n if (id !== null) {\r\n const value = (m.target as HTMLElement).getAttribute(m.attributeName)\r\n this.pendingAttrChanges.push({\r\n id,\r\n attr: m.attributeName,\r\n value: value?.slice(0, 200) ?? null,\r\n })\r\n }\r\n }\r\n }\r\n\r\n if (\r\n this.pendingAdds.length > 0\r\n || this.pendingRemoves.length > 0\r\n || this.pendingTextChanges.length > 0\r\n || this.pendingAttrChanges.length > 0\r\n ) {\r\n this.scheduleFlush()\r\n }\r\n }\r\n}\r\n","import type { Config, SnapshotEvent, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { resetIds, serializeTree } from '../dom/serializer'\r\nimport { getViewport } from '../dom/viewport'\r\n\r\nexport class SnapshotTracker implements Tracker {\r\n private lastUrl = ''\r\n private urlPollTimer: ReturnType<typeof setInterval> | null = null\r\n private checkpointTimer: ReturnType<typeof setInterval> | null = null\r\n private hasMutations = false\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n ) {}\r\n\r\n start() {\r\n this.lastUrl = location.href\r\n this.recordSnapshot()\r\n\r\n this.urlPollTimer = setInterval(() => this.pollUrl(), 500)\r\n this.checkpointTimer = setInterval(() => this.checkpoint(), this.config.checkpointInterval)\r\n }\r\n\r\n stop() {\r\n if (this.urlPollTimer) {\r\n clearInterval(this.urlPollTimer)\r\n }\r\n if (this.checkpointTimer) {\r\n clearInterval(this.checkpointTimer)\r\n }\r\n }\r\n\r\n beforeUnload() {\r\n if (this.hasMutations) {\r\n this.recordSnapshot()\r\n }\r\n }\r\n\r\n markMutation() {\r\n this.hasMutations = true\r\n }\r\n\r\n private recordSnapshot() {\r\n resetIds()\r\n\r\n const snapshot: SnapshotEvent = {\r\n event: 'snapshot',\r\n timestamp: new Date().toISOString(),\r\n url: location.href,\r\n title: document.title,\r\n tree: serializeTree(document.body),\r\n viewport: getViewport(),\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] snapshot: ${location.href} (${snapshot.tree.length} nodes)`)\r\n }\r\n\r\n this.sender.add(snapshot)\r\n this.sender.flush()\r\n this.hasMutations = false\r\n }\r\n\r\n private pollUrl() {\r\n if (location.href !== this.lastUrl) {\r\n const prev = this.lastUrl\r\n this.lastUrl = location.href\r\n if (prev) {\r\n if (this.config.debug) {\r\n console.log(`[dataclient] URL changed: ${prev} → ${location.href}`)\r\n }\r\n this.recordSnapshot()\r\n }\r\n }\r\n }\r\n\r\n private checkpoint() {\r\n if (this.hasMutations) {\r\n if (this.config.debug) {\r\n console.log('[dataclient] checkpoint snapshot')\r\n }\r\n this.recordSnapshot()\r\n }\r\n }\r\n}\r\n","export function getDeviceId(key: string): string {\r\n let id = localStorage.getItem(key)\r\n if (!id) {\r\n id = generateId()\r\n localStorage.setItem(key, id)\r\n }\r\n return id\r\n}\r\n\r\nexport function generateId(): string {\r\n const ts = Date.now().toString(36)\r\n const rand = Math.random().toString(36).substring(2, 12)\r\n return `${ts}-${rand}`\r\n}\r\n","import type { SceneBatch, SceneEvent } from '../types'\n\nconst MAX_RETRIES = 2\nconst BEACON_MAX_SIZE = 60000\n\nexport class Sender {\n private queue: SceneEvent[] = []\n private timer: ReturnType<typeof setInterval> | null = null\n private isFlushing = false\n\n constructor(\n private endpoint: string,\n private apiKey: string,\n private batchSize: number,\n private sessionId: string,\n private deviceId: string,\n flushInterval: number,\n ) {\n this.timer = setInterval(() => this.flush(), flushInterval)\n }\n\n add(event: SceneEvent) {\n this.queue.push(event)\n if (event.event === 'rrweb' || this.queue.length >= this.batchSize) {\n this.flush()\n }\n }\n\n async flush() {\n if (this.queue.length === 0 || this.isFlushing) {\n return\n }\n\n this.isFlushing = true\n\n const events = this.queue.splice(0)\n const batch = this.buildBatch(events)\n const json = JSON.stringify(batch)\n const url = this.buildUrl()\n\n const success = await this.send(json, url)\n\n if (!success) {\n this.queue.unshift(...events)\n }\n\n this.isFlushing = false\n }\n\n flushSync() {\n if (this.queue.length === 0) {\n return\n }\n\n const events = this.queue.splice(0)\n if (events.length === 0) return\n\n const url = this.buildUrl()\n\n let chunk: SceneEvent[] = []\n let chunkSize = 0\n\n for (const event of events) {\n const eventJson = JSON.stringify(event)\n if (chunkSize + eventJson.length > BEACON_MAX_SIZE && chunk.length > 0) {\n this.sendBeacon(chunk, url)\n chunk = []\n chunkSize = 0\n }\n chunk.push(event)\n chunkSize += eventJson.length\n }\n\n if (chunk.length > 0) {\n this.sendBeacon(chunk, url)\n }\n }\n\n destroy() {\n if (this.timer) {\n clearInterval(this.timer)\n }\n this.flushSync()\n }\n\n private sendBeacon(events: SceneEvent[], url: string) {\n const batch = this.buildBatch(events)\n const json = JSON.stringify(batch)\n const blob = new Blob([json], { type: 'application/json' })\n navigator.sendBeacon(url, blob)\n }\n\n private buildUrl(): string {\n return `${this.endpoint}?key=${encodeURIComponent(this.apiKey)}`\n }\n\n private buildBatch(events: SceneEvent[]): SceneBatch {\n return {\n session_id: this.sessionId,\n device_id: this.deviceId,\n events,\n sent_at: new Date().toISOString(),\n page_url: location.href,\n user_agent: navigator.userAgent,\n screen: {\n width: screen.width,\n height: screen.height,\n viewport_width: window.innerWidth,\n viewport_height: window.innerHeight,\n },\n }\n }\n\n private async send(json: string, url: string): Promise<boolean> {\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: json,\n keepalive: true,\n })\n if (response.ok) {\n return true\n }\n }\n catch {}\n\n if (attempt < MAX_RETRIES) {\n await new Promise(r => setTimeout(r, (attempt + 1) * 200))\n }\n }\n return false\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,YAAY;AAClB,IAAM,gBAAgB,IAAI,SAAS;AAEnC,IAAM,YAAY,oBAAI,IAAI,CAAC,UAAU,SAAS,YAAY,OAAO,QAAQ,QAAQ,MAAM,CAAC;AAExF,IAAM,eAAe,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,cAAc;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAEO,IAAM,mBAAmB,oBAAI,IAAI,CAAC,QAAQ,SAAS,YAAY,UAAU,OAAO,OAAO,QAAQ,CAAC;AAEhG,IAAM,mBAAmB,oBAAI,IAAI,CAAC,UAAU,KAAK,SAAS,UAAU,UAAU,CAAC;AAC/E,IAAM,oBAAoB,oBAAI,IAAI,CAAC,UAAU,QAAQ,OAAO,YAAY,YAAY,SAAS,UAAU,QAAQ,CAAC;;;AChDhH,SAAS,SAAS,IAAoC;AACzD,MAAI,CAAC;AACD,WAAO;AACX,QAAM,UAAU,GAAG,aAAa,KAAK,eAC/B,KACA,GAAG;AACT,SAAO,CAAC,CAAC,SAAS,QAAQ,aAAa;AAC3C;AAEO,SAAS,SAAS,MAAsB;AAC3C,MAAI,CAAC,MAAM;AACP,WAAO;AAAA,EACX;AACA,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,SAAS,GAAG,CAAC;AACxD,SAAO,KAAK,MAAM,GAAG,OAAO,IAAI,IAAI,OAAO,KAAK,SAAS,OAAO;AACpE;;;ACjBA,IAAM,sBAAsB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAEA,IAAM,yBAAyB;AAC/B,IAAM,uBAAuB;AAE7B,IAAM,oBAAoB;AAEnB,SAAS,eAAe,IAAgC;AAC3D,MAAI,CAAC,IAAI;AACL,WAAO;AAAA,EACX;AAEA,QAAM,SAAS,WAAW,EAAE;AAC5B,MAAI,QAAQ;AACR,WAAO;AAAA,EACX;AAEA,aAAW,SAAS,GAAG,UAAU;AAC7B,UAAM,OAAO,WAAW,KAAoB;AAC5C,QAAI,MAAM;AACN,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AACX;AAEA,SAAS,WAAW,IAAgC;AAChD,MAAI,CAAC,IAAI,SAAS;AACd,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,GAAG,QAAQ,YAAY;AAEnC,MAAI,QAAQ,OAAO;AACf,WAAO,cAAc,EAAE;AAAA,EAC3B;AAEA,MAAI,QAAQ,OAAO,QAAQ,UAAU,GAAG,UAAU,SAAS,SAAS,GAAG;AACnE,WAAO,eAAe,EAAE;AAAA,EAC5B;AAEA,MAAI,QAAQ,OAAO;AACf,WAAO,cAAc,EAAsB;AAAA,EAC/C;AAEA,QAAM,MAAM,GAAG,cAAc,KAAK;AAClC,MAAI,KAAK;AACL,WAAO,cAAc,GAAG;AAAA,EAC5B;AAEA,SAAO;AACX;AAEA,SAAS,cAAc,KAA6B;AAChD,QAAM,SAAS,IAAI,aAAa,aAAa;AAC7C,MAAI,QAAQ;AACR,WAAO;AAAA,EACX;AAEA,QAAM,WAAW,IAAI,aAAa,WAAW;AAC7C,MAAI,UAAU;AACV,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,IAAI,cAAc,KAAK;AACnC,MAAI,KAAK;AACL,UAAM,OAAO,IAAI,aAAa,MAAM,KAAK,IAAI,aAAa,YAAY;AACtE,QAAI,MAAM;AACN,YAAM,QAAQ,KAAK,MAAM,oBAAoB;AAC7C,UAAI,OAAO;AACP,eAAO,MAAM,CAAC;AAAA,MAClB;AAAA,IACJ;AAAA,EACJ;AAEA,QAAM,YAAY,IAAI,aAAa,YAAY;AAC/C,MAAI,WAAW;AACX,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY,IAAI,aAAa,OAAO,KAAK;AAC7F,QAAM,YAAY,yBAAyB,GAAG;AAC9C,MAAI,WAAW;AACX,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AAEA,SAAS,eAAe,IAAgC;AACpD,QAAM,MAAM,OAAO,GAAG,cAAc,WAAW,GAAG,YAAY,GAAG,aAAa,OAAO,KAAK;AAE1F,MAAI,uBAAuB,KAAK,GAAG,GAAG;AAClC,UAAM,OAAO,GAAG,aAAa,KAAK;AAClC,QAAI,MAAM;AACN,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO,yBAAyB,GAAG;AACvC;AAEA,SAAS,cAAc,KAAsC;AACzD,MAAI,IAAI,QAAQ,qBAAqB,IAAI,SAAS,mBAAmB;AACjE,WAAO;AAAA,EACX;AAEA,MAAI,IAAI,eAAe,qBAAqB,IAAI,gBAAgB,mBAAmB;AAC/E,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,MAAI,KAAK;AACL,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,IAAI,aAAa,KAAK;AAClC,MAAI,KAAK;AACL,UAAM,WAAW,IAAI,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC;AAClE,QAAI,UAAU;AACV,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AACX;AAEA,SAAS,yBAAyB,KAA4B;AAC1D,aAAW,WAAW,qBAAqB;AACvC,UAAM,QAAQ,IAAI,MAAM,OAAO;AAC/B,QAAI,OAAO;AACP,aAAO,MAAM,CAAC;AAAA,IAClB;AAAA,EACJ;AACA,SAAO;AACX;;;AC5IA,IAAI,SAAS;AACb,IAAM,WAAW,oBAAI,QAAsB;AAC3C,IAAM,WAAW,oBAAI,IAAkB;AAEhC,SAAS,WAAW;AACvB,WAAS;AACT,WAAS,MAAM;AACnB;AAEO,SAAS,SAAS,MAAoB;AACzC,QAAM,WAAW,SAAS,IAAI,IAAI;AAClC,MAAI,UAAU;AACV,WAAO;AAAA,EACX;AACA,QAAM,KAAK;AACX,WAAS,IAAI,MAAM,EAAE;AACrB,WAAS,IAAI,IAAI,IAAI;AACrB,SAAO;AACX;AAEO,SAAS,UAAU,MAA2B;AACjD,SAAO,SAAS,IAAI,IAAI,KAAK;AACjC;AAMO,SAAS,aAAa,IAAY;AACrC,QAAM,OAAO,SAAS,IAAI,EAAE;AAC5B,MAAI,MAAM;AACN,aAAS,OAAO,IAAI;AACpB,aAAS,OAAO,EAAE;AAAA,EACtB;AACJ;AAEA,SAAS,UAAU,IAA0B;AACzC,MAAI,GAAG;AACH,WAAO;AACX,MAAI,GAAG,aAAa,aAAa,MAAM;AACnC,WAAO;AACX,MAAI,CAAC,GAAG,gBAAgB,GAAG,YAAY,UAAU,iBAAiB,EAAE,EAAE,aAAa,SAAS;AACxF,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAEA,SAAS,cAAc,IAAyB;AAC5C,MAAI,OAAO;AACX,aAAW,SAAS,GAAG,YAAY;AAC/B,QAAI,MAAM,aAAa,KAAK,WAAW;AACnC,YAAM,IAAI,MAAM,aAAa,KAAK;AAClC,UAAI,GAAG;AACH,iBAAS,OAAO,MAAM,MAAM;AAAA,MAChC;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,YAAY,KAAK,MAAM,GAAG,GAAG;AACnC,SAAO,SAAS,EAAE,IAAI,SAAS,SAAS,IAAI;AAChD;AAEA,SAAS,SAAS,IAAqD;AACnE,QAAM,QAAgC,CAAC;AACvC,MAAI,WAAW;AACf,QAAM,SAAS,SAAS,EAAE;AAE1B,aAAW,QAAQ,cAAc;AAC7B,UAAM,QAAQ,GAAG,aAAa,IAAI;AAClC,QAAI,UAAU,QAAQ,UAAU,IAAI;AAChC,UAAI,IAAI,MAAM,MAAM,GAAG,GAAG;AAC1B,UAAI,WAAW,SAAS,WAAW,SAAS,gBAAgB;AACxD,YAAI,SAAS,CAAC;AAAA,MAClB;AACA,YAAM,IAAI,IAAI;AACd,iBAAW;AAAA,IACf;AAAA,EACJ;AAEA,SAAO,WAAW,QAAQ;AAC9B;AAEA,SAAS,QAAQ,IAAyC;AACtD,QAAM,IAAI,GAAG,sBAAsB;AACnC,MAAI,EAAE,UAAU,KAAK,EAAE,WAAW,GAAG;AACjC,WAAO;AAAA,EACX;AACA,SAAO;AAAA,IACH,GAAG,KAAK,MAAM,EAAE,OAAO,OAAO,OAAO;AAAA,IACrC,GAAG,KAAK,MAAM,EAAE,MAAM,OAAO,OAAO;AAAA,IACpC,GAAG,KAAK,MAAM,EAAE,KAAK;AAAA,IACrB,GAAG,KAAK,MAAM,EAAE,MAAM;AAAA,EAC1B;AACJ;AAEO,SAAS,cAAc,IAAwC;AAClE,QAAM,MAAM,GAAG,SAAS,YAAY;AACpC,MAAI,CAAC,OAAO,UAAU,IAAI,GAAG,GAAG;AAC5B,WAAO;AAAA,EACX;AACA,MAAI,CAAC,UAAU,EAAE,GAAG;AAChB,WAAO;AAAA,EACX;AAEA,QAAM,KAAK,SAAS,EAAE;AACtB,QAAM,OAAO,cAAc,EAAE;AAC7B,QAAM,OAAO,eAAe,EAAE;AAC9B,QAAM,QAAQ,SAAS,EAAE;AACzB,QAAM,OAAO,QAAQ,EAAE;AAEvB,QAAM,WAAqB,CAAC;AAC5B,aAAW,SAAS,GAAG,UAAU;AAC7B,UAAM,aAAa,cAAc,KAAoB;AACrD,QAAI,YAAY;AACZ,eAAS,KAAK,WAAW,EAAE;AAAA,IAC/B;AAAA,EACJ;AAEA,QAAM,OAAuB,EAAE,IAAI,IAAI;AACvC,MAAI,MAAM;AACN,SAAK,OAAO;AAAA,EAChB;AACA,MAAI,MAAM;AACN,SAAK,OAAO;AAAA,EAChB;AACA,MAAI,OAAO;AACP,SAAK,QAAQ;AAAA,EACjB;AACA,MAAI,MAAM;AACN,SAAK,OAAO;AAAA,EAChB;AACA,MAAI,SAAS,SAAS,GAAG;AACrB,SAAK,WAAW;AAAA,EACpB;AAEA,SAAO;AACX;AAEO,SAAS,cAAc,MAAqC;AAC/D,QAAM,QAA0B,CAAC;AAEjC,WAAS,KAAK,IAAiB;AAC3B,UAAM,OAAO,cAAc,EAAE;AAC7B,QAAI,CAAC,MAAM;AACP;AAAA,IACJ;AACA,UAAM,KAAK,IAAI;AACf,eAAW,SAAS,GAAG,UAAU;AAC7B,WAAK,KAAoB;AAAA,IAC7B;AAAA,EACJ;AAEA,OAAK,IAAI;AACT,SAAO;AACX;;;AC5JO,SAAS,cAAwB;AACpC,SAAO;AAAA,IACH,SAAS,KAAK,MAAM,OAAO,OAAO;AAAA,IAClC,SAAS,KAAK,MAAM,OAAO,OAAO;AAAA,IAClC,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,EACnB;AACJ;;;ACPA,mBAAuB;AAGhB,IAAM,eAAN,MAAsC;AAAA,EAGzC,YACY,QACA,QACV;AAFU;AACA;AAAA,EACT;AAAA,EAFS;AAAA,EACA;AAAA,EAJJ,SAA8B;AAAA,EAOtC,QAAQ;AACJ,SAAK,aAAS,qBAAO;AAAA,MACjB,MAAM,CAAC,UAAU;AACb,cAAM,aAAyB;AAAA,UAC3B,OAAO;AAAA,UACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,UAClC,YAAY;AAAA,QAChB;AACA,aAAK,OAAO,IAAI,UAAU;AAAA,MAC9B;AAAA,MACA,0BAA0B;AAAA,MAC1B,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,kBAAkB,EAAE,UAAU,KAAK;AAAA,MACnC,YAAY,UAAQ,IAAI,OAAO,KAAK,MAAM;AAAA,MAC1C,UAAU;AAAA,QACN,WAAW;AAAA,QACX,kBAAkB;AAAA,QAClB,QAAQ;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,CAAC,KAAK;AAEN,QAAI,KAAK,OAAO;AACZ,cAAQ,IAAI,sCAAsC;AAAA,EAC1D;AAAA,EAEA,OAAO;AACH,QAAI,KAAK,QAAQ;AACb,WAAK,OAAO;AACZ,WAAK,SAAS;AAAA,IAClB;AAAA,EACJ;AAAA,EAEA,OAAO,UAAU,KAAa,SAAkB;AAC5C,wBAAO,eAAe,KAAK,OAAO;AAAA,EACtC;AACJ;;;AC1CO,IAAM,gBAAN,MAAuC;AAAA,EAQ1C,YACY,QACA,QACV;AAFU;AACA;AAAA,EACT;AAAA,EAFS;AAAA,EACA;AAAA,EATJ,cAAc,oBAAI,QAAoD;AAAA,EACtE,gBAAgB,oBAAI,IAAiB;AAAA,EAErC,cAAc,CAAC,MAAa,KAAK,QAAQ,CAAC;AAAA,EAC1C,cAAc,CAAC,MAAa,KAAK,QAAQ,CAAC;AAAA,EAC1C,eAAe,CAAC,MAAa,KAAK,SAAS,CAAC;AAAA,EAOpD,QAAQ;AACJ,aAAS,iBAAiB,SAAS,KAAK,aAAa,IAAI;AACzD,aAAS,iBAAiB,SAAS,KAAK,aAAa,IAAI;AACzD,aAAS,iBAAiB,UAAU,KAAK,cAAc,IAAI;AAAA,EAC/D;AAAA,EAEA,OAAO;AACH,aAAS,oBAAoB,SAAS,KAAK,aAAa,IAAI;AAC5D,aAAS,oBAAoB,SAAS,KAAK,aAAa,IAAI;AAC5D,aAAS,oBAAoB,UAAU,KAAK,cAAc,IAAI;AAAA,EAClE;AAAA,EAEA,eAAe;AACX,eAAW,MAAM,KAAK,eAAe;AACjC,YAAM,QAAQ,KAAK,YAAY,IAAI,EAAE;AACrC,UAAI,OAAO;AACP,qBAAa,KAAK;AAAA,MACtB;AACA,WAAK,YAAY,OAAO,EAAE;AAC1B,WAAK,YAAY,EAAE;AAAA,IACvB;AACA,SAAK,cAAc,MAAM;AAAA,EAC7B;AAAA,EAEQ,QAAQ,GAAU;AACtB,UAAM,MAAM,EAAE;AACd,QAAI,CAAC,KAAK;AACN;AAAA,IACJ;AAEA,UAAM,MAAM,IAAI,SAAS,YAAY;AACrC,QAAI,QAAQ,UAAU,QAAQ,QAAQ;AAClC;AAAA,IACJ;AAEA,UAAM,KAAK,KAAK,sBAAsB,GAAG;AACzC,UAAM,OAAO,KAAK,eAAe,EAAE;AAEnC,UAAM,SAAsB;AAAA,MACxB,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,MAAM;AAAA,MACN,UAAU,KAAK;AAAA,MACf,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,KAAK,SAAS;AAAA,MACd,OAAO,KAAK;AAAA,MACZ,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,uBAAuB,KAAK,GAAG,KAAK,KAAK,IAAI,GAAG;AAAA,IAChE;AAEA,SAAK,OAAO,IAAI,MAAM;AACtB,iBAAa,UAAU,SAAS,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,UAAU,KAAK,QAAQ,KAAK,GAAG,GAAG,CAAC;AAAA,EAChH;AAAA,EAEQ,QAAQ,GAAU;AACtB,UAAM,SAAS,EAAE;AACjB,QAAI,CAAC,QAAQ;AACT;AAAA,IACJ;AAEA,UAAM,MAAM,OAAO,SAAS,YAAY;AACxC,QAAI,QAAQ,SAAS;AACjB,YAAM,OAAQ,OAA4B,MAAM,YAAY,KAAK;AACjE,UAAI,CAAC,iBAAiB,IAAI,IAAI,GAAG;AAC7B;AAAA,MACJ;AAAA,IACJ,WACS,QAAQ,YAAY;AACzB;AAAA,IACJ;AAEA,SAAK,cAAc,IAAI,MAAM;AAE7B,UAAM,OAAO,KAAK,YAAY,IAAI,MAAM;AACxC,QAAI,MAAM;AACN,mBAAa,IAAI;AAAA,IACrB;AAEA,SAAK,YAAY,IAAI,QAAQ,WAAW,MAAM;AAC1C,WAAK,YAAY,OAAO,MAAM;AAC9B,WAAK,cAAc,OAAO,MAAM;AAChC,WAAK,YAAY,MAAM;AAAA,IAC3B,GAAG,KAAK,OAAO,aAAa,CAAC;AAAA,EACjC;AAAA,EAEQ,YAAY,QAAqB;AACrC,UAAM,WAAY,OAA4B,SAAS;AACvD,QAAI,CAAC,UAAU;AACX;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,eAAe,MAAM;AACvC,UAAM,OAAQ,OAA4B,MAAM,YAAY,KAAK;AACjE,UAAM,QAAS,KAAK,UAAU,SAAS,aAAc,SAAS,QAAQ,IAAI;AAE1E,UAAM,SAAsB;AAAA,MACxB,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,MAAM;AAAA,MACN,UAAU,KAAK;AAAA,MACf,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,KAAK,SAAS;AAAA,MACd;AAAA,MACA,QAAQ,SAAS;AAAA,MACjB,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,uBAAuB,KAAK,GAAG,KAAK,KAAK,IAAI,YAAO,MAAM,MAAM,QAAQ;AAAA,IACxF;AAEA,SAAK,OAAO,IAAI,MAAM;AACtB,iBAAa,UAAU,SAAS,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,UAAU,KAAK,QAAQ,KAAK,GAAG,GAAG,CAAC;AAAA,EAChH;AAAA,EAEQ,SAAS,GAAU;AACvB,UAAM,SAAS,EAAE;AACjB,QAAI,CAAC,QAAQ;AACT;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,eAAe,MAAM;AACvC,UAAM,MAAM,OAAO,SAAS,YAAY;AAExC,UAAM,SAAsB;AAAA,MACxB,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,MAAM;AAAA,MACN,UAAU,KAAK;AAAA,MACf,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,KAAK,SAAS;AAAA,MACd,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,QAAQ,UAAU;AAClB,YAAM,cAAe,OAA6B;AAClD,aAAO,QAAQ,KAAK,SAAS,SAAS,WAAW,IAAI;AAAA,IACzD,WACS,QAAQ,SAAS;AACtB,YAAM,OAAQ,OAA4B;AAC1C,UAAI,SAAS,cAAc,SAAS,SAAS;AACzC,eAAO,UAAW,OAA4B;AAAA,MAClD;AAAA,IACJ;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,wBAAwB,KAAK,GAAG,KAAK,KAAK,IAAI,GAAG;AAAA,IACjE;AAEA,SAAK,OAAO,IAAI,MAAM;AACtB,iBAAa,UAAU,UAAU,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,WAAW,KAAK,QAAQ,KAAK,GAAG,GAAG,CAAC;AAAA,EAClH;AAAA,EAEQ,eAAe,IAAiB;AACpC,UAAM,MAAM,GAAG,SAAS,YAAY,KAAK;AACzC,QAAI,OAAO;AAEX,QAAI,QAAQ,WAAW,QAAQ,YAAY;AACvC,aAAQ,GAAwB,eAAe,GAAG,aAAa,YAAY,KAAK;AAAA,IACpF,OACK;AACD,iBAAW,SAAS,GAAG,YAAY;AAC/B,YAAI,MAAM,aAAa,KAAK,WAAW;AACnC,gBAAM,IAAI,MAAM,aAAa,KAAK;AAClC,cAAI,GAAG;AACH,qBAAS,OAAO,MAAM,MAAM;AAAA,UAChC;AAAA,QACJ;AAAA,MACJ;AACA,UAAI,CAAC,MAAM;AACP,eAAO,GAAG,aAAa,KAAK,EAAE,MAAM,GAAG,GAAG,KAAK;AAAA,MACnD;AAAA,IACJ;AAEA,UAAM,SAAS,SAAS,EAAE;AAC1B,UAAM,YAAY,SAAS,SAAS,IAAI,IAAI;AAE5C,WAAO;AAAA,MACH;AAAA,MACA,MAAM,UAAU,MAAM,GAAG,GAAG;AAAA,MAC5B,UAAU,UAAU,EAAE;AAAA,MACtB,OAAQ,GAAyB,WAAW,aAAa;AAAA,MACzD;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,sBAAsB,IAA8B;AACxD,QAAI,UAA8B;AAClC,WAAO,WAAW,YAAY,SAAS,MAAM;AACzC,UAAI,iBAAiB,IAAI,QAAQ,SAAS,YAAY,CAAC,GAAG;AACtD,eAAO;AAAA,MACX;AACA,YAAM,OAAO,QAAQ,aAAa,MAAM;AACxC,UAAI,QAAQ,kBAAkB,IAAI,IAAI,GAAG;AACrC,eAAO;AAAA,MACX;AACA,gBAAU,QAAQ;AAAA,IACtB;AACA,WAAO;AAAA,EACX;AACJ;;;AC9NO,IAAM,kBAAN,MAAyC;AAAA,EAS5C,YACY,QACA,QACA,YACV;AAHU;AACA;AACA;AAAA,EACT;AAAA,EAHS;AAAA,EACA;AAAA,EACA;AAAA,EAXJ,WAAoC;AAAA,EACpC,gBAAsD;AAAA,EAEtD,cAA6B,CAAC;AAAA,EAC9B,iBAA2B,CAAC;AAAA,EAC5B,qBAAmC,CAAC;AAAA,EACpC,qBAAmC,CAAC;AAAA,EAQ5C,QAAQ;AACJ,SAAK,WAAW,IAAI,iBAAiB,eAAa,KAAK,gBAAgB,SAAS,CAAC;AACjF,SAAK,SAAS,QAAQ,SAAS,MAAM;AAAA,MACjC,WAAW;AAAA,MACX,SAAS;AAAA,MACT,eAAe;AAAA,MACf,YAAY;AAAA,MACZ,iBAAiB;AAAA,IACrB,CAAC;AAAA,EACL;AAAA,EAEA,OAAO;AACH,SAAK,UAAU,WAAW;AAC1B,SAAK,WAAW;AAChB,QAAI,KAAK,eAAe;AACpB,mBAAa,KAAK,aAAa;AAAA,IACnC;AAAA,EACJ;AAAA,EAEA,eAAe;AACX,QAAI,KAAK,eAAe;AACpB,mBAAa,KAAK,aAAa;AAC/B,WAAK,MAAM;AAAA,IACf;AAAA,EACJ;AAAA,EAEQ,QAAQ;AACZ,SAAK,gBAAgB;AAErB,QACI,KAAK,YAAY,WAAW,KACzB,KAAK,eAAe,WAAW,KAC/B,KAAK,mBAAmB,WAAW,KACnC,KAAK,mBAAmB,WAAW,GACxC;AACE;AAAA,IACJ;AAEA,UAAM,WAA0B;AAAA,MAC5B,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,cAAc,KAAK;AAAA,MACnB,cAAc,KAAK;AAAA,IACvB;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,2BAA2B,KAAK,YAAY,MAAM,KAAK,KAAK,eAAe,MAAM,SAAS,KAAK,mBAAmB,MAAM,SAAS,KAAK,mBAAmB,MAAM,EAAE;AAAA,IACjL;AAEA,SAAK,OAAO,IAAI,QAAQ;AACxB,SAAK,WAAW;AAEhB,SAAK,cAAc,CAAC;AACpB,SAAK,iBAAiB,CAAC;AACvB,SAAK,qBAAqB,CAAC;AAC3B,SAAK,qBAAqB,CAAC;AAAA,EAC/B;AAAA,EAEQ,gBAAgB;AACpB,QAAI,KAAK,eAAe;AACpB,mBAAa,KAAK,aAAa;AAAA,IACnC;AACA,SAAK,gBAAgB,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,OAAO,gBAAgB;AAAA,EACpF;AAAA,EAEQ,gBAAgB,WAA6B;AACjD,eAAW,KAAK,WAAW;AACvB,iBAAW,QAAQ,EAAE,YAAY;AAC7B,YAAI,KAAK,aAAa,KAAK,cAAc;AACrC;AAAA,QACJ;AACA,cAAM,KAAK;AACX,cAAM,aAAa,cAAc,EAAE;AACnC,YAAI,CAAC,YAAY;AACb;AAAA,QACJ;AAEA,cAAM,WAAW,UAAU,GAAG,aAAc;AAC5C,YAAI,aAAa,MAAM;AACnB;AAAA,QACJ;AAEA,aAAK,YAAY,KAAK,EAAE,UAAU,MAAM,WAAW,CAAC;AAAA,MACxD;AAEA,iBAAW,QAAQ,EAAE,cAAc;AAC/B,YAAI,KAAK,aAAa,KAAK,cAAc;AACrC;AAAA,QACJ;AACA,cAAM,KAAK,UAAU,IAAI;AACzB,YAAI,OAAO,MAAM;AACb,eAAK,eAAe,KAAK,EAAE;AAC3B,uBAAa,EAAE;AAAA,QACnB;AAAA,MACJ;AAEA,UAAI,EAAE,SAAS,mBAAmB,EAAE,OAAO,eAAe;AACtD,cAAM,WAAW,UAAU,EAAE,OAAO,aAAa;AACjD,YAAI,aAAa,MAAM;AACnB,gBAAM,OAAO,EAAE,OAAO,aAAa,KAAK,EAAE,MAAM,GAAG,GAAG,KAAK;AAC3D,eAAK,mBAAmB,KAAK,EAAE,IAAI,UAAU,KAAK,CAAC;AAAA,QACvD;AAAA,MACJ;AAEA,UAAI,EAAE,SAAS,gBAAgB,EAAE,eAAe;AAC5C,cAAM,KAAK,UAAU,EAAE,MAAM;AAC7B,YAAI,OAAO,MAAM;AACb,gBAAM,QAAS,EAAE,OAAuB,aAAa,EAAE,aAAa;AACpE,eAAK,mBAAmB,KAAK;AAAA,YACzB;AAAA,YACA,MAAM,EAAE;AAAA,YACR,OAAO,OAAO,MAAM,GAAG,GAAG,KAAK;AAAA,UACnC,CAAC;AAAA,QACL;AAAA,MACJ;AAAA,IACJ;AAEA,QACI,KAAK,YAAY,SAAS,KACvB,KAAK,eAAe,SAAS,KAC7B,KAAK,mBAAmB,SAAS,KACjC,KAAK,mBAAmB,SAAS,GACtC;AACE,WAAK,cAAc;AAAA,IACvB;AAAA,EACJ;AACJ;;;AC/IO,IAAM,kBAAN,MAAyC;AAAA,EAM5C,YACY,QACA,QACV;AAFU;AACA;AAAA,EACT;AAAA,EAFS;AAAA,EACA;AAAA,EAPJ,UAAU;AAAA,EACV,eAAsD;AAAA,EACtD,kBAAyD;AAAA,EACzD,eAAe;AAAA,EAOvB,QAAQ;AACJ,SAAK,UAAU,SAAS;AACxB,SAAK,eAAe;AAEpB,SAAK,eAAe,YAAY,MAAM,KAAK,QAAQ,GAAG,GAAG;AACzD,SAAK,kBAAkB,YAAY,MAAM,KAAK,WAAW,GAAG,KAAK,OAAO,kBAAkB;AAAA,EAC9F;AAAA,EAEA,OAAO;AACH,QAAI,KAAK,cAAc;AACnB,oBAAc,KAAK,YAAY;AAAA,IACnC;AACA,QAAI,KAAK,iBAAiB;AACtB,oBAAc,KAAK,eAAe;AAAA,IACtC;AAAA,EACJ;AAAA,EAEA,eAAe;AACX,QAAI,KAAK,cAAc;AACnB,WAAK,eAAe;AAAA,IACxB;AAAA,EACJ;AAAA,EAEA,eAAe;AACX,SAAK,eAAe;AAAA,EACxB;AAAA,EAEQ,iBAAiB;AACrB,aAAS;AAET,UAAM,WAA0B;AAAA,MAC5B,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,KAAK,SAAS;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM,cAAc,SAAS,IAAI;AAAA,MACjC,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,0BAA0B,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,SAAS;AAAA,IACzF;AAEA,SAAK,OAAO,IAAI,QAAQ;AACxB,SAAK,OAAO,MAAM;AAClB,SAAK,eAAe;AAAA,EACxB;AAAA,EAEQ,UAAU;AACd,QAAI,SAAS,SAAS,KAAK,SAAS;AAChC,YAAM,OAAO,KAAK;AAClB,WAAK,UAAU,SAAS;AACxB,UAAI,MAAM;AACN,YAAI,KAAK,OAAO,OAAO;AACnB,kBAAQ,IAAI,6BAA6B,IAAI,WAAM,SAAS,IAAI,EAAE;AAAA,QACtE;AACA,aAAK,eAAe;AAAA,MACxB;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,aAAa;AACjB,QAAI,KAAK,cAAc;AACnB,UAAI,KAAK,OAAO,OAAO;AACnB,gBAAQ,IAAI,kCAAkC;AAAA,MAClD;AACA,WAAK,eAAe;AAAA,IACxB;AAAA,EACJ;AACJ;;;ACrFO,SAAS,YAAY,KAAqB;AAC7C,MAAI,KAAK,aAAa,QAAQ,GAAG;AACjC,MAAI,CAAC,IAAI;AACL,SAAK,WAAW;AAChB,iBAAa,QAAQ,KAAK,EAAE;AAAA,EAChC;AACA,SAAO;AACX;AAEO,SAAS,aAAqB;AACjC,QAAM,KAAK,KAAK,IAAI,EAAE,SAAS,EAAE;AACjC,QAAM,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE;AACvD,SAAO,GAAG,EAAE,IAAI,IAAI;AACxB;;;ACXA,IAAM,cAAc;AACpB,IAAM,kBAAkB;AAEjB,IAAM,SAAN,MAAa;AAAA,EAKhB,YACY,UACA,QACA,WACA,WACA,UACR,eACF;AANU;AACA;AACA;AACA;AACA;AAGR,SAAK,QAAQ,YAAY,MAAM,KAAK,MAAM,GAAG,aAAa;AAAA,EAC9D;AAAA,EARY;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EATJ,QAAsB,CAAC;AAAA,EACvB,QAA+C;AAAA,EAC/C,aAAa;AAAA,EAarB,IAAI,OAAmB;AACnB,SAAK,MAAM,KAAK,KAAK;AACrB,QAAI,MAAM,UAAU,WAAW,KAAK,MAAM,UAAU,KAAK,WAAW;AAChE,WAAK,MAAM;AAAA,IACf;AAAA,EACJ;AAAA,EAEA,MAAM,QAAQ;AACV,QAAI,KAAK,MAAM,WAAW,KAAK,KAAK,YAAY;AAC5C;AAAA,IACJ;AAEA,SAAK,aAAa;AAElB,UAAM,SAAS,KAAK,MAAM,OAAO,CAAC;AAClC,UAAM,QAAQ,KAAK,WAAW,MAAM;AACpC,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,UAAM,MAAM,KAAK,SAAS;AAE1B,UAAM,UAAU,MAAM,KAAK,KAAK,MAAM,GAAG;AAEzC,QAAI,CAAC,SAAS;AACV,WAAK,MAAM,QAAQ,GAAG,MAAM;AAAA,IAChC;AAEA,SAAK,aAAa;AAAA,EACtB;AAAA,EAEA,YAAY;AACR,QAAI,KAAK,MAAM,WAAW,GAAG;AACzB;AAAA,IACJ;AAEA,UAAM,SAAS,KAAK,MAAM,OAAO,CAAC;AAClC,QAAI,OAAO,WAAW,EAAG;AAEzB,UAAM,MAAM,KAAK,SAAS;AAE1B,QAAI,QAAsB,CAAC;AAC3B,QAAI,YAAY;AAEhB,eAAW,SAAS,QAAQ;AACxB,YAAM,YAAY,KAAK,UAAU,KAAK;AACtC,UAAI,YAAY,UAAU,SAAS,mBAAmB,MAAM,SAAS,GAAG;AACpE,aAAK,WAAW,OAAO,GAAG;AAC1B,gBAAQ,CAAC;AACT,oBAAY;AAAA,MAChB;AACA,YAAM,KAAK,KAAK;AAChB,mBAAa,UAAU;AAAA,IAC3B;AAEA,QAAI,MAAM,SAAS,GAAG;AAClB,WAAK,WAAW,OAAO,GAAG;AAAA,IAC9B;AAAA,EACJ;AAAA,EAEA,UAAU;AACN,QAAI,KAAK,OAAO;AACZ,oBAAc,KAAK,KAAK;AAAA,IAC5B;AACA,SAAK,UAAU;AAAA,EACnB;AAAA,EAEQ,WAAW,QAAsB,KAAa;AAClD,UAAM,QAAQ,KAAK,WAAW,MAAM;AACpC,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,UAAM,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC1D,cAAU,WAAW,KAAK,IAAI;AAAA,EAClC;AAAA,EAEQ,WAAmB;AACvB,WAAO,GAAG,KAAK,QAAQ,QAAQ,mBAAmB,KAAK,MAAM,CAAC;AAAA,EAClE;AAAA,EAEQ,WAAW,QAAkC;AACjD,WAAO;AAAA,MACH,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB;AAAA,MACA,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,MAChC,UAAU,SAAS;AAAA,MACnB,YAAY,UAAU;AAAA,MACtB,QAAQ;AAAA,QACJ,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,gBAAgB,OAAO;AAAA,QACvB,iBAAiB,OAAO;AAAA,MAC5B;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAc,KAAK,MAAc,KAA+B;AAC5D,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACrD,UAAI;AACA,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAC9B,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM;AAAA,UACN,WAAW;AAAA,QACf,CAAC;AACD,YAAI,SAAS,IAAI;AACb,iBAAO;AAAA,QACX;AAAA,MACJ,QACM;AAAA,MAAC;AAEP,UAAI,UAAU,aAAa;AACvB,cAAM,IAAI,QAAQ,OAAK,WAAW,IAAI,UAAU,KAAK,GAAG,CAAC;AAAA,MAC7D;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AACJ;;;AX5HA,IAAM,WAAmB;AAAA,EACrB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb,QAAQ;AACZ;AAEO,IAAM,aAAN,MAAiB;AAAA,EACZ;AAAA,EACA,WAAsB,CAAC;AAAA,EACvB;AAAA,EAER,YAAY,SAA2B;AACnC,SAAK,SAAS,EAAE,GAAG,UAAU,GAAG,QAAQ;AAExC,UAAM,YAAY,WAAW;AAC7B,UAAM,WAAW,YAAY,KAAK,OAAO,WAAW;AAEpD,SAAK,SAAS,IAAI;AAAA,MACd,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,MACZ;AAAA,MACA;AAAA,MACA,KAAK,OAAO;AAAA,IAChB;AAEA,UAAM,kBAAkB,IAAI,gBAAgB,KAAK,QAAQ,KAAK,MAAM;AACpE,UAAM,kBAAkB,IAAI,gBAAgB,KAAK,QAAQ,KAAK,QAAQ,MAAM,gBAAgB,aAAa,CAAC;AAC1G,UAAM,gBAAgB,IAAI,cAAc,KAAK,QAAQ,KAAK,MAAM;AAChE,UAAM,eAAe,IAAI,aAAa,KAAK,QAAQ,KAAK,MAAM;AAE9D,SAAK,WAAW,CAAC,iBAAiB,iBAAiB,eAAe,YAAY;AAC9E,SAAK,SAAS,QAAQ,OAAK,EAAE,MAAM,CAAC;AAEpC,UAAM,UAAU,MAAM;AAClB,WAAK,SAAS,QAAQ,OAAK,EAAE,eAAe,CAAC;AAC7C,WAAK,OAAO,UAAU;AAAA,IAC1B;AAEA,aAAS,iBAAiB,oBAAoB,MAAM;AAChD,UAAI,SAAS,oBAAoB,SAAU,SAAQ;AAAA,IACvD,CAAC;AACD,WAAO,iBAAiB,YAAY,OAAO;AAAA,EAC/C;AAAA,EAEA,QAAQ,QAAgB;AACpB,SAAK,OAAO,IAAI,EAAE,OAAO,YAAY,YAAW,oBAAI,KAAK,GAAE,YAAY,GAAG,SAAS,OAAO,CAAC;AAAA,EAC/F;AAAA,EAEA,eAAe,SAAS,IAAI;AACxB,SAAK,OAAO,IAAI,EAAE,OAAO,WAAW,YAAW,oBAAI,KAAK,GAAE,YAAY,GAAG,OAAO,CAAC;AACjF,SAAK,QAAQ;AAAA,EACjB;AAAA,EAEA,UAAU;AACN,SAAK,SAAS,QAAQ,OAAK,EAAE,KAAK,CAAC;AACnC,SAAK,WAAW,CAAC;AACjB,SAAK,OAAO,QAAQ;AAAA,EACxB;AACJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/dom/mask.ts","../src/dom/icon.ts","../src/dom/serializer.ts","../src/dom/viewport.ts","../src/trackers/rrweb.ts","../src/trackers/action.ts","../src/trackers/mutation.ts","../src/trackers/snapshot.ts","../src/utils/identity.ts","../src/utils/sender.ts"],"sourcesContent":["import type { Config, Tracker } from './types'\nimport { ActionTracker } from './trackers/action'\nimport { MutationTracker } from './trackers/mutation'\nimport { RrwebTracker } from './trackers/rrweb'\nimport { SnapshotTracker } from './trackers/snapshot'\nimport { generateId, getDeviceId } from './utils/identity'\nimport { Sender } from './utils/sender'\n\nexport type * from './types'\n\nconst defaults: Config = {\n endpoint: 'https://my.tamsense.com/api/scenes2',\n debug: false,\n batchSize: 5,\n flushInterval: 5000,\n checkpointInterval: 30000,\n mutationDebounce: 200,\n inputDebounce: 1000,\n sessionIdKey: 'sc2_sid',\n deviceIdKey: 'sc2_did',\n apiKey: '',\n}\n\nexport class DataClient {\n private sender: Sender\n private trackers: Tracker[] = []\n private config: Config\n\n constructor(options?: Partial<Config>) {\n this.config = { ...defaults, ...options }\n\n const sessionId = generateId()\n const deviceId = getDeviceId(this.config.deviceIdKey)\n\n this.sender = new Sender(\n this.config.endpoint,\n this.config.apiKey,\n this.config.batchSize,\n sessionId,\n deviceId,\n this.config.flushInterval,\n )\n\n const snapshotTracker = new SnapshotTracker(this.config, this.sender)\n const mutationTracker = new MutationTracker(this.config, this.sender, () => snapshotTracker.markMutation())\n const actionTracker = new ActionTracker(this.config, this.sender)\n const rrwebTracker = new RrwebTracker(this.config, this.sender)\n\n this.trackers = [snapshotTracker, mutationTracker, actionTracker, rrwebTracker]\n this.trackers.forEach(t => t.start())\n\n const onLeave = () => {\n this.trackers.forEach(t => t.beforeUnload?.())\n this.sender.flushSync()\n }\n\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden') onLeave()\n })\n window.addEventListener('pagehide', onLeave)\n }\n\n setUser(userId: string) {\n this.sender.add({ event: 'identify', timestamp: new Date().toISOString(), user_id: userId })\n }\n\n excludeSession(reason = '') {\n this.sender.add({ event: 'exclude', timestamp: new Date().toISOString(), reason })\n this.destroy()\n }\n\n destroy() {\n this.trackers.forEach(t => t.stop())\n this.trackers = []\n this.sender.destroy()\n }\n}\n","export const MASK_ATTR = 'dataclient-mask'\r\nexport const MASK_SELECTOR = `[${MASK_ATTR}]`\r\n\r\nexport const SKIP_TAGS = new Set(['script', 'style', 'noscript', 'svg', 'link', 'meta', 'head'])\r\n\r\nexport const RECORD_ATTRS = new Set([\r\n 'class',\r\n 'role',\r\n 'href',\r\n 'type',\r\n 'placeholder',\r\n 'disabled',\r\n 'hidden',\r\n 'aria-label',\r\n 'aria-selected',\r\n 'aria-expanded',\r\n 'aria-invalid',\r\n 'aria-busy',\r\n 'aria-checked',\r\n 'aria-hidden',\r\n 'value',\r\n 'name',\r\n 'id',\r\n 'for',\r\n 'target',\r\n 'data-state',\r\n MASK_ATTR,\r\n])\r\n\r\nexport const WATCH_ATTRS = [\r\n 'class',\r\n 'role',\r\n 'href',\r\n 'disabled',\r\n 'hidden',\r\n 'placeholder',\r\n 'aria-label',\r\n 'aria-selected',\r\n 'aria-expanded',\r\n 'aria-invalid',\r\n 'aria-busy',\r\n 'aria-checked',\r\n 'aria-hidden',\r\n 'data-state',\r\n 'value',\r\n]\r\n\r\nexport const TEXT_INPUT_TYPES = new Set(['text', 'email', 'password', 'search', 'tel', 'url', 'number'])\r\n\r\nexport const INTERACTIVE_TAGS = new Set(['button', 'a', 'input', 'select', 'textarea'])\r\nexport const INTERACTIVE_ROLES = new Set(['button', 'link', 'tab', 'menuitem', 'checkbox', 'radio', 'switch', 'option'])\r\n","import { MASK_SELECTOR } from '../constants'\r\n\r\nexport function isMasked(el: Element | Node | null): boolean {\r\n if (!el)\r\n return false\r\n const element = el.nodeType === Node.ELEMENT_NODE\r\n ? el as Element\r\n : el.parentElement\r\n return !!element?.closest(MASK_SELECTOR)\r\n}\r\n\r\nexport function maskText(text: string): string {\r\n if (!text) {\r\n return text\r\n }\r\n const visible = Math.max(1, Math.ceil(text.length * 0.2))\r\n return text.slice(0, visible) + '*'.repeat(text.length - visible)\r\n}\r\n\r\nexport function maskValue(el: Element | null, value: string): string {\r\n return isMasked(el) ? maskText(value) : value\r\n}\r\n","const ICON_CLASS_PATTERNS = [\r\n /\\bfa-([a-z0-9-]+)\\b/,\r\n /\\bmdi-([a-z0-9-]+)\\b/,\r\n /\\bbi-([a-z0-9-]+)\\b/,\r\n /\\bicon-([a-z0-9-]+)\\b/,\r\n /\\blucide-([a-z0-9-]+)\\b/,\r\n /\\bri-([a-z0-9-]+)\\b/,\r\n /\\btabler-icon-([a-z0-9-]+)\\b/,\r\n /\\bi-[a-z0-9-]+[:/]([a-z0-9-]+)\\b/,\r\n]\r\n\r\nconst MATERIAL_CLASS_PATTERN = /\\b(?:material-icons|material-symbols-[a-z]+)\\b/\r\nconst SVG_USE_HREF_PATTERN = /#(.+)/\r\n\r\nconst MAX_ICON_IMG_SIZE = 48\r\n\r\nexport function getElementIcon(el: HTMLElement): string | null {\r\n if (!el) {\r\n return null\r\n }\r\n\r\n const direct = detectIcon(el)\r\n if (direct) {\r\n return direct\r\n }\r\n\r\n for (const child of el.children) {\r\n const icon = detectIcon(child as HTMLElement)\r\n if (icon) {\r\n return icon\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction detectIcon(el: HTMLElement): string | null {\r\n if (!el?.tagName) {\r\n return null\r\n }\r\n\r\n const tag = el.tagName.toLowerCase()\r\n\r\n if (tag === 'svg') {\r\n return detectSvgIcon(el)\r\n }\r\n\r\n if (tag === 'i' || tag === 'span' || el.classList.contains('iconify')) {\r\n return detectFontIcon(el)\r\n }\r\n\r\n if (tag === 'img') {\r\n return detectImgIcon(el as HTMLImageElement)\r\n }\r\n\r\n const svg = el.querySelector('svg')\r\n if (svg) {\r\n return detectSvgIcon(svg)\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction detectSvgIcon(svg: Element): string | null {\r\n const lucide = svg.getAttribute('data-lucide')\r\n if (lucide) {\r\n return lucide\r\n }\r\n\r\n const dataIcon = svg.getAttribute('data-icon')\r\n if (dataIcon) {\r\n return dataIcon\r\n }\r\n\r\n const use = svg.querySelector('use')\r\n if (use) {\r\n const href = use.getAttribute('href') || use.getAttribute('xlink:href')\r\n if (href) {\r\n const match = href.match(SVG_USE_HREF_PATTERN)\r\n if (match) {\r\n return match[1]\r\n }\r\n }\r\n }\r\n\r\n const ariaLabel = svg.getAttribute('aria-label')\r\n if (ariaLabel) {\r\n return ariaLabel\r\n }\r\n\r\n const cls = typeof svg.className === 'string' ? svg.className : svg.getAttribute('class') || ''\r\n const fromClass = extractIconNameFromClass(cls)\r\n if (fromClass) {\r\n return fromClass\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction detectFontIcon(el: HTMLElement): string | null {\r\n const cls = typeof el.className === 'string' ? el.className : el.getAttribute('class') || ''\r\n\r\n if (MATERIAL_CLASS_PATTERN.test(cls)) {\r\n const text = el.textContent?.trim()\r\n if (text) {\r\n return text\r\n }\r\n }\r\n\r\n return extractIconNameFromClass(cls)\r\n}\r\n\r\nfunction detectImgIcon(img: HTMLImageElement): string | null {\r\n if (img.width > MAX_ICON_IMG_SIZE || img.height > MAX_ICON_IMG_SIZE) {\r\n return null\r\n }\r\n\r\n if (img.naturalWidth > MAX_ICON_IMG_SIZE || img.naturalHeight > MAX_ICON_IMG_SIZE) {\r\n return null\r\n }\r\n\r\n const alt = img.alt?.trim()\r\n if (alt) {\r\n return alt\r\n }\r\n\r\n const src = img.getAttribute('src')\r\n if (src) {\r\n const filename = src.split('/').pop()?.split('?')[0]?.split('.')[0]\r\n if (filename) {\r\n return filename\r\n }\r\n }\r\n\r\n return null\r\n}\r\n\r\nfunction extractIconNameFromClass(cls: string): string | null {\r\n for (const pattern of ICON_CLASS_PATTERNS) {\r\n const match = cls.match(pattern)\r\n if (match) {\r\n return match[1]\r\n }\r\n }\r\n return null\r\n}\r\n","import type { SerializedNode } from '../types'\r\nimport { RECORD_ATTRS, SKIP_TAGS } from '../constants'\r\nimport { getElementIcon } from './icon'\r\nimport { isMasked, maskText } from './mask'\r\n\r\nlet nextId = 1\r\nconst nodeToId = new WeakMap<Node, number>()\r\nconst idToNode = new Map<number, Node>()\r\n\r\nexport function resetIds() {\r\n nextId = 1\r\n idToNode.clear()\r\n}\r\n\r\nexport function assignId(node: Node): number {\r\n const existing = nodeToId.get(node)\r\n if (existing) {\r\n return existing\r\n }\r\n const id = nextId++\r\n nodeToId.set(node, id)\r\n idToNode.set(id, node)\r\n return id\r\n}\r\n\r\nexport function getNodeId(node: Node): number | null {\r\n return nodeToId.get(node) ?? null\r\n}\r\n\r\nexport function getNodeById(id: number): Node | null {\r\n return idToNode.get(id) ?? null\r\n}\r\n\r\nexport function removeNodeId(id: number) {\r\n const node = idToNode.get(id)\r\n if (node) {\r\n nodeToId.delete(node)\r\n idToNode.delete(id)\r\n }\r\n}\r\n\r\nfunction isVisible(el: HTMLElement): boolean {\r\n if (el.hidden)\r\n return false\r\n if (el.getAttribute('aria-hidden') === 'true')\r\n return false\r\n if (!el.offsetParent && el.tagName !== 'BODY' && getComputedStyle(el).position !== 'fixed') {\r\n return false\r\n }\r\n return true\r\n}\r\n\r\nfunction getDirectText(el: HTMLElement): string {\r\n let text = ''\r\n for (const child of el.childNodes) {\r\n if (child.nodeType === Node.TEXT_NODE) {\r\n const t = child.textContent?.trim()\r\n if (t) {\r\n text += (text ? ' ' : '') + t\r\n }\r\n }\r\n }\r\n const truncated = text.slice(0, 200)\r\n return isMasked(el) ? maskText(truncated) : truncated\r\n}\r\n\r\nfunction getAttrs(el: HTMLElement): Record<string, string> | undefined {\r\n const attrs: Record<string, string> = {}\r\n let hasAttrs = false\r\n const masked = isMasked(el)\r\n\r\n for (const name of RECORD_ATTRS) {\r\n const value = el.getAttribute(name)\r\n if (value !== null && value !== '') {\r\n let v = value.slice(0, 200)\r\n if (masked && (name === 'value' || name === 'placeholder')) {\r\n v = maskText(v)\r\n }\r\n attrs[name] = v\r\n hasAttrs = true\r\n }\r\n }\r\n\r\n return hasAttrs ? attrs : undefined\r\n}\r\n\r\nfunction getRect(el: HTMLElement): SerializedNode['rect'] {\r\n const r = el.getBoundingClientRect()\r\n if (r.width === 0 && r.height === 0) {\r\n return undefined\r\n }\r\n return {\r\n x: Math.round(r.left + window.scrollX),\r\n y: Math.round(r.top + window.scrollY),\r\n w: Math.round(r.width),\r\n h: Math.round(r.height),\r\n }\r\n}\r\n\r\nexport function serializeNode(el: HTMLElement): SerializedNode | null {\r\n const tag = el.tagName?.toLowerCase()\r\n if (!tag || SKIP_TAGS.has(tag)) {\r\n return null\r\n }\r\n if (!isVisible(el)) {\r\n return null\r\n }\r\n\r\n const id = assignId(el)\r\n const text = getDirectText(el)\r\n const icon = getElementIcon(el)\r\n const attrs = getAttrs(el)\r\n const rect = getRect(el)\r\n\r\n const children: number[] = []\r\n for (const child of el.children) {\r\n const serialized = serializeNode(child as HTMLElement)\r\n if (serialized) {\r\n children.push(serialized.id)\r\n }\r\n }\r\n\r\n const node: SerializedNode = { id, tag }\r\n if (text) {\r\n node.text = text\r\n }\r\n if (icon) {\r\n node.icon = icon\r\n }\r\n if (attrs) {\r\n node.attrs = attrs\r\n }\r\n if (rect) {\r\n node.rect = rect\r\n }\r\n if (children.length > 0) {\r\n node.children = children\r\n }\r\n\r\n return node\r\n}\r\n\r\nexport function serializeTree(root: HTMLElement): SerializedNode[] {\r\n const nodes: SerializedNode[] = []\r\n\r\n function walk(el: HTMLElement) {\r\n const node = serializeNode(el)\r\n if (!node) {\r\n return\r\n }\r\n nodes.push(node)\r\n for (const child of el.children) {\r\n walk(child as HTMLElement)\r\n }\r\n }\r\n\r\n walk(root)\r\n return nodes\r\n}\r\n","import type { Viewport } from '../types'\r\n\r\nexport function getViewport(): Viewport {\r\n return {\r\n scrollX: Math.round(window.scrollX),\r\n scrollY: Math.round(window.scrollY),\r\n width: window.innerWidth,\r\n height: window.innerHeight,\r\n }\r\n}\r\n","import type { Config, RrwebEvent, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { record } from 'rrweb'\r\nimport { MASK_SELECTOR } from '../constants'\r\n\r\nexport class RrwebTracker implements Tracker {\r\n private stopFn: (() => void) | null = null\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n ) {}\r\n\r\n start() {\r\n this.stopFn = record({\r\n emit: (event) => {\r\n const rrwebEvent: RrwebEvent = {\r\n event: 'rrweb',\r\n timestamp: new Date().toISOString(),\r\n rrwebEvent: event,\r\n }\r\n this.sender.add(rrwebEvent)\r\n },\r\n recordCrossOriginIframes: false,\r\n recordCanvas: false,\r\n maskTextSelector: MASK_SELECTOR,\r\n maskInputOptions: { password: true },\r\n maskTextFn: text => '*'.repeat(text.length),\r\n sampling: {\r\n mousemove: false,\r\n mouseInteraction: true,\r\n scroll: 500,\r\n input: 'last',\r\n },\r\n }) ?? null\r\n\r\n if (this.config.debug)\r\n console.log('[dataclient] rrweb recording started')\r\n }\r\n\r\n stop() {\r\n if (this.stopFn) {\r\n this.stopFn()\r\n this.stopFn = null\r\n }\r\n }\r\n\r\n static addMarker(tag: string, payload: unknown) {\r\n record.addCustomEvent(tag, payload)\r\n }\r\n}\r\n","import type { ActionEvent, Config, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { INTERACTIVE_ROLES, INTERACTIVE_TAGS, TEXT_INPUT_TYPES } from '../constants'\r\nimport { isMasked, maskText } from '../dom/mask'\r\nimport { getNodeId } from '../dom/serializer'\r\nimport { getViewport } from '../dom/viewport'\r\nimport { RrwebTracker } from './rrweb'\r\n\r\nexport class ActionTracker implements Tracker {\r\n private inputTimers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>()\r\n private pendingInputs = new Set<HTMLElement>()\r\n\r\n private handleClick = (e: Event) => this.onClick(e)\r\n private handleInput = (e: Event) => this.onInput(e)\r\n private handleChange = (e: Event) => this.onChange(e)\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n ) {}\r\n\r\n start() {\r\n document.addEventListener('click', this.handleClick, true)\r\n document.addEventListener('input', this.handleInput, true)\r\n document.addEventListener('change', this.handleChange, true)\r\n }\r\n\r\n stop() {\r\n document.removeEventListener('click', this.handleClick, true)\r\n document.removeEventListener('input', this.handleInput, true)\r\n document.removeEventListener('change', this.handleChange, true)\r\n }\r\n\r\n beforeUnload() {\r\n for (const el of this.pendingInputs) {\r\n const timer = this.inputTimers.get(el)\r\n if (timer) {\r\n clearTimeout(timer)\r\n }\r\n this.inputTimers.delete(el)\r\n this.recordInput(el)\r\n }\r\n this.pendingInputs.clear()\r\n }\r\n\r\n private onClick(e: Event) {\r\n const raw = e.target as HTMLElement\r\n if (!raw) {\r\n return\r\n }\r\n\r\n const tag = raw.tagName?.toLowerCase()\r\n if (tag === 'body' || tag === 'html') {\r\n return\r\n }\r\n\r\n const el = this.findMeaningfulElement(raw)\r\n const info = this.getElementInfo(el)\r\n\r\n const action: ActionEvent = {\r\n event: 'action',\r\n timestamp: new Date().toISOString(),\r\n type: 'click',\r\n targetId: info.targetId,\r\n tag: info.tag,\r\n text: info.text,\r\n url: location.href,\r\n state: info.state,\r\n viewport: getViewport(),\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] click: ${info.tag} \"${info.text}\"`)\r\n }\r\n\r\n this.sender.add(action)\r\n RrwebTracker.addMarker('click', { tag: info.tag, text: info.text, label: `Click: ${info.text || info.tag}` })\r\n }\r\n\r\n private onInput(e: Event) {\r\n const target = e.target as HTMLElement\r\n if (!target) {\r\n return\r\n }\r\n\r\n const tag = target.tagName?.toLowerCase()\r\n if (tag === 'input') {\r\n const type = (target as HTMLInputElement).type?.toLowerCase() || 'text'\r\n if (!TEXT_INPUT_TYPES.has(type)) {\r\n return\r\n }\r\n }\r\n else if (tag !== 'textarea') {\r\n return\r\n }\r\n\r\n this.pendingInputs.add(target)\r\n\r\n const prev = this.inputTimers.get(target)\r\n if (prev) {\r\n clearTimeout(prev)\r\n }\r\n\r\n this.inputTimers.set(target, setTimeout(() => {\r\n this.inputTimers.delete(target)\r\n this.pendingInputs.delete(target)\r\n this.recordInput(target)\r\n }, this.config.inputDebounce))\r\n }\r\n\r\n private recordInput(target: HTMLElement) {\r\n const rawValue = (target as HTMLInputElement).value || ''\r\n if (!rawValue) {\r\n return\r\n }\r\n\r\n const info = this.getElementInfo(target)\r\n const type = (target as HTMLInputElement).type?.toLowerCase() || ''\r\n const value = (info.masked || type === 'password') ? maskText(rawValue) : rawValue\r\n\r\n const action: ActionEvent = {\r\n event: 'action',\r\n timestamp: new Date().toISOString(),\r\n type: 'input',\r\n targetId: info.targetId,\r\n tag: info.tag,\r\n text: info.text,\r\n url: location.href,\r\n value,\r\n length: rawValue.length,\r\n viewport: getViewport(),\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] input: ${info.tag} \"${info.text}\" → ${value.length} chars`)\r\n }\r\n\r\n this.sender.add(action)\r\n RrwebTracker.addMarker('input', { tag: info.tag, text: info.text, label: `Input: ${info.text || info.tag}` })\r\n }\r\n\r\n private onChange(e: Event) {\r\n const target = e.target as HTMLElement\r\n if (!target) {\r\n return\r\n }\r\n\r\n const info = this.getElementInfo(target)\r\n const tag = target.tagName?.toLowerCase()\r\n\r\n const action: ActionEvent = {\r\n event: 'action',\r\n timestamp: new Date().toISOString(),\r\n type: 'change',\r\n targetId: info.targetId,\r\n tag: info.tag,\r\n text: info.text,\r\n url: location.href,\r\n viewport: getViewport(),\r\n }\r\n\r\n if (tag === 'select') {\r\n const selectValue = (target as HTMLSelectElement).value\r\n action.value = info.masked ? maskText(selectValue) : selectValue\r\n }\r\n else if (tag === 'input') {\r\n const type = (target as HTMLInputElement).type\r\n if (type === 'checkbox' || type === 'radio') {\r\n action.checked = (target as HTMLInputElement).checked\r\n }\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] change: ${info.tag} \"${info.text}\"`)\r\n }\r\n\r\n this.sender.add(action)\r\n RrwebTracker.addMarker('change', { tag: info.tag, text: info.text, label: `Change: ${info.text || info.tag}` })\r\n }\r\n\r\n private getElementInfo(el: HTMLElement) {\r\n const tag = el.tagName?.toLowerCase() || ''\r\n let text = ''\r\n\r\n if (tag === 'input' || tag === 'textarea') {\r\n text = (el as HTMLInputElement).placeholder || el.getAttribute('aria-label') || ''\r\n }\r\n else {\r\n for (const child of el.childNodes) {\r\n if (child.nodeType === Node.TEXT_NODE) {\r\n const t = child.textContent?.trim()\r\n if (t) {\r\n text += (text ? ' ' : '') + t\r\n }\r\n }\r\n }\r\n if (!text) {\r\n text = el.textContent?.trim().slice(0, 100) || ''\r\n }\r\n }\r\n\r\n const masked = isMasked(el)\r\n const finalText = masked ? maskText(text) : text\r\n\r\n return {\r\n tag,\r\n text: finalText.slice(0, 100),\r\n targetId: getNodeId(el),\r\n state: (el as HTMLButtonElement).disabled ? 'disabled' : 'enabled',\r\n masked,\r\n }\r\n }\r\n\r\n private findMeaningfulElement(el: HTMLElement): HTMLElement {\r\n let current: HTMLElement | null = el\r\n while (current && current !== document.body) {\r\n if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {\r\n return current\r\n }\r\n const role = current.getAttribute('role')\r\n if (role && INTERACTIVE_ROLES.has(role)) {\r\n return current\r\n }\r\n current = current.parentElement\r\n }\r\n return el\r\n }\r\n}\r\n","import type { AttrChange, Config, MutationAdd, MutationEvent, TextChange, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { WATCH_ATTRS } from '../constants'\r\nimport { getNodeId, removeNodeId, serializeNode } from '../dom/serializer'\r\n\r\nexport class MutationTracker implements Tracker {\r\n private observer: MutationObserver | null = null\r\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\r\n\r\n private pendingAdds: MutationAdd[] = []\r\n private pendingRemoves: number[] = []\r\n private pendingTextChanges: TextChange[] = []\r\n private pendingAttrChanges: AttrChange[] = []\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n private onMutation: () => void,\r\n ) {}\r\n\r\n start() {\r\n this.observer = new MutationObserver(mutations => this.handleMutations(mutations))\r\n this.observer.observe(document.body, {\r\n childList: true,\r\n subtree: true,\r\n characterData: true,\r\n attributes: true,\r\n attributeFilter: WATCH_ATTRS,\r\n })\r\n }\r\n\r\n stop() {\r\n this.observer?.disconnect()\r\n this.observer = null\r\n if (this.debounceTimer) {\r\n clearTimeout(this.debounceTimer)\r\n }\r\n }\r\n\r\n beforeUnload() {\r\n if (this.debounceTimer) {\r\n clearTimeout(this.debounceTimer)\r\n this.flush()\r\n }\r\n }\r\n\r\n private flush() {\r\n this.debounceTimer = null\r\n\r\n if (\r\n this.pendingAdds.length === 0\r\n && this.pendingRemoves.length === 0\r\n && this.pendingTextChanges.length === 0\r\n && this.pendingAttrChanges.length === 0\r\n ) {\r\n return\r\n }\r\n\r\n const mutation: MutationEvent = {\r\n event: 'mutation',\r\n timestamp: new Date().toISOString(),\r\n adds: this.pendingAdds,\r\n removes: this.pendingRemoves,\r\n text_changes: this.pendingTextChanges,\r\n attr_changes: this.pendingAttrChanges,\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] mutation: +${this.pendingAdds.length} -${this.pendingRemoves.length} text:${this.pendingTextChanges.length} attr:${this.pendingAttrChanges.length}`)\r\n }\r\n\r\n this.sender.add(mutation)\r\n this.onMutation()\r\n\r\n this.pendingAdds = []\r\n this.pendingRemoves = []\r\n this.pendingTextChanges = []\r\n this.pendingAttrChanges = []\r\n }\r\n\r\n private scheduleFlush() {\r\n if (this.debounceTimer) {\r\n clearTimeout(this.debounceTimer)\r\n }\r\n this.debounceTimer = setTimeout(() => this.flush(), this.config.mutationDebounce)\r\n }\r\n\r\n private handleMutations(mutations: MutationRecord[]) {\r\n for (const m of mutations) {\r\n for (const node of m.addedNodes) {\r\n if (node.nodeType !== Node.ELEMENT_NODE) {\r\n continue\r\n }\r\n const el = node as HTMLElement\r\n const serialized = serializeNode(el)\r\n if (!serialized) {\r\n continue\r\n }\r\n\r\n const parentId = getNodeId(el.parentElement!)\r\n if (parentId === null) {\r\n continue\r\n }\r\n\r\n this.pendingAdds.push({ parentId, node: serialized })\r\n }\r\n\r\n for (const node of m.removedNodes) {\r\n if (node.nodeType !== Node.ELEMENT_NODE) {\r\n continue\r\n }\r\n const id = getNodeId(node)\r\n if (id !== null) {\r\n this.pendingRemoves.push(id)\r\n removeNodeId(id)\r\n }\r\n }\r\n\r\n if (m.type === 'characterData' && m.target.parentElement) {\r\n const parentId = getNodeId(m.target.parentElement)\r\n if (parentId !== null) {\r\n const text = m.target.textContent?.trim().slice(0, 200) || ''\r\n this.pendingTextChanges.push({ id: parentId, text })\r\n }\r\n }\r\n\r\n if (m.type === 'attributes' && m.attributeName) {\r\n const id = getNodeId(m.target)\r\n if (id !== null) {\r\n const value = (m.target as HTMLElement).getAttribute(m.attributeName)\r\n this.pendingAttrChanges.push({\r\n id,\r\n attr: m.attributeName,\r\n value: value?.slice(0, 200) ?? null,\r\n })\r\n }\r\n }\r\n }\r\n\r\n if (\r\n this.pendingAdds.length > 0\r\n || this.pendingRemoves.length > 0\r\n || this.pendingTextChanges.length > 0\r\n || this.pendingAttrChanges.length > 0\r\n ) {\r\n this.scheduleFlush()\r\n }\r\n }\r\n}\r\n","import type { Config, SnapshotEvent, Tracker } from '../types'\r\nimport type { Sender } from '../utils/sender'\r\nimport { resetIds, serializeTree } from '../dom/serializer'\r\nimport { getViewport } from '../dom/viewport'\r\n\r\nexport class SnapshotTracker implements Tracker {\r\n private lastUrl = ''\r\n private urlPollTimer: ReturnType<typeof setInterval> | null = null\r\n private checkpointTimer: ReturnType<typeof setInterval> | null = null\r\n private hasMutations = false\r\n\r\n constructor(\r\n private config: Config,\r\n private sender: Sender,\r\n ) {}\r\n\r\n start() {\r\n this.lastUrl = location.href\r\n this.recordSnapshot()\r\n\r\n this.urlPollTimer = setInterval(() => this.pollUrl(), 500)\r\n this.checkpointTimer = setInterval(() => this.checkpoint(), this.config.checkpointInterval)\r\n }\r\n\r\n stop() {\r\n if (this.urlPollTimer) {\r\n clearInterval(this.urlPollTimer)\r\n }\r\n if (this.checkpointTimer) {\r\n clearInterval(this.checkpointTimer)\r\n }\r\n }\r\n\r\n beforeUnload() {\r\n if (this.hasMutations) {\r\n this.recordSnapshot()\r\n }\r\n }\r\n\r\n markMutation() {\r\n this.hasMutations = true\r\n }\r\n\r\n private recordSnapshot() {\r\n resetIds()\r\n\r\n const snapshot: SnapshotEvent = {\r\n event: 'snapshot',\r\n timestamp: new Date().toISOString(),\r\n url: location.href,\r\n title: document.title,\r\n tree: serializeTree(document.body),\r\n viewport: getViewport(),\r\n }\r\n\r\n if (this.config.debug) {\r\n console.log(`[dataclient] snapshot: ${location.href} (${snapshot.tree.length} nodes)`)\r\n }\r\n\r\n this.sender.add(snapshot)\r\n this.sender.flush()\r\n this.hasMutations = false\r\n }\r\n\r\n private pollUrl() {\r\n if (location.href !== this.lastUrl) {\r\n const prev = this.lastUrl\r\n this.lastUrl = location.href\r\n if (prev) {\r\n if (this.config.debug) {\r\n console.log(`[dataclient] URL changed: ${prev} → ${location.href}`)\r\n }\r\n this.recordSnapshot()\r\n }\r\n }\r\n }\r\n\r\n private checkpoint() {\r\n if (this.hasMutations) {\r\n if (this.config.debug) {\r\n console.log('[dataclient] checkpoint snapshot')\r\n }\r\n this.recordSnapshot()\r\n }\r\n }\r\n}\r\n","export function getDeviceId(key: string): string {\r\n let id = localStorage.getItem(key)\r\n if (!id) {\r\n id = generateId()\r\n localStorage.setItem(key, id)\r\n }\r\n return id\r\n}\r\n\r\nexport function generateId(): string {\r\n const ts = Date.now().toString(36)\r\n const rand = Math.random().toString(36).substring(2, 12)\r\n return `${ts}-${rand}`\r\n}\r\n","import type { SceneBatch, SceneEvent } from '../types'\n\nconst MAX_RETRIES = 2\nconst BEACON_MAX_SIZE = 60000\n\nexport class Sender {\n private queue: SceneEvent[] = []\n private timer: ReturnType<typeof setInterval> | null = null\n private flushPromise: Promise<void> = Promise.resolve()\n\n constructor(\n private endpoint: string,\n private apiKey: string,\n private batchSize: number,\n private sessionId: string,\n private deviceId: string,\n flushInterval: number,\n ) {\n this.timer = setInterval(() => this.flush(), flushInterval)\n }\n\n add(event: SceneEvent) {\n this.queue.push(event)\n\n // rrweb full snapshot (type 2) is large and critical — flush immediately\n // so it's sent via fetch (no size limit) and not left for sendBeacon\n const isRrwebSnapshot = event.event === 'rrweb'\n && (event as any).rrwebEvent?.type === 2\n\n if (isRrwebSnapshot || this.queue.length >= this.batchSize) {\n this.flush()\n }\n }\n\n flush() {\n // Chain flushes — no events are skipped, each flush waits for the previous\n this.flushPromise = this.flushPromise.then(() => this.doFlush())\n }\n\n flushSync() {\n if (this.queue.length === 0) return\n\n const events = this.queue.splice(0)\n const url = this.buildUrl()\n\n // Split into chunks that fit sendBeacon size limit\n let chunk: SceneEvent[] = []\n let chunkSize = 0\n\n for (const event of events) {\n const eventJson = JSON.stringify(event)\n if (chunkSize + eventJson.length > BEACON_MAX_SIZE && chunk.length > 0) {\n this.sendBeacon(chunk, url)\n chunk = []\n chunkSize = 0\n }\n chunk.push(event)\n chunkSize += eventJson.length\n }\n\n if (chunk.length > 0) {\n this.sendBeacon(chunk, url)\n }\n }\n\n destroy() {\n if (this.timer) {\n clearInterval(this.timer)\n }\n this.flushSync()\n }\n\n private async doFlush() {\n if (this.queue.length === 0) return\n\n const events = this.queue.splice(0)\n const batch = this.buildBatch(events)\n const json = JSON.stringify(batch)\n const url = this.buildUrl()\n\n const success = await this.send(json, url)\n\n if (!success) {\n this.queue.unshift(...events)\n }\n }\n\n private sendBeacon(events: SceneEvent[], url: string) {\n const batch = this.buildBatch(events)\n const json = JSON.stringify(batch)\n const blob = new Blob([json], { type: 'application/json' })\n navigator.sendBeacon(url, blob)\n }\n\n private buildUrl(): string {\n return `${this.endpoint}?key=${encodeURIComponent(this.apiKey)}`\n }\n\n private buildBatch(events: SceneEvent[]): SceneBatch {\n return {\n session_id: this.sessionId,\n device_id: this.deviceId,\n events,\n sent_at: new Date().toISOString(),\n page_url: location.href,\n user_agent: navigator.userAgent,\n screen: {\n width: screen.width,\n height: screen.height,\n viewport_width: window.innerWidth,\n viewport_height: window.innerHeight,\n },\n }\n }\n\n private async send(json: string, url: string): Promise<boolean> {\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: json,\n })\n if (response.ok) return true\n }\n catch {}\n\n if (attempt < MAX_RETRIES) {\n await new Promise(r => setTimeout(r, (attempt + 1) * 200))\n }\n }\n return false\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.global.js
CHANGED
|
@@ -13111,34 +13111,20 @@ var dataclient = (() => {
|
|
|
13111
13111
|
deviceId;
|
|
13112
13112
|
queue = [];
|
|
13113
13113
|
timer = null;
|
|
13114
|
-
|
|
13114
|
+
flushPromise = Promise.resolve();
|
|
13115
13115
|
add(event) {
|
|
13116
13116
|
this.queue.push(event);
|
|
13117
|
-
|
|
13117
|
+
const isRrwebSnapshot = event.event === "rrweb" && event.rrwebEvent?.type === 2;
|
|
13118
|
+
if (isRrwebSnapshot || this.queue.length >= this.batchSize) {
|
|
13118
13119
|
this.flush();
|
|
13119
13120
|
}
|
|
13120
13121
|
}
|
|
13121
|
-
|
|
13122
|
-
|
|
13123
|
-
return;
|
|
13124
|
-
}
|
|
13125
|
-
this.isFlushing = true;
|
|
13126
|
-
const events = this.queue.splice(0);
|
|
13127
|
-
const batch = this.buildBatch(events);
|
|
13128
|
-
const json = JSON.stringify(batch);
|
|
13129
|
-
const url = this.buildUrl();
|
|
13130
|
-
const success = await this.send(json, url);
|
|
13131
|
-
if (!success) {
|
|
13132
|
-
this.queue.unshift(...events);
|
|
13133
|
-
}
|
|
13134
|
-
this.isFlushing = false;
|
|
13122
|
+
flush() {
|
|
13123
|
+
this.flushPromise = this.flushPromise.then(() => this.doFlush());
|
|
13135
13124
|
}
|
|
13136
13125
|
flushSync() {
|
|
13137
|
-
if (this.queue.length === 0)
|
|
13138
|
-
return;
|
|
13139
|
-
}
|
|
13126
|
+
if (this.queue.length === 0) return;
|
|
13140
13127
|
const events = this.queue.splice(0);
|
|
13141
|
-
if (events.length === 0) return;
|
|
13142
13128
|
const url = this.buildUrl();
|
|
13143
13129
|
let chunk = [];
|
|
13144
13130
|
let chunkSize = 0;
|
|
@@ -13162,6 +13148,17 @@ var dataclient = (() => {
|
|
|
13162
13148
|
}
|
|
13163
13149
|
this.flushSync();
|
|
13164
13150
|
}
|
|
13151
|
+
async doFlush() {
|
|
13152
|
+
if (this.queue.length === 0) return;
|
|
13153
|
+
const events = this.queue.splice(0);
|
|
13154
|
+
const batch = this.buildBatch(events);
|
|
13155
|
+
const json = JSON.stringify(batch);
|
|
13156
|
+
const url = this.buildUrl();
|
|
13157
|
+
const success = await this.send(json, url);
|
|
13158
|
+
if (!success) {
|
|
13159
|
+
this.queue.unshift(...events);
|
|
13160
|
+
}
|
|
13161
|
+
}
|
|
13165
13162
|
sendBeacon(events, url) {
|
|
13166
13163
|
const batch = this.buildBatch(events);
|
|
13167
13164
|
const json = JSON.stringify(batch);
|
|
@@ -13193,12 +13190,9 @@ var dataclient = (() => {
|
|
|
13193
13190
|
const response = await fetch(url, {
|
|
13194
13191
|
method: "POST",
|
|
13195
13192
|
headers: { "Content-Type": "application/json" },
|
|
13196
|
-
body: json
|
|
13197
|
-
keepalive: true
|
|
13193
|
+
body: json
|
|
13198
13194
|
});
|
|
13199
|
-
if (response.ok)
|
|
13200
|
-
return true;
|
|
13201
|
-
}
|
|
13195
|
+
if (response.ok) return true;
|
|
13202
13196
|
} catch {
|
|
13203
13197
|
}
|
|
13204
13198
|
if (attempt < MAX_RETRIES) {
|