@tamsensedev/dataclient 0.1.10 → 0.2.3
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 -1
- package/dist/index.cjs +139 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.global.js +139 -25
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +139 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@tamsensedev/dataclient)
|
|
5
5
|
[](https://github.com/tamsensedev/dataclient-js/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
**[Website](https://tamsense.com)**
|
|
7
|
+
**[Website](https://tamsense.com)**
|
|
8
8
|
|
|
9
9
|
Capture frontend user behavior for TAMsense to detect friction across onboarding, activation, evaluation, and conversion to paid journeys.
|
|
10
10
|
|
|
@@ -105,6 +105,7 @@ const client = new DataClient({
|
|
|
105
105
|
| `idleTimeout` | `number` | `3600000` | Session idle timeout in ms (default 1h) |
|
|
106
106
|
| `batchSize` | `number` | `5` | Events per batch |
|
|
107
107
|
| `flushInterval` | `number` | `5000` | Flush interval in ms |
|
|
108
|
+
| `scoped` | `string` | `''` | Attribute name that narrows tracking to a specific subtree when present on the page. See [Scoped mode](#scoped-mode). |
|
|
108
109
|
|
|
109
110
|
### `client.setUser(userId)`
|
|
110
111
|
|
|
@@ -137,6 +138,35 @@ Passwords are always masked automatically.
|
|
|
137
138
|
|
|
138
139
|
---
|
|
139
140
|
|
|
141
|
+
## Scoped mode
|
|
142
|
+
|
|
143
|
+
Pass `scoped` with an attribute name to make tracking page-aware:
|
|
144
|
+
|
|
145
|
+
- If the attribute exists on the current page, the SDK tracks **only inside** that element. DOM snapshots, mutations, and user actions are limited to that subtree, and everything outside is masked in the session replay.
|
|
146
|
+
- If the attribute does not exist on the current page, the SDK tracks **the whole page** as usual.
|
|
147
|
+
|
|
148
|
+
The SDK re-evaluates as the DOM changes (useful for SPAs and widgets that mount asynchronously) — when the attribute appears, the scope narrows; when it goes away, the scope widens back.
|
|
149
|
+
|
|
150
|
+
```html
|
|
151
|
+
<!-- Pages where you want to limit tracking -->
|
|
152
|
+
<div dataclient-root>
|
|
153
|
+
<!-- Only this subtree is tracked -->
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<!-- Pages without the attribute are tracked in full -->
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
new DataClient({
|
|
161
|
+
apiKey: 'YOUR_API_KEY',
|
|
162
|
+
scoped: 'dataclient-root',
|
|
163
|
+
})
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The attribute name is up to you — `dataclient-root` is just a convention. Any HTML-valid attribute name works (e.g. `data-my-widget`).
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
140
170
|
## Framework examples
|
|
141
171
|
|
|
142
172
|
### Nuxt
|
package/dist/index.cjs
CHANGED
|
@@ -348,14 +348,19 @@ function getViewport() {
|
|
|
348
348
|
// src/trackers/rrweb.ts
|
|
349
349
|
var import_rrweb = require("rrweb");
|
|
350
350
|
var RrwebTracker = class {
|
|
351
|
-
constructor(config, sender) {
|
|
351
|
+
constructor(config, sender, root) {
|
|
352
352
|
this.config = config;
|
|
353
353
|
this.sender = sender;
|
|
354
|
+
this.root = root;
|
|
354
355
|
}
|
|
355
356
|
config;
|
|
356
357
|
sender;
|
|
358
|
+
root;
|
|
357
359
|
stopFn = null;
|
|
358
360
|
start() {
|
|
361
|
+
const scoped = this.config.scoped;
|
|
362
|
+
const rootIsScoped = !!scoped && this.root !== document.body && this.root.hasAttribute(scoped);
|
|
363
|
+
const blockSelector = rootIsScoped ? `:not([${scoped}]):not([${scoped}] *):not(:has([${scoped}]))` : void 0;
|
|
359
364
|
this.stopFn = (0, import_rrweb.record)({
|
|
360
365
|
emit: (event) => {
|
|
361
366
|
const rrwebEvent = {
|
|
@@ -370,6 +375,7 @@ var RrwebTracker = class {
|
|
|
370
375
|
maskTextSelector: MASK_SELECTOR,
|
|
371
376
|
maskInputOptions: { password: true },
|
|
372
377
|
maskTextFn: (text) => "*".repeat(text.length),
|
|
378
|
+
blockSelector,
|
|
373
379
|
sampling: {
|
|
374
380
|
mousemove: false,
|
|
375
381
|
mouseInteraction: true,
|
|
@@ -393,26 +399,33 @@ var RrwebTracker = class {
|
|
|
393
399
|
|
|
394
400
|
// src/trackers/action.ts
|
|
395
401
|
var ActionTracker = class {
|
|
396
|
-
constructor(config, sender) {
|
|
402
|
+
constructor(config, sender, root) {
|
|
397
403
|
this.config = config;
|
|
398
404
|
this.sender = sender;
|
|
405
|
+
this.root = root;
|
|
399
406
|
}
|
|
400
407
|
config;
|
|
401
408
|
sender;
|
|
409
|
+
root;
|
|
402
410
|
inputTimers = /* @__PURE__ */ new WeakMap();
|
|
403
411
|
pendingInputs = /* @__PURE__ */ new Set();
|
|
404
412
|
handleClick = (e) => this.onClick(e);
|
|
405
413
|
handleInput = (e) => this.onInput(e);
|
|
406
414
|
handleChange = (e) => this.onChange(e);
|
|
415
|
+
get listenerTarget() {
|
|
416
|
+
return this.config.scoped ? this.root : document;
|
|
417
|
+
}
|
|
407
418
|
start() {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
419
|
+
const target = this.listenerTarget;
|
|
420
|
+
target.addEventListener("click", this.handleClick, true);
|
|
421
|
+
target.addEventListener("input", this.handleInput, true);
|
|
422
|
+
target.addEventListener("change", this.handleChange, true);
|
|
411
423
|
}
|
|
412
424
|
stop() {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
425
|
+
const target = this.listenerTarget;
|
|
426
|
+
target.removeEventListener("click", this.handleClick, true);
|
|
427
|
+
target.removeEventListener("input", this.handleInput, true);
|
|
428
|
+
target.removeEventListener("change", this.handleChange, true);
|
|
416
429
|
}
|
|
417
430
|
beforeUnload() {
|
|
418
431
|
for (const el of this.pendingInputs) {
|
|
@@ -566,7 +579,7 @@ var ActionTracker = class {
|
|
|
566
579
|
}
|
|
567
580
|
findMeaningfulElement(el) {
|
|
568
581
|
let current = el;
|
|
569
|
-
while (current && current !== document.body) {
|
|
582
|
+
while (current && current !== this.root && current !== document.body) {
|
|
570
583
|
if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {
|
|
571
584
|
return current;
|
|
572
585
|
}
|
|
@@ -582,13 +595,15 @@ var ActionTracker = class {
|
|
|
582
595
|
|
|
583
596
|
// src/trackers/mutation.ts
|
|
584
597
|
var MutationTracker = class {
|
|
585
|
-
constructor(config, sender, onMutation) {
|
|
598
|
+
constructor(config, sender, root, onMutation) {
|
|
586
599
|
this.config = config;
|
|
587
600
|
this.sender = sender;
|
|
601
|
+
this.root = root;
|
|
588
602
|
this.onMutation = onMutation;
|
|
589
603
|
}
|
|
590
604
|
config;
|
|
591
605
|
sender;
|
|
606
|
+
root;
|
|
592
607
|
onMutation;
|
|
593
608
|
observer = null;
|
|
594
609
|
debounceTimer = null;
|
|
@@ -598,7 +613,7 @@ var MutationTracker = class {
|
|
|
598
613
|
pendingAttrChanges = [];
|
|
599
614
|
start() {
|
|
600
615
|
this.observer = new MutationObserver((mutations) => this.handleMutations(mutations));
|
|
601
|
-
this.observer.observe(
|
|
616
|
+
this.observer.observe(this.root, {
|
|
602
617
|
childList: true,
|
|
603
618
|
subtree: true,
|
|
604
619
|
characterData: true,
|
|
@@ -703,12 +718,14 @@ var MutationTracker = class {
|
|
|
703
718
|
|
|
704
719
|
// src/trackers/snapshot.ts
|
|
705
720
|
var SnapshotTracker = class {
|
|
706
|
-
constructor(config, sender) {
|
|
721
|
+
constructor(config, sender, root) {
|
|
707
722
|
this.config = config;
|
|
708
723
|
this.sender = sender;
|
|
724
|
+
this.root = root;
|
|
709
725
|
}
|
|
710
726
|
config;
|
|
711
727
|
sender;
|
|
728
|
+
root;
|
|
712
729
|
lastUrl = "";
|
|
713
730
|
urlPollTimer = null;
|
|
714
731
|
checkpointTimer = null;
|
|
@@ -742,7 +759,7 @@ var SnapshotTracker = class {
|
|
|
742
759
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
743
760
|
url: location.href,
|
|
744
761
|
title: document.title,
|
|
745
|
-
tree: serializeTree(
|
|
762
|
+
tree: serializeTree(this.root),
|
|
746
763
|
viewport: getViewport()
|
|
747
764
|
};
|
|
748
765
|
if (this.config.debug) {
|
|
@@ -915,8 +932,10 @@ var defaults = {
|
|
|
915
932
|
inputDebounce: 1e3,
|
|
916
933
|
sessionIdKey: "sc2_sid",
|
|
917
934
|
deviceIdKey: "sc2_did",
|
|
918
|
-
apiKey: ""
|
|
935
|
+
apiKey: "",
|
|
936
|
+
scoped: ""
|
|
919
937
|
};
|
|
938
|
+
var VALID_ATTR_NAME = /^[a-z][\w-]*$/i;
|
|
920
939
|
var DataClient = class {
|
|
921
940
|
sender = null;
|
|
922
941
|
trackers = [];
|
|
@@ -924,13 +943,21 @@ var DataClient = class {
|
|
|
924
943
|
deviceId;
|
|
925
944
|
idleTimer = null;
|
|
926
945
|
userId = null;
|
|
946
|
+
rootEl = null;
|
|
947
|
+
scopeObserver = null;
|
|
948
|
+
scopeCheckScheduled = false;
|
|
949
|
+
activityHandler = null;
|
|
950
|
+
activityTarget = null;
|
|
927
951
|
constructor(options) {
|
|
928
952
|
this.config = { ...defaults, ...options };
|
|
953
|
+
this.config.scoped = this.normalizeScoped(this.config.scoped);
|
|
929
954
|
this.deviceId = getDeviceId(this.config.deviceIdKey);
|
|
930
|
-
this.
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
955
|
+
if (this.config.scoped) {
|
|
956
|
+
this.startScopedMode();
|
|
957
|
+
} else {
|
|
958
|
+
this.startSession(document.body);
|
|
959
|
+
this.attachActivityListeners(document);
|
|
960
|
+
}
|
|
934
961
|
}
|
|
935
962
|
setUser(userId) {
|
|
936
963
|
this.userId = userId;
|
|
@@ -939,10 +966,77 @@ var DataClient = class {
|
|
|
939
966
|
excludeSession(reason = "") {
|
|
940
967
|
this.sender?.add({ event: "exclude", timestamp: (/* @__PURE__ */ new Date()).toISOString(), reason });
|
|
941
968
|
this.stopSession();
|
|
969
|
+
if (this.scopeObserver) {
|
|
970
|
+
this.scopeObserver.disconnect();
|
|
971
|
+
this.scopeObserver = null;
|
|
972
|
+
}
|
|
973
|
+
this.detachActivityListeners();
|
|
974
|
+
}
|
|
975
|
+
normalizeScoped(value) {
|
|
976
|
+
if (!value) {
|
|
977
|
+
return "";
|
|
978
|
+
}
|
|
979
|
+
if (!VALID_ATTR_NAME.test(value)) {
|
|
980
|
+
if (this.config.debug) {
|
|
981
|
+
console.warn(`[dataclient] invalid "scoped" attribute name: ${JSON.stringify(value)}. Falling back to non-scoped mode.`);
|
|
982
|
+
}
|
|
983
|
+
return "";
|
|
984
|
+
}
|
|
985
|
+
return value;
|
|
986
|
+
}
|
|
987
|
+
startScopedMode() {
|
|
988
|
+
const initial = this.resolveScope();
|
|
989
|
+
this.activateScope(initial);
|
|
990
|
+
this.scopeObserver = new MutationObserver(() => this.scheduleScopeCheck());
|
|
991
|
+
this.scopeObserver.observe(document.body, { childList: true, subtree: true });
|
|
992
|
+
if (this.config.debug) {
|
|
993
|
+
console.log(`[dataclient] scoped mode: looking for [${this.config.scoped}], fallback to body`);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
findRoot() {
|
|
997
|
+
return document.querySelector(`[${this.config.scoped}]`);
|
|
998
|
+
}
|
|
999
|
+
resolveScope() {
|
|
1000
|
+
return this.findRoot() ?? document.body;
|
|
1001
|
+
}
|
|
1002
|
+
scheduleScopeCheck() {
|
|
1003
|
+
if (this.scopeCheckScheduled) {
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
this.scopeCheckScheduled = true;
|
|
1007
|
+
queueMicrotask(() => {
|
|
1008
|
+
this.scopeCheckScheduled = false;
|
|
1009
|
+
this.handleScopeChange();
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
handleScopeChange() {
|
|
1013
|
+
const current = this.resolveScope();
|
|
1014
|
+
if (current === this.rootEl) {
|
|
1015
|
+
return;
|
|
1016
|
+
}
|
|
1017
|
+
this.detachActivityListeners();
|
|
1018
|
+
this.stopSession();
|
|
1019
|
+
this.activateScope(current);
|
|
1020
|
+
}
|
|
1021
|
+
activateScope(root) {
|
|
1022
|
+
this.rootEl = root;
|
|
1023
|
+
this.attachActivityListeners(root);
|
|
1024
|
+
this.startSession(root);
|
|
1025
|
+
if (this.config.debug) {
|
|
1026
|
+
const kind = root === document.body ? "document.body (fallback)" : `[${this.config.scoped}]`;
|
|
1027
|
+
console.log(`[dataclient] scope: ${kind}`);
|
|
1028
|
+
}
|
|
942
1029
|
}
|
|
943
1030
|
onActivity() {
|
|
944
1031
|
if (!this.sender) {
|
|
945
|
-
this.
|
|
1032
|
+
if (this.config.scoped) {
|
|
1033
|
+
if (!this.rootEl) {
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
this.startSession(this.rootEl);
|
|
1037
|
+
} else {
|
|
1038
|
+
this.startSession(document.body);
|
|
1039
|
+
}
|
|
946
1040
|
}
|
|
947
1041
|
this.resetIdleTimer();
|
|
948
1042
|
}
|
|
@@ -951,7 +1045,26 @@ var DataClient = class {
|
|
|
951
1045
|
clearTimeout(this.idleTimer);
|
|
952
1046
|
this.idleTimer = setTimeout(() => this.stopSession(), this.config.idleTimeout);
|
|
953
1047
|
}
|
|
954
|
-
|
|
1048
|
+
attachActivityListeners(target) {
|
|
1049
|
+
this.detachActivityListeners();
|
|
1050
|
+
const handler = () => this.onActivity();
|
|
1051
|
+
target.addEventListener("click", handler, true);
|
|
1052
|
+
target.addEventListener("input", handler, true);
|
|
1053
|
+
target.addEventListener("change", handler, true);
|
|
1054
|
+
this.activityHandler = handler;
|
|
1055
|
+
this.activityTarget = target;
|
|
1056
|
+
}
|
|
1057
|
+
detachActivityListeners() {
|
|
1058
|
+
if (!this.activityHandler || !this.activityTarget) {
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
this.activityTarget.removeEventListener("click", this.activityHandler, true);
|
|
1062
|
+
this.activityTarget.removeEventListener("input", this.activityHandler, true);
|
|
1063
|
+
this.activityTarget.removeEventListener("change", this.activityHandler, true);
|
|
1064
|
+
this.activityHandler = null;
|
|
1065
|
+
this.activityTarget = null;
|
|
1066
|
+
}
|
|
1067
|
+
startSession(root) {
|
|
955
1068
|
const sessionId = generateId();
|
|
956
1069
|
this.sender = new Sender(
|
|
957
1070
|
this.config.endpoint,
|
|
@@ -961,10 +1074,10 @@ var DataClient = class {
|
|
|
961
1074
|
this.deviceId,
|
|
962
1075
|
this.config.flushInterval
|
|
963
1076
|
);
|
|
964
|
-
const snapshotTracker = new SnapshotTracker(this.config, this.sender);
|
|
965
|
-
const mutationTracker = new MutationTracker(this.config, this.sender, () => snapshotTracker.markMutation());
|
|
966
|
-
const actionTracker = new ActionTracker(this.config, this.sender);
|
|
967
|
-
const rrwebTracker = new RrwebTracker(this.config, this.sender);
|
|
1077
|
+
const snapshotTracker = new SnapshotTracker(this.config, this.sender, root);
|
|
1078
|
+
const mutationTracker = new MutationTracker(this.config, this.sender, root, () => snapshotTracker.markMutation());
|
|
1079
|
+
const actionTracker = new ActionTracker(this.config, this.sender, root);
|
|
1080
|
+
const rrwebTracker = new RrwebTracker(this.config, this.sender, root);
|
|
968
1081
|
this.trackers = [snapshotTracker, mutationTracker, actionTracker, rrwebTracker];
|
|
969
1082
|
this.trackers.forEach((t) => t.start());
|
|
970
1083
|
if (this.userId) {
|
|
@@ -989,6 +1102,7 @@ var DataClient = class {
|
|
|
989
1102
|
clearTimeout(this.idleTimer);
|
|
990
1103
|
this.idleTimer = null;
|
|
991
1104
|
}
|
|
1105
|
+
this.trackers.forEach((t) => t.beforeUnload?.());
|
|
992
1106
|
this.trackers.forEach((t) => t.stop());
|
|
993
1107
|
this.trackers = [];
|
|
994
1108
|
if (this.sender) {
|
|
@@ -996,7 +1110,7 @@ var DataClient = class {
|
|
|
996
1110
|
this.sender = null;
|
|
997
1111
|
}
|
|
998
1112
|
if (this.config.debug) {
|
|
999
|
-
console.log("[dataclient] Session stopped
|
|
1113
|
+
console.log("[dataclient] Session stopped");
|
|
1000
1114
|
}
|
|
1001
1115
|
}
|
|
1002
1116
|
};
|
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/events',\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":[]}
|
|
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/events',\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 scoped: '',\n}\n\nconst VALID_ATTR_NAME = /^[a-z][\\w-]*$/i\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 private rootEl: HTMLElement | null = null\n private scopeObserver: MutationObserver | null = null\n private scopeCheckScheduled = false\n private activityHandler: (() => void) | null = null\n private activityTarget: EventTarget | null = null\n\n constructor(options?: Partial<Config>) {\n this.config = { ...defaults, ...options }\n this.config.scoped = this.normalizeScoped(this.config.scoped)\n this.deviceId = getDeviceId(this.config.deviceIdKey)\n\n if (this.config.scoped) {\n this.startScopedMode()\n }\n else {\n this.startSession(document.body)\n this.attachActivityListeners(document)\n }\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 if (this.scopeObserver) {\n this.scopeObserver.disconnect()\n this.scopeObserver = null\n }\n this.detachActivityListeners()\n }\n\n private normalizeScoped(value: string): string {\n if (!value) {\n return ''\n }\n if (!VALID_ATTR_NAME.test(value)) {\n if (this.config.debug) {\n console.warn(`[dataclient] invalid \"scoped\" attribute name: ${JSON.stringify(value)}. Falling back to non-scoped mode.`)\n }\n return ''\n }\n return value\n }\n\n private startScopedMode() {\n const initial = this.resolveScope()\n this.activateScope(initial)\n\n this.scopeObserver = new MutationObserver(() => this.scheduleScopeCheck())\n this.scopeObserver.observe(document.body, { childList: true, subtree: true })\n\n if (this.config.debug) {\n console.log(`[dataclient] scoped mode: looking for [${this.config.scoped}], fallback to body`)\n }\n }\n\n private findRoot(): HTMLElement | null {\n return document.querySelector<HTMLElement>(`[${this.config.scoped}]`)\n }\n\n private resolveScope(): HTMLElement {\n return this.findRoot() ?? document.body\n }\n\n private scheduleScopeCheck() {\n if (this.scopeCheckScheduled) {\n return\n }\n this.scopeCheckScheduled = true\n queueMicrotask(() => {\n this.scopeCheckScheduled = false\n this.handleScopeChange()\n })\n }\n\n private handleScopeChange() {\n const current = this.resolveScope()\n if (current === this.rootEl) {\n return\n }\n this.detachActivityListeners()\n this.stopSession()\n this.activateScope(current)\n }\n\n private activateScope(root: HTMLElement) {\n this.rootEl = root\n this.attachActivityListeners(root)\n this.startSession(root)\n if (this.config.debug) {\n const kind = root === document.body ? 'document.body (fallback)' : `[${this.config.scoped}]`\n console.log(`[dataclient] scope: ${kind}`)\n }\n }\n\n private onActivity() {\n if (!this.sender) {\n if (this.config.scoped) {\n if (!this.rootEl) {\n return\n }\n this.startSession(this.rootEl)\n }\n else {\n this.startSession(document.body)\n }\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 attachActivityListeners(target: EventTarget) {\n this.detachActivityListeners()\n const handler = () => this.onActivity()\n target.addEventListener('click', handler, true)\n target.addEventListener('input', handler, true)\n target.addEventListener('change', handler, true)\n this.activityHandler = handler\n this.activityTarget = target\n }\n\n private detachActivityListeners() {\n if (!this.activityHandler || !this.activityTarget) {\n return\n }\n this.activityTarget.removeEventListener('click', this.activityHandler, true)\n this.activityTarget.removeEventListener('input', this.activityHandler, true)\n this.activityTarget.removeEventListener('change', this.activityHandler, true)\n this.activityHandler = null\n this.activityTarget = null\n }\n\n private startSession(root: HTMLElement) {\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, root)\n const mutationTracker = new MutationTracker(this.config, this.sender, root, () => snapshotTracker.markMutation())\n const actionTracker = new ActionTracker(this.config, this.sender, root)\n const rrwebTracker = new RrwebTracker(this.config, this.sender, root)\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.beforeUnload?.())\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')\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 private root: HTMLElement,\n ) {}\n\n start() {\n const scoped = this.config.scoped\n const rootIsScoped = !!scoped && this.root !== document.body && this.root.hasAttribute(scoped)\n const blockSelector = rootIsScoped\n ? `:not([${scoped}]):not([${scoped}] *):not(:has([${scoped}]))`\n : undefined\n\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 blockSelector,\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 private root: HTMLElement,\n ) {}\n\n private get listenerTarget(): EventTarget {\n return this.config.scoped ? this.root : document\n }\n\n start() {\n const target = this.listenerTarget\n target.addEventListener('click', this.handleClick, true)\n target.addEventListener('input', this.handleInput, true)\n target.addEventListener('change', this.handleChange, true)\n }\n\n stop() {\n const target = this.listenerTarget\n target.removeEventListener('click', this.handleClick, true)\n target.removeEventListener('input', this.handleInput, true)\n target.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 !== this.root && 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 root: HTMLElement,\n private onMutation: () => void,\n ) {}\n\n start() {\n this.observer = new MutationObserver(mutations => this.handleMutations(mutations))\n this.observer.observe(this.root, {\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 private root: HTMLElement,\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(this.root),\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":[]}
|