@tamsensedev/dataclient 0.1.7 → 0.1.9
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 +31 -16
- package/dist/index.cjs +20 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.global.js +22 -27
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +20 -25
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
# TAMsense SDK
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@tamsensedev/dataclient)
|
|
4
|
+
[](https://www.npmjs.com/package/@tamsensedev/dataclient)
|
|
5
|
+
[](https://github.com/tamsensedev/dataclient-js/blob/main/LICENSE)
|
|
6
|
+
|
|
7
|
+
**[Website](https://tamsense.com)** | **[Product Hunt](https://www.producthunt.com/products/tamsense)**
|
|
8
|
+
|
|
3
9
|
Capture frontend user behavior for TAMsense to detect friction across onboarding, activation, evaluation, and conversion to paid journeys.
|
|
4
10
|
|
|
5
|
-
TAMsense helps SaaS companies turn more free users into paying customers by generating specific product recommendations based on the analysis of user behavior across the entire product
|
|
11
|
+
TAMsense helps SaaS companies turn more free users into paying customers by generating specific product recommendations based on the analysis of user behavior across the entire product.
|
|
12
|
+
|
|
13
|
+
### Features
|
|
6
14
|
|
|
7
|
-
**
|
|
15
|
+
- **Session replay** — full video recording via rrweb
|
|
16
|
+
- **DOM snapshots** — serialized page structure for AI analysis
|
|
17
|
+
- **User actions** — clicks, inputs, form changes with element context
|
|
18
|
+
- **Auto idle management** — sessions auto-stop after inactivity, restart on interaction
|
|
19
|
+
- **Privacy first** — mask sensitive data with a single HTML attribute
|
|
20
|
+
- **Lightweight** — small bundle, no impact on page performance
|
|
21
|
+
- **Framework agnostic** — works with React, Vue, Nuxt, Next.js, Angular, or plain JS
|
|
8
22
|
|
|
23
|
+
**Looking to improve conversion to paid?** Support us on [Product Hunt](https://www.producthunt.com/products/tamsense?launch=tamsense)
|
|
9
24
|
|
|
10
25
|
---
|
|
11
26
|
|
|
@@ -63,7 +78,7 @@ npm install @tamsensedev/dataclient
|
|
|
63
78
|
import { DataClient } from '@tamsensedev/dataclient'
|
|
64
79
|
|
|
65
80
|
const client = new DataClient({
|
|
66
|
-
|
|
81
|
+
apiKey: 'YOUR_API_KEY'
|
|
67
82
|
})
|
|
68
83
|
```
|
|
69
84
|
|
|
@@ -131,9 +146,9 @@ Passwords are always masked automatically.
|
|
|
131
146
|
import { DataClient } from '@tamsensedev/dataclient'
|
|
132
147
|
|
|
133
148
|
export default defineNuxtPlugin(() => {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
149
|
+
const client = new DataClient({
|
|
150
|
+
apiKey: 'YOUR_API_KEY'
|
|
151
|
+
})
|
|
137
152
|
})
|
|
138
153
|
```
|
|
139
154
|
|
|
@@ -144,7 +159,7 @@ export default defineNuxtPlugin(() => {
|
|
|
144
159
|
import { DataClient } from '@tamsensedev/dataclient'
|
|
145
160
|
|
|
146
161
|
const client = new DataClient({
|
|
147
|
-
|
|
162
|
+
apiKey: 'YOUR_API_KEY'
|
|
148
163
|
})
|
|
149
164
|
|
|
150
165
|
// After login:
|
|
@@ -158,7 +173,7 @@ client.setUser(user.id)
|
|
|
158
173
|
import { DataClient } from '@tamsensedev/dataclient'
|
|
159
174
|
|
|
160
175
|
const client = new DataClient({
|
|
161
|
-
|
|
176
|
+
apiKey: 'YOUR_API_KEY'
|
|
162
177
|
})
|
|
163
178
|
|
|
164
179
|
app.provide('dataclient', client)
|
|
@@ -208,17 +223,17 @@ import { Component, OnInit } from '@angular/core'
|
|
|
208
223
|
import { DataClient } from '@tamsensedev/dataclient'
|
|
209
224
|
|
|
210
225
|
@Component({
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
selector: 'app-root',
|
|
227
|
+
template: '<router-outlet></router-outlet>'
|
|
213
228
|
})
|
|
214
229
|
export class AppComponent implements OnInit {
|
|
215
|
-
|
|
230
|
+
private client: DataClient | null = null
|
|
216
231
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
232
|
+
ngOnInit() {
|
|
233
|
+
this.client = new DataClient({
|
|
234
|
+
apiKey: 'YOUR_API_KEY'
|
|
235
|
+
})
|
|
236
|
+
}
|
|
222
237
|
}
|
|
223
238
|
```
|
|
224
239
|
|
package/dist/index.cjs
CHANGED
|
@@ -287,7 +287,7 @@ function getRect(el) {
|
|
|
287
287
|
h: Math.round(r.height)
|
|
288
288
|
};
|
|
289
289
|
}
|
|
290
|
-
function
|
|
290
|
+
function collectNode(el, out) {
|
|
291
291
|
const tag = el.tagName?.toLowerCase();
|
|
292
292
|
if (!tag || SKIP_TAGS.has(tag)) {
|
|
293
293
|
return null;
|
|
@@ -300,13 +300,6 @@ function serializeNode(el) {
|
|
|
300
300
|
const icon = getElementIcon(el);
|
|
301
301
|
const attrs = getAttrs(el);
|
|
302
302
|
const rect = getRect(el);
|
|
303
|
-
const children = [];
|
|
304
|
-
for (const child of el.children) {
|
|
305
|
-
const serialized = serializeNode(child);
|
|
306
|
-
if (serialized) {
|
|
307
|
-
children.push(serialized.id);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
303
|
const node = { id, tag };
|
|
311
304
|
if (text) {
|
|
312
305
|
node.text = text;
|
|
@@ -320,26 +313,27 @@ function serializeNode(el) {
|
|
|
320
313
|
if (rect) {
|
|
321
314
|
node.rect = rect;
|
|
322
315
|
}
|
|
323
|
-
|
|
324
|
-
|
|
316
|
+
out.push(node);
|
|
317
|
+
const childrenIds = [];
|
|
318
|
+
for (const child of el.children) {
|
|
319
|
+
const childNode = collectNode(child, out);
|
|
320
|
+
if (childNode) {
|
|
321
|
+
childrenIds.push(childNode.id);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (childrenIds.length > 0) {
|
|
325
|
+
node.children = childrenIds;
|
|
325
326
|
}
|
|
326
327
|
return node;
|
|
327
328
|
}
|
|
328
|
-
function
|
|
329
|
+
function serializeSubtree(el) {
|
|
329
330
|
const nodes = [];
|
|
330
|
-
|
|
331
|
-
const node = serializeNode(el);
|
|
332
|
-
if (!node) {
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
nodes.push(node);
|
|
336
|
-
for (const child of el.children) {
|
|
337
|
-
walk(child);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
walk(root);
|
|
331
|
+
collectNode(el, nodes);
|
|
341
332
|
return nodes;
|
|
342
333
|
}
|
|
334
|
+
function serializeTree(root) {
|
|
335
|
+
return serializeSubtree(root);
|
|
336
|
+
}
|
|
343
337
|
|
|
344
338
|
// src/dom/viewport.ts
|
|
345
339
|
function getViewport() {
|
|
@@ -633,6 +627,7 @@ var MutationTracker = class {
|
|
|
633
627
|
const mutation = {
|
|
634
628
|
event: "mutation",
|
|
635
629
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
630
|
+
url: window.location.href,
|
|
636
631
|
adds: this.pendingAdds,
|
|
637
632
|
removes: this.pendingRemoves,
|
|
638
633
|
text_changes: this.pendingTextChanges,
|
|
@@ -661,15 +656,15 @@ var MutationTracker = class {
|
|
|
661
656
|
continue;
|
|
662
657
|
}
|
|
663
658
|
const el = node;
|
|
664
|
-
const
|
|
665
|
-
if (
|
|
659
|
+
const nodes = serializeSubtree(el);
|
|
660
|
+
if (nodes.length === 0) {
|
|
666
661
|
continue;
|
|
667
662
|
}
|
|
668
663
|
const parentId = getNodeId(el.parentElement);
|
|
669
664
|
if (parentId === null) {
|
|
670
665
|
continue;
|
|
671
666
|
}
|
|
672
|
-
this.pendingAdds.push({ parentId,
|
|
667
|
+
this.pendingAdds.push({ parentId, nodes });
|
|
673
668
|
}
|
|
674
669
|
for (const node of m.removedNodes) {
|
|
675
670
|
if (node.nodeType !== Node.ELEMENT_NODE) {
|
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 idleTimeout: 60 * 60 * 1000,\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 | null = null\n private trackers: Tracker[] = []\n private config: Config\n private deviceId: string\n private idleTimer: ReturnType<typeof setTimeout> | null = null\n private userId: string | null = null\n\n constructor(options?: Partial<Config>) {\n this.config = { ...defaults, ...options }\n this.deviceId = getDeviceId(this.config.deviceIdKey)\n\n this.startSession()\n\n document.addEventListener('click', () => this.onActivity(), true)\n document.addEventListener('input', () => this.onActivity(), true)\n document.addEventListener('change', () => this.onActivity(), true)\n }\n\n setUser(userId: string) {\n this.userId = userId\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.stopSession()\n }\n\n private onActivity() {\n if (!this.sender) {\n this.startSession()\n }\n this.resetIdleTimer()\n }\n\n private resetIdleTimer() {\n if (this.idleTimer)\n clearTimeout(this.idleTimer)\n this.idleTimer = setTimeout(() => this.stopSession(), this.config.idleTimeout)\n }\n\n private startSession() {\n const sessionId = generateId()\n\n this.sender = new Sender(\n this.config.endpoint,\n this.config.apiKey,\n this.config.batchSize,\n sessionId,\n this.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 if (this.userId) {\n this.sender.add({ event: 'identify', timestamp: new Date().toISOString(), user_id: this.userId })\n }\n\n this.resetIdleTimer()\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')\n onLeave()\n })\n window.addEventListener('pagehide', onLeave)\n\n if (this.config.debug) {\n console.log(`[dataclient] Session started: ${sessionId}`)\n }\n }\n\n private stopSession() {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = null\n }\n this.trackers.forEach(t => t.stop())\n this.trackers = []\n if (this.sender) {\n this.sender.destroy()\n this.sender = null\n }\n\n if (this.config.debug) {\n console.log('[dataclient] Session stopped (idle timeout)')\n }\n }\n}\n","export const MASK_ATTR = 'dataclient-mask'\nexport const MASK_SELECTOR = `[${MASK_ATTR}]`\n\nexport const SKIP_TAGS = new Set(['script', 'style', 'noscript', 'svg', 'link', 'meta', 'head'])\n\nexport const RECORD_ATTRS = new Set([\n 'class',\n 'role',\n 'href',\n 'type',\n 'placeholder',\n 'disabled',\n 'hidden',\n 'aria-label',\n 'aria-selected',\n 'aria-expanded',\n 'aria-invalid',\n 'aria-busy',\n 'aria-checked',\n 'aria-hidden',\n 'value',\n 'name',\n 'id',\n 'for',\n 'target',\n 'data-state',\n MASK_ATTR,\n])\n\nexport const WATCH_ATTRS = [\n 'class',\n 'role',\n 'href',\n 'disabled',\n 'hidden',\n 'placeholder',\n 'aria-label',\n 'aria-selected',\n 'aria-expanded',\n 'aria-invalid',\n 'aria-busy',\n 'aria-checked',\n 'aria-hidden',\n 'data-state',\n 'value',\n]\n\nexport const TEXT_INPUT_TYPES = new Set(['text', 'email', 'password', 'search', 'tel', 'url', 'number'])\n\nexport const INTERACTIVE_TAGS = new Set(['button', 'a', 'input', 'select', 'textarea'])\nexport const INTERACTIVE_ROLES = new Set(['button', 'link', 'tab', 'menuitem', 'checkbox', 'radio', 'switch', 'option'])\n","import { MASK_SELECTOR } from '../constants'\n\nexport function isMasked(el: Element | Node | null): boolean {\n if (!el)\n return false\n const element = el.nodeType === Node.ELEMENT_NODE\n ? el as Element\n : el.parentElement\n return !!element?.closest(MASK_SELECTOR)\n}\n\nexport function maskText(text: string): string {\n if (!text) {\n return text\n }\n const visible = Math.max(1, Math.ceil(text.length * 0.2))\n return text.slice(0, visible) + '*'.repeat(text.length - visible)\n}\n\nexport function maskValue(el: Element | null, value: string): string {\n return isMasked(el) ? maskText(value) : value\n}\n","const ICON_CLASS_PATTERNS = [\n /\\bfa-([a-z0-9-]+)\\b/,\n /\\bmdi-([a-z0-9-]+)\\b/,\n /\\bbi-([a-z0-9-]+)\\b/,\n /\\bicon-([a-z0-9-]+)\\b/,\n /\\blucide-([a-z0-9-]+)\\b/,\n /\\bri-([a-z0-9-]+)\\b/,\n /\\btabler-icon-([a-z0-9-]+)\\b/,\n /\\bi-[a-z0-9-]+[:/]([a-z0-9-]+)\\b/,\n]\n\nconst MATERIAL_CLASS_PATTERN = /\\b(?:material-icons|material-symbols-[a-z]+)\\b/\nconst SVG_USE_HREF_PATTERN = /#(.+)/\n\nconst MAX_ICON_IMG_SIZE = 48\n\nexport function getElementIcon(el: HTMLElement): string | null {\n if (!el) {\n return null\n }\n\n const direct = detectIcon(el)\n if (direct) {\n return direct\n }\n\n for (const child of el.children) {\n const icon = detectIcon(child as HTMLElement)\n if (icon) {\n return icon\n }\n }\n\n return null\n}\n\nfunction detectIcon(el: HTMLElement): string | null {\n if (!el?.tagName) {\n return null\n }\n\n const tag = el.tagName.toLowerCase()\n\n if (tag === 'svg') {\n return detectSvgIcon(el)\n }\n\n if (tag === 'i' || tag === 'span' || el.classList.contains('iconify')) {\n return detectFontIcon(el)\n }\n\n if (tag === 'img') {\n return detectImgIcon(el as HTMLImageElement)\n }\n\n const svg = el.querySelector('svg')\n if (svg) {\n return detectSvgIcon(svg)\n }\n\n return null\n}\n\nfunction detectSvgIcon(svg: Element): string | null {\n const lucide = svg.getAttribute('data-lucide')\n if (lucide) {\n return lucide\n }\n\n const dataIcon = svg.getAttribute('data-icon')\n if (dataIcon) {\n return dataIcon\n }\n\n const use = svg.querySelector('use')\n if (use) {\n const href = use.getAttribute('href') || use.getAttribute('xlink:href')\n if (href) {\n const match = href.match(SVG_USE_HREF_PATTERN)\n if (match) {\n return match[1]\n }\n }\n }\n\n const ariaLabel = svg.getAttribute('aria-label')\n if (ariaLabel) {\n return ariaLabel\n }\n\n const cls = typeof svg.className === 'string' ? svg.className : svg.getAttribute('class') || ''\n const fromClass = extractIconNameFromClass(cls)\n if (fromClass) {\n return fromClass\n }\n\n return null\n}\n\nfunction detectFontIcon(el: HTMLElement): string | null {\n const cls = typeof el.className === 'string' ? el.className : el.getAttribute('class') || ''\n\n if (MATERIAL_CLASS_PATTERN.test(cls)) {\n const text = el.textContent?.trim()\n if (text) {\n return text\n }\n }\n\n return extractIconNameFromClass(cls)\n}\n\nfunction detectImgIcon(img: HTMLImageElement): string | null {\n if (img.width > MAX_ICON_IMG_SIZE || img.height > MAX_ICON_IMG_SIZE) {\n return null\n }\n\n if (img.naturalWidth > MAX_ICON_IMG_SIZE || img.naturalHeight > MAX_ICON_IMG_SIZE) {\n return null\n }\n\n const alt = img.alt?.trim()\n if (alt) {\n return alt\n }\n\n const src = img.getAttribute('src')\n if (src) {\n const filename = src.split('/').pop()?.split('?')[0]?.split('.')[0]\n if (filename) {\n return filename\n }\n }\n\n return null\n}\n\nfunction extractIconNameFromClass(cls: string): string | null {\n for (const pattern of ICON_CLASS_PATTERNS) {\n const match = cls.match(pattern)\n if (match) {\n return match[1]\n }\n }\n return null\n}\n","import type { SerializedNode } from '../types'\nimport { RECORD_ATTRS, SKIP_TAGS } from '../constants'\nimport { getElementIcon } from './icon'\nimport { isMasked, maskText } from './mask'\n\nlet nextId = 1\nconst nodeToId = new WeakMap<Node, number>()\nconst idToNode = new Map<number, Node>()\n\nexport function resetIds() {\n nextId = 1\n idToNode.clear()\n}\n\nexport function assignId(node: Node): number {\n const existing = nodeToId.get(node)\n if (existing) {\n return existing\n }\n const id = nextId++\n nodeToId.set(node, id)\n idToNode.set(id, node)\n return id\n}\n\nexport function getNodeId(node: Node): number | null {\n return nodeToId.get(node) ?? null\n}\n\nexport function getNodeById(id: number): Node | null {\n return idToNode.get(id) ?? null\n}\n\nexport function removeNodeId(id: number) {\n const node = idToNode.get(id)\n if (node) {\n nodeToId.delete(node)\n idToNode.delete(id)\n }\n}\n\nfunction isVisible(el: HTMLElement): boolean {\n if (el.hidden)\n return false\n if (el.getAttribute('aria-hidden') === 'true')\n return false\n if (!el.offsetParent && el.tagName !== 'BODY' && getComputedStyle(el).position !== 'fixed') {\n return false\n }\n return true\n}\n\nfunction getDirectText(el: HTMLElement): string {\n let text = ''\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n const t = child.textContent?.trim()\n if (t) {\n text += (text ? ' ' : '') + t\n }\n }\n }\n const truncated = text.slice(0, 200)\n return isMasked(el) ? maskText(truncated) : truncated\n}\n\nfunction getAttrs(el: HTMLElement): Record<string, string> | undefined {\n const attrs: Record<string, string> = {}\n let hasAttrs = false\n const masked = isMasked(el)\n\n for (const name of RECORD_ATTRS) {\n const value = el.getAttribute(name)\n if (value !== null && value !== '') {\n let v = value.slice(0, 200)\n if (masked && (name === 'value' || name === 'placeholder')) {\n v = maskText(v)\n }\n attrs[name] = v\n hasAttrs = true\n }\n }\n\n return hasAttrs ? attrs : undefined\n}\n\nfunction getRect(el: HTMLElement): SerializedNode['rect'] {\n const r = el.getBoundingClientRect()\n if (r.width === 0 && r.height === 0) {\n return undefined\n }\n return {\n x: Math.round(r.left + window.scrollX),\n y: Math.round(r.top + window.scrollY),\n w: Math.round(r.width),\n h: Math.round(r.height),\n }\n}\n\nexport function serializeNode(el: HTMLElement): SerializedNode | null {\n const tag = el.tagName?.toLowerCase()\n if (!tag || SKIP_TAGS.has(tag)) {\n return null\n }\n if (!isVisible(el)) {\n return null\n }\n\n const id = assignId(el)\n const text = getDirectText(el)\n const icon = getElementIcon(el)\n const attrs = getAttrs(el)\n const rect = getRect(el)\n\n const children: number[] = []\n for (const child of el.children) {\n const serialized = serializeNode(child as HTMLElement)\n if (serialized) {\n children.push(serialized.id)\n }\n }\n\n const node: SerializedNode = { id, tag }\n if (text) {\n node.text = text\n }\n if (icon) {\n node.icon = icon\n }\n if (attrs) {\n node.attrs = attrs\n }\n if (rect) {\n node.rect = rect\n }\n if (children.length > 0) {\n node.children = children\n }\n\n return node\n}\n\nexport function serializeTree(root: HTMLElement): SerializedNode[] {\n const nodes: SerializedNode[] = []\n\n function walk(el: HTMLElement) {\n const node = serializeNode(el)\n if (!node) {\n return\n }\n nodes.push(node)\n for (const child of el.children) {\n walk(child as HTMLElement)\n }\n }\n\n walk(root)\n return nodes\n}\n","import type { Viewport } from '../types'\n\nexport function getViewport(): Viewport {\n return {\n scrollX: Math.round(window.scrollX),\n scrollY: Math.round(window.scrollY),\n width: window.innerWidth,\n height: window.innerHeight,\n }\n}\n","import type { Config, RrwebEvent, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { record } from 'rrweb'\nimport { MASK_SELECTOR } from '../constants'\n\nexport class RrwebTracker implements Tracker {\n private stopFn: (() => void) | null = null\n\n constructor(\n private config: Config,\n private sender: Sender,\n ) {}\n\n start() {\n this.stopFn = record({\n emit: (event) => {\n const rrwebEvent: RrwebEvent = {\n event: 'rrweb',\n timestamp: new Date().toISOString(),\n rrwebEvent: event,\n }\n this.sender.add(rrwebEvent)\n },\n recordCrossOriginIframes: false,\n recordCanvas: false,\n maskTextSelector: MASK_SELECTOR,\n maskInputOptions: { password: true },\n maskTextFn: text => '*'.repeat(text.length),\n sampling: {\n mousemove: false,\n mouseInteraction: true,\n scroll: 500,\n input: 'last',\n },\n }) ?? null\n\n if (this.config.debug)\n console.log('[dataclient] rrweb recording started')\n }\n\n stop() {\n if (this.stopFn) {\n this.stopFn()\n this.stopFn = null\n }\n }\n\n static addMarker(tag: string, payload: unknown) {\n record.addCustomEvent(tag, payload)\n }\n}\n","import type { ActionEvent, Config, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { INTERACTIVE_ROLES, INTERACTIVE_TAGS, TEXT_INPUT_TYPES } from '../constants'\nimport { isMasked, maskText } from '../dom/mask'\nimport { getNodeId } from '../dom/serializer'\nimport { getViewport } from '../dom/viewport'\nimport { RrwebTracker } from './rrweb'\n\nexport class ActionTracker implements Tracker {\n private inputTimers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>()\n private pendingInputs = new Set<HTMLElement>()\n\n private handleClick = (e: Event) => this.onClick(e)\n private handleInput = (e: Event) => this.onInput(e)\n private handleChange = (e: Event) => this.onChange(e)\n\n constructor(\n private config: Config,\n private sender: Sender,\n ) {}\n\n start() {\n document.addEventListener('click', this.handleClick, true)\n document.addEventListener('input', this.handleInput, true)\n document.addEventListener('change', this.handleChange, true)\n }\n\n stop() {\n document.removeEventListener('click', this.handleClick, true)\n document.removeEventListener('input', this.handleInput, true)\n document.removeEventListener('change', this.handleChange, true)\n }\n\n beforeUnload() {\n for (const el of this.pendingInputs) {\n const timer = this.inputTimers.get(el)\n if (timer) {\n clearTimeout(timer)\n }\n this.inputTimers.delete(el)\n this.recordInput(el)\n }\n this.pendingInputs.clear()\n }\n\n private onClick(e: Event) {\n const raw = e.target as HTMLElement\n if (!raw) {\n return\n }\n\n const tag = raw.tagName?.toLowerCase()\n if (tag === 'body' || tag === 'html') {\n return\n }\n\n const el = this.findMeaningfulElement(raw)\n const info = this.getElementInfo(el)\n\n const action: ActionEvent = {\n event: 'action',\n timestamp: new Date().toISOString(),\n type: 'click',\n targetId: info.targetId,\n tag: info.tag,\n text: info.text,\n url: location.href,\n state: info.state,\n viewport: getViewport(),\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] click: ${info.tag} \"${info.text}\"`)\n }\n\n this.sender.add(action)\n RrwebTracker.addMarker('click', { tag: info.tag, text: info.text, label: `Click: ${info.text || info.tag}` })\n }\n\n private onInput(e: Event) {\n const target = e.target as HTMLElement\n if (!target) {\n return\n }\n\n const tag = target.tagName?.toLowerCase()\n if (tag === 'input') {\n const type = (target as HTMLInputElement).type?.toLowerCase() || 'text'\n if (!TEXT_INPUT_TYPES.has(type)) {\n return\n }\n }\n else if (tag !== 'textarea') {\n return\n }\n\n this.pendingInputs.add(target)\n\n const prev = this.inputTimers.get(target)\n if (prev) {\n clearTimeout(prev)\n }\n\n this.inputTimers.set(target, setTimeout(() => {\n this.inputTimers.delete(target)\n this.pendingInputs.delete(target)\n this.recordInput(target)\n }, this.config.inputDebounce))\n }\n\n private recordInput(target: HTMLElement) {\n const rawValue = (target as HTMLInputElement).value || ''\n if (!rawValue) {\n return\n }\n\n const info = this.getElementInfo(target)\n const type = (target as HTMLInputElement).type?.toLowerCase() || ''\n const value = (info.masked || type === 'password') ? maskText(rawValue) : rawValue\n\n const action: ActionEvent = {\n event: 'action',\n timestamp: new Date().toISOString(),\n type: 'input',\n targetId: info.targetId,\n tag: info.tag,\n text: info.text,\n url: location.href,\n value,\n length: rawValue.length,\n viewport: getViewport(),\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] input: ${info.tag} \"${info.text}\" → ${value.length} chars`)\n }\n\n this.sender.add(action)\n RrwebTracker.addMarker('input', { tag: info.tag, text: info.text, label: `Input: ${info.text || info.tag}` })\n }\n\n private onChange(e: Event) {\n const target = e.target as HTMLElement\n if (!target) {\n return\n }\n\n const info = this.getElementInfo(target)\n const tag = target.tagName?.toLowerCase()\n\n const action: ActionEvent = {\n event: 'action',\n timestamp: new Date().toISOString(),\n type: 'change',\n targetId: info.targetId,\n tag: info.tag,\n text: info.text,\n url: location.href,\n viewport: getViewport(),\n }\n\n if (tag === 'select') {\n const selectValue = (target as HTMLSelectElement).value\n action.value = info.masked ? maskText(selectValue) : selectValue\n }\n else if (tag === 'input') {\n const type = (target as HTMLInputElement).type\n if (type === 'checkbox' || type === 'radio') {\n action.checked = (target as HTMLInputElement).checked\n }\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] change: ${info.tag} \"${info.text}\"`)\n }\n\n this.sender.add(action)\n RrwebTracker.addMarker('change', { tag: info.tag, text: info.text, label: `Change: ${info.text || info.tag}` })\n }\n\n private getElementInfo(el: HTMLElement) {\n const tag = el.tagName?.toLowerCase() || ''\n let text = ''\n\n if (tag === 'input' || tag === 'textarea') {\n text = (el as HTMLInputElement).placeholder || el.getAttribute('aria-label') || ''\n }\n else {\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n const t = child.textContent?.trim()\n if (t) {\n text += (text ? ' ' : '') + t\n }\n }\n }\n if (!text) {\n text = el.textContent?.trim().slice(0, 100) || ''\n }\n }\n\n const masked = isMasked(el)\n const finalText = masked ? maskText(text) : text\n\n return {\n tag,\n text: finalText.slice(0, 100),\n targetId: getNodeId(el),\n state: (el as HTMLButtonElement).disabled ? 'disabled' : 'enabled',\n masked,\n }\n }\n\n private findMeaningfulElement(el: HTMLElement): HTMLElement {\n let current: HTMLElement | null = el\n while (current && current !== document.body) {\n if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {\n return current\n }\n const role = current.getAttribute('role')\n if (role && INTERACTIVE_ROLES.has(role)) {\n return current\n }\n current = current.parentElement\n }\n return el\n }\n}\n","import type { AttrChange, Config, MutationAdd, MutationEvent, TextChange, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { WATCH_ATTRS } from '../constants'\nimport { getNodeId, removeNodeId, serializeNode } from '../dom/serializer'\n\nexport class MutationTracker implements Tracker {\n private observer: MutationObserver | null = null\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n private pendingAdds: MutationAdd[] = []\n private pendingRemoves: number[] = []\n private pendingTextChanges: TextChange[] = []\n private pendingAttrChanges: AttrChange[] = []\n\n constructor(\n private config: Config,\n private sender: Sender,\n private onMutation: () => void,\n ) {}\n\n start() {\n this.observer = new MutationObserver(mutations => this.handleMutations(mutations))\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: WATCH_ATTRS,\n })\n }\n\n stop() {\n this.observer?.disconnect()\n this.observer = null\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n }\n }\n\n beforeUnload() {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n this.flush()\n }\n }\n\n private flush() {\n this.debounceTimer = null\n\n if (\n this.pendingAdds.length === 0\n && this.pendingRemoves.length === 0\n && this.pendingTextChanges.length === 0\n && this.pendingAttrChanges.length === 0\n ) {\n return\n }\n\n const mutation: MutationEvent = {\n event: 'mutation',\n timestamp: new Date().toISOString(),\n adds: this.pendingAdds,\n removes: this.pendingRemoves,\n text_changes: this.pendingTextChanges,\n attr_changes: this.pendingAttrChanges,\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] mutation: +${this.pendingAdds.length} -${this.pendingRemoves.length} text:${this.pendingTextChanges.length} attr:${this.pendingAttrChanges.length}`)\n }\n\n this.sender.add(mutation)\n this.onMutation()\n\n this.pendingAdds = []\n this.pendingRemoves = []\n this.pendingTextChanges = []\n this.pendingAttrChanges = []\n }\n\n private scheduleFlush() {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n }\n this.debounceTimer = setTimeout(() => this.flush(), this.config.mutationDebounce)\n }\n\n private handleMutations(mutations: MutationRecord[]) {\n for (const m of mutations) {\n for (const node of m.addedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE) {\n continue\n }\n const el = node as HTMLElement\n const serialized = serializeNode(el)\n if (!serialized) {\n continue\n }\n\n const parentId = getNodeId(el.parentElement!)\n if (parentId === null) {\n continue\n }\n\n this.pendingAdds.push({ parentId, node: serialized })\n }\n\n for (const node of m.removedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE) {\n continue\n }\n const id = getNodeId(node)\n if (id !== null) {\n this.pendingRemoves.push(id)\n removeNodeId(id)\n }\n }\n\n if (m.type === 'characterData' && m.target.parentElement) {\n const parentId = getNodeId(m.target.parentElement)\n if (parentId !== null) {\n const text = m.target.textContent?.trim().slice(0, 200) || ''\n this.pendingTextChanges.push({ id: parentId, text })\n }\n }\n\n if (m.type === 'attributes' && m.attributeName) {\n const id = getNodeId(m.target)\n if (id !== null) {\n const value = (m.target as HTMLElement).getAttribute(m.attributeName)\n this.pendingAttrChanges.push({\n id,\n attr: m.attributeName,\n value: value?.slice(0, 200) ?? null,\n })\n }\n }\n }\n\n if (\n this.pendingAdds.length > 0\n || this.pendingRemoves.length > 0\n || this.pendingTextChanges.length > 0\n || this.pendingAttrChanges.length > 0\n ) {\n this.scheduleFlush()\n }\n }\n}\n","import type { Config, SnapshotEvent, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { resetIds, serializeTree } from '../dom/serializer'\nimport { getViewport } from '../dom/viewport'\n\nexport class SnapshotTracker implements Tracker {\n private lastUrl = ''\n private urlPollTimer: ReturnType<typeof setInterval> | null = null\n private checkpointTimer: ReturnType<typeof setInterval> | null = null\n private hasMutations = false\n\n constructor(\n private config: Config,\n private sender: Sender,\n ) {}\n\n start() {\n this.lastUrl = location.href\n this.recordSnapshot()\n\n this.urlPollTimer = setInterval(() => this.pollUrl(), 500)\n this.checkpointTimer = setInterval(() => this.checkpoint(), this.config.checkpointInterval)\n }\n\n stop() {\n if (this.urlPollTimer) {\n clearInterval(this.urlPollTimer)\n }\n if (this.checkpointTimer) {\n clearInterval(this.checkpointTimer)\n }\n }\n\n beforeUnload() {\n if (this.hasMutations) {\n this.recordSnapshot()\n }\n }\n\n markMutation() {\n this.hasMutations = true\n }\n\n private recordSnapshot() {\n resetIds()\n\n const snapshot: SnapshotEvent = {\n event: 'snapshot',\n timestamp: new Date().toISOString(),\n url: location.href,\n title: document.title,\n tree: serializeTree(document.body),\n viewport: getViewport(),\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] snapshot: ${location.href} (${snapshot.tree.length} nodes)`)\n }\n\n this.sender.add(snapshot)\n this.sender.flush()\n this.hasMutations = false\n }\n\n private pollUrl() {\n if (location.href !== this.lastUrl) {\n const prev = this.lastUrl\n this.lastUrl = location.href\n if (prev) {\n if (this.config.debug) {\n console.log(`[dataclient] URL changed: ${prev} → ${location.href}`)\n }\n this.recordSnapshot()\n }\n }\n }\n\n private checkpoint() {\n if (this.hasMutations) {\n if (this.config.debug) {\n console.log('[dataclient] checkpoint snapshot')\n }\n this.recordSnapshot()\n }\n }\n}\n","export function getDeviceId(key: string): string {\n let id = localStorage.getItem(key)\n if (!id) {\n id = generateId()\n localStorage.setItem(key, id)\n }\n return id\n}\n\nexport function generateId(): string {\n const ts = Date.now().toString(36)\n const rand = Math.random().toString(36).substring(2, 12)\n return `${ts}-${rand}`\n}\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 const isRrwebSnapshot = event.event === 'rrweb'\n && event.rrwebEvent.type === 2\n\n if (isRrwebSnapshot || this.queue.length >= this.batchSize) {\n this.flush()\n }\n }\n\n flush() {\n this.flushPromise = this.flushPromise.then(() => this.doFlush())\n }\n\n flushSync() {\n if (this.queue.length === 0)\n return\n\n const events = this.queue.splice(0)\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 async doFlush() {\n if (this.queue.length === 0)\n 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)\n 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":[]}
|
|
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 idleTimeout: 60 * 60 * 1000,\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 | null = null\n private trackers: Tracker[] = []\n private config: Config\n private deviceId: string\n private idleTimer: ReturnType<typeof setTimeout> | null = null\n private userId: string | null = null\n\n constructor(options?: Partial<Config>) {\n this.config = { ...defaults, ...options }\n this.deviceId = getDeviceId(this.config.deviceIdKey)\n\n this.startSession()\n\n document.addEventListener('click', () => this.onActivity(), true)\n document.addEventListener('input', () => this.onActivity(), true)\n document.addEventListener('change', () => this.onActivity(), true)\n }\n\n setUser(userId: string) {\n this.userId = userId\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.stopSession()\n }\n\n private onActivity() {\n if (!this.sender) {\n this.startSession()\n }\n this.resetIdleTimer()\n }\n\n private resetIdleTimer() {\n if (this.idleTimer)\n clearTimeout(this.idleTimer)\n this.idleTimer = setTimeout(() => this.stopSession(), this.config.idleTimeout)\n }\n\n private startSession() {\n const sessionId = generateId()\n\n this.sender = new Sender(\n this.config.endpoint,\n this.config.apiKey,\n this.config.batchSize,\n sessionId,\n this.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 if (this.userId) {\n this.sender.add({ event: 'identify', timestamp: new Date().toISOString(), user_id: this.userId })\n }\n\n this.resetIdleTimer()\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')\n onLeave()\n })\n window.addEventListener('pagehide', onLeave)\n\n if (this.config.debug) {\n console.log(`[dataclient] Session started: ${sessionId}`)\n }\n }\n\n private stopSession() {\n if (this.idleTimer) {\n clearTimeout(this.idleTimer)\n this.idleTimer = null\n }\n this.trackers.forEach(t => t.stop())\n this.trackers = []\n if (this.sender) {\n this.sender.destroy()\n this.sender = null\n }\n\n if (this.config.debug) {\n console.log('[dataclient] Session stopped (idle timeout)')\n }\n }\n}\n","export const MASK_ATTR = 'dataclient-mask'\nexport const MASK_SELECTOR = `[${MASK_ATTR}]`\n\nexport const SKIP_TAGS = new Set(['script', 'style', 'noscript', 'svg', 'link', 'meta', 'head'])\n\nexport const RECORD_ATTRS = new Set([\n 'class',\n 'role',\n 'href',\n 'type',\n 'placeholder',\n 'disabled',\n 'hidden',\n 'aria-label',\n 'aria-selected',\n 'aria-expanded',\n 'aria-invalid',\n 'aria-busy',\n 'aria-checked',\n 'aria-hidden',\n 'value',\n 'name',\n 'id',\n 'for',\n 'target',\n 'data-state',\n MASK_ATTR,\n])\n\nexport const WATCH_ATTRS = [\n 'class',\n 'role',\n 'href',\n 'disabled',\n 'hidden',\n 'placeholder',\n 'aria-label',\n 'aria-selected',\n 'aria-expanded',\n 'aria-invalid',\n 'aria-busy',\n 'aria-checked',\n 'aria-hidden',\n 'data-state',\n 'value',\n]\n\nexport const TEXT_INPUT_TYPES = new Set(['text', 'email', 'password', 'search', 'tel', 'url', 'number'])\n\nexport const INTERACTIVE_TAGS = new Set(['button', 'a', 'input', 'select', 'textarea'])\nexport const INTERACTIVE_ROLES = new Set(['button', 'link', 'tab', 'menuitem', 'checkbox', 'radio', 'switch', 'option'])\n","import { MASK_SELECTOR } from '../constants'\n\nexport function isMasked(el: Element | Node | null): boolean {\n if (!el)\n return false\n const element = el.nodeType === Node.ELEMENT_NODE\n ? el as Element\n : el.parentElement\n return !!element?.closest(MASK_SELECTOR)\n}\n\nexport function maskText(text: string): string {\n if (!text) {\n return text\n }\n const visible = Math.max(1, Math.ceil(text.length * 0.2))\n return text.slice(0, visible) + '*'.repeat(text.length - visible)\n}\n\nexport function maskValue(el: Element | null, value: string): string {\n return isMasked(el) ? maskText(value) : value\n}\n","const ICON_CLASS_PATTERNS = [\n /\\bfa-([a-z0-9-]+)\\b/,\n /\\bmdi-([a-z0-9-]+)\\b/,\n /\\bbi-([a-z0-9-]+)\\b/,\n /\\bicon-([a-z0-9-]+)\\b/,\n /\\blucide-([a-z0-9-]+)\\b/,\n /\\bri-([a-z0-9-]+)\\b/,\n /\\btabler-icon-([a-z0-9-]+)\\b/,\n /\\bi-[a-z0-9-]+[:/]([a-z0-9-]+)\\b/,\n]\n\nconst MATERIAL_CLASS_PATTERN = /\\b(?:material-icons|material-symbols-[a-z]+)\\b/\nconst SVG_USE_HREF_PATTERN = /#(.+)/\n\nconst MAX_ICON_IMG_SIZE = 48\n\nexport function getElementIcon(el: HTMLElement): string | null {\n if (!el) {\n return null\n }\n\n const direct = detectIcon(el)\n if (direct) {\n return direct\n }\n\n for (const child of el.children) {\n const icon = detectIcon(child as HTMLElement)\n if (icon) {\n return icon\n }\n }\n\n return null\n}\n\nfunction detectIcon(el: HTMLElement): string | null {\n if (!el?.tagName) {\n return null\n }\n\n const tag = el.tagName.toLowerCase()\n\n if (tag === 'svg') {\n return detectSvgIcon(el)\n }\n\n if (tag === 'i' || tag === 'span' || el.classList.contains('iconify')) {\n return detectFontIcon(el)\n }\n\n if (tag === 'img') {\n return detectImgIcon(el as HTMLImageElement)\n }\n\n const svg = el.querySelector('svg')\n if (svg) {\n return detectSvgIcon(svg)\n }\n\n return null\n}\n\nfunction detectSvgIcon(svg: Element): string | null {\n const lucide = svg.getAttribute('data-lucide')\n if (lucide) {\n return lucide\n }\n\n const dataIcon = svg.getAttribute('data-icon')\n if (dataIcon) {\n return dataIcon\n }\n\n const use = svg.querySelector('use')\n if (use) {\n const href = use.getAttribute('href') || use.getAttribute('xlink:href')\n if (href) {\n const match = href.match(SVG_USE_HREF_PATTERN)\n if (match) {\n return match[1]\n }\n }\n }\n\n const ariaLabel = svg.getAttribute('aria-label')\n if (ariaLabel) {\n return ariaLabel\n }\n\n const cls = typeof svg.className === 'string' ? svg.className : svg.getAttribute('class') || ''\n const fromClass = extractIconNameFromClass(cls)\n if (fromClass) {\n return fromClass\n }\n\n return null\n}\n\nfunction detectFontIcon(el: HTMLElement): string | null {\n const cls = typeof el.className === 'string' ? el.className : el.getAttribute('class') || ''\n\n if (MATERIAL_CLASS_PATTERN.test(cls)) {\n const text = el.textContent?.trim()\n if (text) {\n return text\n }\n }\n\n return extractIconNameFromClass(cls)\n}\n\nfunction detectImgIcon(img: HTMLImageElement): string | null {\n if (img.width > MAX_ICON_IMG_SIZE || img.height > MAX_ICON_IMG_SIZE) {\n return null\n }\n\n if (img.naturalWidth > MAX_ICON_IMG_SIZE || img.naturalHeight > MAX_ICON_IMG_SIZE) {\n return null\n }\n\n const alt = img.alt?.trim()\n if (alt) {\n return alt\n }\n\n const src = img.getAttribute('src')\n if (src) {\n const filename = src.split('/').pop()?.split('?')[0]?.split('.')[0]\n if (filename) {\n return filename\n }\n }\n\n return null\n}\n\nfunction extractIconNameFromClass(cls: string): string | null {\n for (const pattern of ICON_CLASS_PATTERNS) {\n const match = cls.match(pattern)\n if (match) {\n return match[1]\n }\n }\n return null\n}\n","import type { SerializedNode } from '../types'\nimport { RECORD_ATTRS, SKIP_TAGS } from '../constants'\nimport { getElementIcon } from './icon'\nimport { isMasked, maskText } from './mask'\n\nlet nextId = 1\nconst nodeToId = new WeakMap<Node, number>()\nconst idToNode = new Map<number, Node>()\n\nexport function resetIds() {\n nextId = 1\n idToNode.clear()\n}\n\nexport function assignId(node: Node): number {\n const existing = nodeToId.get(node)\n if (existing) {\n return existing\n }\n const id = nextId++\n nodeToId.set(node, id)\n idToNode.set(id, node)\n return id\n}\n\nexport function getNodeId(node: Node): number | null {\n return nodeToId.get(node) ?? null\n}\n\nexport function getNodeById(id: number): Node | null {\n return idToNode.get(id) ?? null\n}\n\nexport function removeNodeId(id: number) {\n const node = idToNode.get(id)\n if (node) {\n nodeToId.delete(node)\n idToNode.delete(id)\n }\n}\n\nfunction isVisible(el: HTMLElement): boolean {\n if (el.hidden)\n return false\n if (el.getAttribute('aria-hidden') === 'true')\n return false\n if (!el.offsetParent && el.tagName !== 'BODY' && getComputedStyle(el).position !== 'fixed') {\n return false\n }\n return true\n}\n\nfunction getDirectText(el: HTMLElement): string {\n let text = ''\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n const t = child.textContent?.trim()\n if (t) {\n text += (text ? ' ' : '') + t\n }\n }\n }\n const truncated = text.slice(0, 200)\n return isMasked(el) ? maskText(truncated) : truncated\n}\n\nfunction getAttrs(el: HTMLElement): Record<string, string> | undefined {\n const attrs: Record<string, string> = {}\n let hasAttrs = false\n const masked = isMasked(el)\n\n for (const name of RECORD_ATTRS) {\n const value = el.getAttribute(name)\n if (value !== null && value !== '') {\n let v = value.slice(0, 200)\n if (masked && (name === 'value' || name === 'placeholder')) {\n v = maskText(v)\n }\n attrs[name] = v\n hasAttrs = true\n }\n }\n\n return hasAttrs ? attrs : undefined\n}\n\nfunction getRect(el: HTMLElement): SerializedNode['rect'] {\n const r = el.getBoundingClientRect()\n if (r.width === 0 && r.height === 0) {\n return undefined\n }\n return {\n x: Math.round(r.left + window.scrollX),\n y: Math.round(r.top + window.scrollY),\n w: Math.round(r.width),\n h: Math.round(r.height),\n }\n}\n\nfunction collectNode(el: HTMLElement, out: SerializedNode[]): SerializedNode | null {\n const tag = el.tagName?.toLowerCase()\n if (!tag || SKIP_TAGS.has(tag)) {\n return null\n }\n if (!isVisible(el)) {\n return null\n }\n\n const id = assignId(el)\n const text = getDirectText(el)\n const icon = getElementIcon(el)\n const attrs = getAttrs(el)\n const rect = getRect(el)\n\n const node: SerializedNode = { id, tag }\n if (text) {\n node.text = text\n }\n if (icon) {\n node.icon = icon\n }\n if (attrs) {\n node.attrs = attrs\n }\n if (rect) {\n node.rect = rect\n }\n\n out.push(node)\n\n const childrenIds: number[] = []\n for (const child of el.children) {\n const childNode = collectNode(child as HTMLElement, out)\n if (childNode) {\n childrenIds.push(childNode.id)\n }\n }\n if (childrenIds.length > 0) {\n node.children = childrenIds\n }\n\n return node\n}\n\nexport function serializeSubtree(el: HTMLElement): SerializedNode[] {\n const nodes: SerializedNode[] = []\n collectNode(el, nodes)\n return nodes\n}\n\nexport function serializeTree(root: HTMLElement): SerializedNode[] {\n return serializeSubtree(root)\n}\n","import type { Viewport } from '../types'\n\nexport function getViewport(): Viewport {\n return {\n scrollX: Math.round(window.scrollX),\n scrollY: Math.round(window.scrollY),\n width: window.innerWidth,\n height: window.innerHeight,\n }\n}\n","import type { Config, RrwebEvent, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { record } from 'rrweb'\nimport { MASK_SELECTOR } from '../constants'\n\nexport class RrwebTracker implements Tracker {\n private stopFn: (() => void) | null = null\n\n constructor(\n private config: Config,\n private sender: Sender,\n ) {}\n\n start() {\n this.stopFn = record({\n emit: (event) => {\n const rrwebEvent: RrwebEvent = {\n event: 'rrweb',\n timestamp: new Date().toISOString(),\n rrwebEvent: event,\n }\n this.sender.add(rrwebEvent)\n },\n recordCrossOriginIframes: false,\n recordCanvas: false,\n maskTextSelector: MASK_SELECTOR,\n maskInputOptions: { password: true },\n maskTextFn: text => '*'.repeat(text.length),\n sampling: {\n mousemove: false,\n mouseInteraction: true,\n scroll: 500,\n input: 'last',\n },\n }) ?? null\n\n if (this.config.debug)\n console.log('[dataclient] rrweb recording started')\n }\n\n stop() {\n if (this.stopFn) {\n this.stopFn()\n this.stopFn = null\n }\n }\n\n static addMarker(tag: string, payload: unknown) {\n record.addCustomEvent(tag, payload)\n }\n}\n","import type { ActionEvent, Config, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { INTERACTIVE_ROLES, INTERACTIVE_TAGS, TEXT_INPUT_TYPES } from '../constants'\nimport { isMasked, maskText } from '../dom/mask'\nimport { getNodeId } from '../dom/serializer'\nimport { getViewport } from '../dom/viewport'\nimport { RrwebTracker } from './rrweb'\n\nexport class ActionTracker implements Tracker {\n private inputTimers = new WeakMap<HTMLElement, ReturnType<typeof setTimeout>>()\n private pendingInputs = new Set<HTMLElement>()\n\n private handleClick = (e: Event) => this.onClick(e)\n private handleInput = (e: Event) => this.onInput(e)\n private handleChange = (e: Event) => this.onChange(e)\n\n constructor(\n private config: Config,\n private sender: Sender,\n ) {}\n\n start() {\n document.addEventListener('click', this.handleClick, true)\n document.addEventListener('input', this.handleInput, true)\n document.addEventListener('change', this.handleChange, true)\n }\n\n stop() {\n document.removeEventListener('click', this.handleClick, true)\n document.removeEventListener('input', this.handleInput, true)\n document.removeEventListener('change', this.handleChange, true)\n }\n\n beforeUnload() {\n for (const el of this.pendingInputs) {\n const timer = this.inputTimers.get(el)\n if (timer) {\n clearTimeout(timer)\n }\n this.inputTimers.delete(el)\n this.recordInput(el)\n }\n this.pendingInputs.clear()\n }\n\n private onClick(e: Event) {\n const raw = e.target as HTMLElement\n if (!raw) {\n return\n }\n\n const tag = raw.tagName?.toLowerCase()\n if (tag === 'body' || tag === 'html') {\n return\n }\n\n const el = this.findMeaningfulElement(raw)\n const info = this.getElementInfo(el)\n\n const action: ActionEvent = {\n event: 'action',\n timestamp: new Date().toISOString(),\n type: 'click',\n targetId: info.targetId,\n tag: info.tag,\n text: info.text,\n url: location.href,\n state: info.state,\n viewport: getViewport(),\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] click: ${info.tag} \"${info.text}\"`)\n }\n\n this.sender.add(action)\n RrwebTracker.addMarker('click', { tag: info.tag, text: info.text, label: `Click: ${info.text || info.tag}` })\n }\n\n private onInput(e: Event) {\n const target = e.target as HTMLElement\n if (!target) {\n return\n }\n\n const tag = target.tagName?.toLowerCase()\n if (tag === 'input') {\n const type = (target as HTMLInputElement).type?.toLowerCase() || 'text'\n if (!TEXT_INPUT_TYPES.has(type)) {\n return\n }\n }\n else if (tag !== 'textarea') {\n return\n }\n\n this.pendingInputs.add(target)\n\n const prev = this.inputTimers.get(target)\n if (prev) {\n clearTimeout(prev)\n }\n\n this.inputTimers.set(target, setTimeout(() => {\n this.inputTimers.delete(target)\n this.pendingInputs.delete(target)\n this.recordInput(target)\n }, this.config.inputDebounce))\n }\n\n private recordInput(target: HTMLElement) {\n const rawValue = (target as HTMLInputElement).value || ''\n if (!rawValue) {\n return\n }\n\n const info = this.getElementInfo(target)\n const type = (target as HTMLInputElement).type?.toLowerCase() || ''\n const value = (info.masked || type === 'password') ? maskText(rawValue) : rawValue\n\n const action: ActionEvent = {\n event: 'action',\n timestamp: new Date().toISOString(),\n type: 'input',\n targetId: info.targetId,\n tag: info.tag,\n text: info.text,\n url: location.href,\n value,\n length: rawValue.length,\n viewport: getViewport(),\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] input: ${info.tag} \"${info.text}\" → ${value.length} chars`)\n }\n\n this.sender.add(action)\n RrwebTracker.addMarker('input', { tag: info.tag, text: info.text, label: `Input: ${info.text || info.tag}` })\n }\n\n private onChange(e: Event) {\n const target = e.target as HTMLElement\n if (!target) {\n return\n }\n\n const info = this.getElementInfo(target)\n const tag = target.tagName?.toLowerCase()\n\n const action: ActionEvent = {\n event: 'action',\n timestamp: new Date().toISOString(),\n type: 'change',\n targetId: info.targetId,\n tag: info.tag,\n text: info.text,\n url: location.href,\n viewport: getViewport(),\n }\n\n if (tag === 'select') {\n const selectValue = (target as HTMLSelectElement).value\n action.value = info.masked ? maskText(selectValue) : selectValue\n }\n else if (tag === 'input') {\n const type = (target as HTMLInputElement).type\n if (type === 'checkbox' || type === 'radio') {\n action.checked = (target as HTMLInputElement).checked\n }\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] change: ${info.tag} \"${info.text}\"`)\n }\n\n this.sender.add(action)\n RrwebTracker.addMarker('change', { tag: info.tag, text: info.text, label: `Change: ${info.text || info.tag}` })\n }\n\n private getElementInfo(el: HTMLElement) {\n const tag = el.tagName?.toLowerCase() || ''\n let text = ''\n\n if (tag === 'input' || tag === 'textarea') {\n text = (el as HTMLInputElement).placeholder || el.getAttribute('aria-label') || ''\n }\n else {\n for (const child of el.childNodes) {\n if (child.nodeType === Node.TEXT_NODE) {\n const t = child.textContent?.trim()\n if (t) {\n text += (text ? ' ' : '') + t\n }\n }\n }\n if (!text) {\n text = el.textContent?.trim().slice(0, 100) || ''\n }\n }\n\n const masked = isMasked(el)\n const finalText = masked ? maskText(text) : text\n\n return {\n tag,\n text: finalText.slice(0, 100),\n targetId: getNodeId(el),\n state: (el as HTMLButtonElement).disabled ? 'disabled' : 'enabled',\n masked,\n }\n }\n\n private findMeaningfulElement(el: HTMLElement): HTMLElement {\n let current: HTMLElement | null = el\n while (current && current !== document.body) {\n if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {\n return current\n }\n const role = current.getAttribute('role')\n if (role && INTERACTIVE_ROLES.has(role)) {\n return current\n }\n current = current.parentElement\n }\n return el\n }\n}\n","import type { AttrChange, Config, MutationAdd, MutationEvent, TextChange, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { WATCH_ATTRS } from '../constants'\nimport { getNodeId, removeNodeId, serializeSubtree } from '../dom/serializer'\n\nexport class MutationTracker implements Tracker {\n private observer: MutationObserver | null = null\n private debounceTimer: ReturnType<typeof setTimeout> | null = null\n\n private pendingAdds: MutationAdd[] = []\n private pendingRemoves: number[] = []\n private pendingTextChanges: TextChange[] = []\n private pendingAttrChanges: AttrChange[] = []\n\n constructor(\n private config: Config,\n private sender: Sender,\n private onMutation: () => void,\n ) {}\n\n start() {\n this.observer = new MutationObserver(mutations => this.handleMutations(mutations))\n this.observer.observe(document.body, {\n childList: true,\n subtree: true,\n characterData: true,\n attributes: true,\n attributeFilter: WATCH_ATTRS,\n })\n }\n\n stop() {\n this.observer?.disconnect()\n this.observer = null\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n }\n }\n\n beforeUnload() {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n this.flush()\n }\n }\n\n private flush() {\n this.debounceTimer = null\n\n if (\n this.pendingAdds.length === 0\n && this.pendingRemoves.length === 0\n && this.pendingTextChanges.length === 0\n && this.pendingAttrChanges.length === 0\n ) {\n return\n }\n\n const mutation: MutationEvent = {\n event: 'mutation',\n timestamp: new Date().toISOString(),\n url: window.location.href,\n adds: this.pendingAdds,\n removes: this.pendingRemoves,\n text_changes: this.pendingTextChanges,\n attr_changes: this.pendingAttrChanges,\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] mutation: +${this.pendingAdds.length} -${this.pendingRemoves.length} text:${this.pendingTextChanges.length} attr:${this.pendingAttrChanges.length}`)\n }\n\n this.sender.add(mutation)\n this.onMutation()\n\n this.pendingAdds = []\n this.pendingRemoves = []\n this.pendingTextChanges = []\n this.pendingAttrChanges = []\n }\n\n private scheduleFlush() {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer)\n }\n this.debounceTimer = setTimeout(() => this.flush(), this.config.mutationDebounce)\n }\n\n private handleMutations(mutations: MutationRecord[]) {\n for (const m of mutations) {\n for (const node of m.addedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE) {\n continue\n }\n const el = node as HTMLElement\n const nodes = serializeSubtree(el)\n if (nodes.length === 0) {\n continue\n }\n\n const parentId = getNodeId(el.parentElement!)\n if (parentId === null) {\n continue\n }\n\n this.pendingAdds.push({ parentId, nodes })\n }\n\n for (const node of m.removedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE) {\n continue\n }\n const id = getNodeId(node)\n if (id !== null) {\n this.pendingRemoves.push(id)\n removeNodeId(id)\n }\n }\n\n if (m.type === 'characterData' && m.target.parentElement) {\n const parentId = getNodeId(m.target.parentElement)\n if (parentId !== null) {\n const text = m.target.textContent?.trim().slice(0, 200) || ''\n this.pendingTextChanges.push({ id: parentId, text })\n }\n }\n\n if (m.type === 'attributes' && m.attributeName) {\n const id = getNodeId(m.target)\n if (id !== null) {\n const value = (m.target as HTMLElement).getAttribute(m.attributeName)\n this.pendingAttrChanges.push({\n id,\n attr: m.attributeName,\n value: value?.slice(0, 200) ?? null,\n })\n }\n }\n }\n\n if (\n this.pendingAdds.length > 0\n || this.pendingRemoves.length > 0\n || this.pendingTextChanges.length > 0\n || this.pendingAttrChanges.length > 0\n ) {\n this.scheduleFlush()\n }\n }\n}\n","import type { Config, SnapshotEvent, Tracker } from '../types'\nimport type { Sender } from '../utils/sender'\nimport { resetIds, serializeTree } from '../dom/serializer'\nimport { getViewport } from '../dom/viewport'\n\nexport class SnapshotTracker implements Tracker {\n private lastUrl = ''\n private urlPollTimer: ReturnType<typeof setInterval> | null = null\n private checkpointTimer: ReturnType<typeof setInterval> | null = null\n private hasMutations = false\n\n constructor(\n private config: Config,\n private sender: Sender,\n ) {}\n\n start() {\n this.lastUrl = location.href\n this.recordSnapshot()\n\n this.urlPollTimer = setInterval(() => this.pollUrl(), 500)\n this.checkpointTimer = setInterval(() => this.checkpoint(), this.config.checkpointInterval)\n }\n\n stop() {\n if (this.urlPollTimer) {\n clearInterval(this.urlPollTimer)\n }\n if (this.checkpointTimer) {\n clearInterval(this.checkpointTimer)\n }\n }\n\n beforeUnload() {\n if (this.hasMutations) {\n this.recordSnapshot()\n }\n }\n\n markMutation() {\n this.hasMutations = true\n }\n\n private recordSnapshot() {\n resetIds()\n\n const snapshot: SnapshotEvent = {\n event: 'snapshot',\n timestamp: new Date().toISOString(),\n url: location.href,\n title: document.title,\n tree: serializeTree(document.body),\n viewport: getViewport(),\n }\n\n if (this.config.debug) {\n console.log(`[dataclient] snapshot: ${location.href} (${snapshot.tree.length} nodes)`)\n }\n\n this.sender.add(snapshot)\n this.sender.flush()\n this.hasMutations = false\n }\n\n private pollUrl() {\n if (location.href !== this.lastUrl) {\n const prev = this.lastUrl\n this.lastUrl = location.href\n if (prev) {\n if (this.config.debug) {\n console.log(`[dataclient] URL changed: ${prev} → ${location.href}`)\n }\n this.recordSnapshot()\n }\n }\n }\n\n private checkpoint() {\n if (this.hasMutations) {\n if (this.config.debug) {\n console.log('[dataclient] checkpoint snapshot')\n }\n this.recordSnapshot()\n }\n }\n}\n","export function getDeviceId(key: string): string {\n let id = localStorage.getItem(key)\n if (!id) {\n id = generateId()\n localStorage.setItem(key, id)\n }\n return id\n}\n\nexport function generateId(): string {\n const ts = Date.now().toString(36)\n const rand = Math.random().toString(36).substring(2, 12)\n return `${ts}-${rand}`\n}\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 const isRrwebSnapshot = event.event === 'rrweb'\n && event.rrwebEvent.type === 2\n\n if (isRrwebSnapshot || this.queue.length >= this.batchSize) {\n this.flush()\n }\n }\n\n flush() {\n this.flushPromise = this.flushPromise.then(() => this.doFlush())\n }\n\n flushSync() {\n if (this.queue.length === 0)\n return\n\n const events = this.queue.splice(0)\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 async doFlush() {\n if (this.queue.length === 0)\n 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)\n 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.d.cts
CHANGED
|
@@ -30,6 +30,7 @@ interface SnapshotEvent {
|
|
|
30
30
|
interface MutationEvent {
|
|
31
31
|
event: 'mutation';
|
|
32
32
|
timestamp: string;
|
|
33
|
+
url: string;
|
|
33
34
|
adds: MutationAdd[];
|
|
34
35
|
removes: number[];
|
|
35
36
|
text_changes: TextChange[];
|
|
@@ -37,7 +38,7 @@ interface MutationEvent {
|
|
|
37
38
|
}
|
|
38
39
|
interface MutationAdd {
|
|
39
40
|
parentId: number;
|
|
40
|
-
|
|
41
|
+
nodes: SerializedNode[];
|
|
41
42
|
}
|
|
42
43
|
interface TextChange {
|
|
43
44
|
id: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ interface SnapshotEvent {
|
|
|
30
30
|
interface MutationEvent {
|
|
31
31
|
event: 'mutation';
|
|
32
32
|
timestamp: string;
|
|
33
|
+
url: string;
|
|
33
34
|
adds: MutationAdd[];
|
|
34
35
|
removes: number[];
|
|
35
36
|
text_changes: TextChange[];
|
|
@@ -37,7 +38,7 @@ interface MutationEvent {
|
|
|
37
38
|
}
|
|
38
39
|
interface MutationAdd {
|
|
39
40
|
parentId: number;
|
|
40
|
-
|
|
41
|
+
nodes: SerializedNode[];
|
|
41
42
|
}
|
|
42
43
|
interface TextChange {
|
|
43
44
|
id: number;
|
package/dist/index.global.js
CHANGED
|
@@ -287,7 +287,7 @@ var dataclient = (() => {
|
|
|
287
287
|
h: Math.round(r.height)
|
|
288
288
|
};
|
|
289
289
|
}
|
|
290
|
-
function
|
|
290
|
+
function collectNode(el, out) {
|
|
291
291
|
const tag = el.tagName?.toLowerCase();
|
|
292
292
|
if (!tag || SKIP_TAGS.has(tag)) {
|
|
293
293
|
return null;
|
|
@@ -300,13 +300,6 @@ var dataclient = (() => {
|
|
|
300
300
|
const icon = getElementIcon(el);
|
|
301
301
|
const attrs = getAttrs(el);
|
|
302
302
|
const rect = getRect(el);
|
|
303
|
-
const children = [];
|
|
304
|
-
for (const child of el.children) {
|
|
305
|
-
const serialized = serializeNode(child);
|
|
306
|
-
if (serialized) {
|
|
307
|
-
children.push(serialized.id);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
303
|
const node2 = { id, tag };
|
|
311
304
|
if (text) {
|
|
312
305
|
node2.text = text;
|
|
@@ -320,26 +313,27 @@ var dataclient = (() => {
|
|
|
320
313
|
if (rect) {
|
|
321
314
|
node2.rect = rect;
|
|
322
315
|
}
|
|
323
|
-
|
|
324
|
-
|
|
316
|
+
out.push(node2);
|
|
317
|
+
const childrenIds = [];
|
|
318
|
+
for (const child of el.children) {
|
|
319
|
+
const childNode = collectNode(child, out);
|
|
320
|
+
if (childNode) {
|
|
321
|
+
childrenIds.push(childNode.id);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (childrenIds.length > 0) {
|
|
325
|
+
node2.children = childrenIds;
|
|
325
326
|
}
|
|
326
327
|
return node2;
|
|
327
328
|
}
|
|
328
|
-
function
|
|
329
|
+
function serializeSubtree(el) {
|
|
329
330
|
const nodes = [];
|
|
330
|
-
|
|
331
|
-
const node2 = serializeNode(el);
|
|
332
|
-
if (!node2) {
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
|
-
nodes.push(node2);
|
|
336
|
-
for (const child of el.children) {
|
|
337
|
-
walk(child);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
walk(root2);
|
|
331
|
+
collectNode(el, nodes);
|
|
341
332
|
return nodes;
|
|
342
333
|
}
|
|
334
|
+
function serializeTree(root2) {
|
|
335
|
+
return serializeSubtree(root2);
|
|
336
|
+
}
|
|
343
337
|
|
|
344
338
|
// src/dom/viewport.ts
|
|
345
339
|
function getViewport() {
|
|
@@ -1162,7 +1156,7 @@ var dataclient = (() => {
|
|
|
1162
1156
|
listener();
|
|
1163
1157
|
});
|
|
1164
1158
|
}
|
|
1165
|
-
function
|
|
1159
|
+
function serializeNode(n2, options) {
|
|
1166
1160
|
const {
|
|
1167
1161
|
doc,
|
|
1168
1162
|
mirror: mirror2,
|
|
@@ -1561,7 +1555,7 @@ var dataclient = (() => {
|
|
|
1561
1555
|
checkAncestors
|
|
1562
1556
|
);
|
|
1563
1557
|
}
|
|
1564
|
-
const _serializedNode =
|
|
1558
|
+
const _serializedNode = serializeNode(n2, {
|
|
1565
1559
|
doc,
|
|
1566
1560
|
mirror: mirror2,
|
|
1567
1561
|
blockClass,
|
|
@@ -12931,6 +12925,7 @@ var dataclient = (() => {
|
|
|
12931
12925
|
const mutation = {
|
|
12932
12926
|
event: "mutation",
|
|
12933
12927
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12928
|
+
url: window.location.href,
|
|
12934
12929
|
adds: this.pendingAdds,
|
|
12935
12930
|
removes: this.pendingRemoves,
|
|
12936
12931
|
text_changes: this.pendingTextChanges,
|
|
@@ -12959,15 +12954,15 @@ var dataclient = (() => {
|
|
|
12959
12954
|
continue;
|
|
12960
12955
|
}
|
|
12961
12956
|
const el = node2;
|
|
12962
|
-
const
|
|
12963
|
-
if (
|
|
12957
|
+
const nodes = serializeSubtree(el);
|
|
12958
|
+
if (nodes.length === 0) {
|
|
12964
12959
|
continue;
|
|
12965
12960
|
}
|
|
12966
12961
|
const parentId = getNodeId(el.parentElement);
|
|
12967
12962
|
if (parentId === null) {
|
|
12968
12963
|
continue;
|
|
12969
12964
|
}
|
|
12970
|
-
this.pendingAdds.push({ parentId,
|
|
12965
|
+
this.pendingAdds.push({ parentId, nodes });
|
|
12971
12966
|
}
|
|
12972
12967
|
for (const node2 of m.removedNodes) {
|
|
12973
12968
|
if (node2.nodeType !== Node.ELEMENT_NODE) {
|