@tamsensedev/dataclient 0.1.10 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/dist/index.cjs +143 -23
- 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 +143 -23
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +143 -23
- 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 marking the tracked subtree. When set, the SDK only tracks inside the element carrying this attribute. See [Scoped mode](#scoped-mode). |
|
|
108
109
|
|
|
109
110
|
### `client.setUser(userId)`
|
|
110
111
|
|
|
@@ -137,6 +138,37 @@ Passwords are always masked automatically.
|
|
|
137
138
|
|
|
138
139
|
---
|
|
139
140
|
|
|
141
|
+
## Scoped mode
|
|
142
|
+
|
|
143
|
+
When the SDK is embedded into a third-party site (for example, a widget mounted into a host page), you usually want tracking limited to the widget itself — not the surrounding page.
|
|
144
|
+
|
|
145
|
+
Pass `scoped` with the attribute name marking the widget root. The SDK will:
|
|
146
|
+
|
|
147
|
+
- wait until an element with that attribute appears in the DOM before starting a session
|
|
148
|
+
- scope DOM snapshots, mutations, and user actions to that subtree
|
|
149
|
+
- mask everything outside the subtree in the session replay
|
|
150
|
+
- end the session when the element is removed from the DOM
|
|
151
|
+
|
|
152
|
+
If the attribute element is never present, no data is collected.
|
|
153
|
+
|
|
154
|
+
```html
|
|
155
|
+
<!-- Host page -->
|
|
156
|
+
<div dataclient-root>
|
|
157
|
+
<!-- Your widget content -->
|
|
158
|
+
</div>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
new DataClient({
|
|
163
|
+
apiKey: 'YOUR_API_KEY',
|
|
164
|
+
scoped: 'dataclient-root',
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
The attribute name is up to you — `dataclient-root` is just a convention. Any HTML-valid attribute name works (e.g. `data-my-widget`).
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
140
172
|
## Framework examples
|
|
141
173
|
|
|
142
174
|
### Nuxt
|
package/dist/index.cjs
CHANGED
|
@@ -356,6 +356,8 @@ var RrwebTracker = class {
|
|
|
356
356
|
sender;
|
|
357
357
|
stopFn = null;
|
|
358
358
|
start() {
|
|
359
|
+
const scoped = this.config.scoped;
|
|
360
|
+
const blockSelector = scoped ? `:not([${scoped}]):not([${scoped}] *):not(:has([${scoped}]))` : void 0;
|
|
359
361
|
this.stopFn = (0, import_rrweb.record)({
|
|
360
362
|
emit: (event) => {
|
|
361
363
|
const rrwebEvent = {
|
|
@@ -370,6 +372,7 @@ var RrwebTracker = class {
|
|
|
370
372
|
maskTextSelector: MASK_SELECTOR,
|
|
371
373
|
maskInputOptions: { password: true },
|
|
372
374
|
maskTextFn: (text) => "*".repeat(text.length),
|
|
375
|
+
blockSelector,
|
|
373
376
|
sampling: {
|
|
374
377
|
mousemove: false,
|
|
375
378
|
mouseInteraction: true,
|
|
@@ -393,26 +396,33 @@ var RrwebTracker = class {
|
|
|
393
396
|
|
|
394
397
|
// src/trackers/action.ts
|
|
395
398
|
var ActionTracker = class {
|
|
396
|
-
constructor(config, sender) {
|
|
399
|
+
constructor(config, sender, root) {
|
|
397
400
|
this.config = config;
|
|
398
401
|
this.sender = sender;
|
|
402
|
+
this.root = root;
|
|
399
403
|
}
|
|
400
404
|
config;
|
|
401
405
|
sender;
|
|
406
|
+
root;
|
|
402
407
|
inputTimers = /* @__PURE__ */ new WeakMap();
|
|
403
408
|
pendingInputs = /* @__PURE__ */ new Set();
|
|
404
409
|
handleClick = (e) => this.onClick(e);
|
|
405
410
|
handleInput = (e) => this.onInput(e);
|
|
406
411
|
handleChange = (e) => this.onChange(e);
|
|
412
|
+
get listenerTarget() {
|
|
413
|
+
return this.config.scoped ? this.root : document;
|
|
414
|
+
}
|
|
407
415
|
start() {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
416
|
+
const target = this.listenerTarget;
|
|
417
|
+
target.addEventListener("click", this.handleClick, true);
|
|
418
|
+
target.addEventListener("input", this.handleInput, true);
|
|
419
|
+
target.addEventListener("change", this.handleChange, true);
|
|
411
420
|
}
|
|
412
421
|
stop() {
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
422
|
+
const target = this.listenerTarget;
|
|
423
|
+
target.removeEventListener("click", this.handleClick, true);
|
|
424
|
+
target.removeEventListener("input", this.handleInput, true);
|
|
425
|
+
target.removeEventListener("change", this.handleChange, true);
|
|
416
426
|
}
|
|
417
427
|
beforeUnload() {
|
|
418
428
|
for (const el of this.pendingInputs) {
|
|
@@ -566,7 +576,7 @@ var ActionTracker = class {
|
|
|
566
576
|
}
|
|
567
577
|
findMeaningfulElement(el) {
|
|
568
578
|
let current = el;
|
|
569
|
-
while (current && current !== document.body) {
|
|
579
|
+
while (current && current !== this.root && current !== document.body) {
|
|
570
580
|
if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {
|
|
571
581
|
return current;
|
|
572
582
|
}
|
|
@@ -582,13 +592,15 @@ var ActionTracker = class {
|
|
|
582
592
|
|
|
583
593
|
// src/trackers/mutation.ts
|
|
584
594
|
var MutationTracker = class {
|
|
585
|
-
constructor(config, sender, onMutation) {
|
|
595
|
+
constructor(config, sender, root, onMutation) {
|
|
586
596
|
this.config = config;
|
|
587
597
|
this.sender = sender;
|
|
598
|
+
this.root = root;
|
|
588
599
|
this.onMutation = onMutation;
|
|
589
600
|
}
|
|
590
601
|
config;
|
|
591
602
|
sender;
|
|
603
|
+
root;
|
|
592
604
|
onMutation;
|
|
593
605
|
observer = null;
|
|
594
606
|
debounceTimer = null;
|
|
@@ -598,7 +610,7 @@ var MutationTracker = class {
|
|
|
598
610
|
pendingAttrChanges = [];
|
|
599
611
|
start() {
|
|
600
612
|
this.observer = new MutationObserver((mutations) => this.handleMutations(mutations));
|
|
601
|
-
this.observer.observe(
|
|
613
|
+
this.observer.observe(this.root, {
|
|
602
614
|
childList: true,
|
|
603
615
|
subtree: true,
|
|
604
616
|
characterData: true,
|
|
@@ -703,12 +715,14 @@ var MutationTracker = class {
|
|
|
703
715
|
|
|
704
716
|
// src/trackers/snapshot.ts
|
|
705
717
|
var SnapshotTracker = class {
|
|
706
|
-
constructor(config, sender) {
|
|
718
|
+
constructor(config, sender, root) {
|
|
707
719
|
this.config = config;
|
|
708
720
|
this.sender = sender;
|
|
721
|
+
this.root = root;
|
|
709
722
|
}
|
|
710
723
|
config;
|
|
711
724
|
sender;
|
|
725
|
+
root;
|
|
712
726
|
lastUrl = "";
|
|
713
727
|
urlPollTimer = null;
|
|
714
728
|
checkpointTimer = null;
|
|
@@ -742,7 +756,7 @@ var SnapshotTracker = class {
|
|
|
742
756
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
743
757
|
url: location.href,
|
|
744
758
|
title: document.title,
|
|
745
|
-
tree: serializeTree(
|
|
759
|
+
tree: serializeTree(this.root),
|
|
746
760
|
viewport: getViewport()
|
|
747
761
|
};
|
|
748
762
|
if (this.config.debug) {
|
|
@@ -915,8 +929,10 @@ var defaults = {
|
|
|
915
929
|
inputDebounce: 1e3,
|
|
916
930
|
sessionIdKey: "sc2_sid",
|
|
917
931
|
deviceIdKey: "sc2_did",
|
|
918
|
-
apiKey: ""
|
|
932
|
+
apiKey: "",
|
|
933
|
+
scoped: ""
|
|
919
934
|
};
|
|
935
|
+
var VALID_ATTR_NAME = /^[a-z][\w-]*$/i;
|
|
920
936
|
var DataClient = class {
|
|
921
937
|
sender = null;
|
|
922
938
|
trackers = [];
|
|
@@ -924,13 +940,21 @@ var DataClient = class {
|
|
|
924
940
|
deviceId;
|
|
925
941
|
idleTimer = null;
|
|
926
942
|
userId = null;
|
|
943
|
+
rootEl = null;
|
|
944
|
+
scopeObserver = null;
|
|
945
|
+
scopeCheckScheduled = false;
|
|
946
|
+
activityHandler = null;
|
|
947
|
+
activityTarget = null;
|
|
927
948
|
constructor(options) {
|
|
928
949
|
this.config = { ...defaults, ...options };
|
|
950
|
+
this.config.scoped = this.normalizeScoped(this.config.scoped);
|
|
929
951
|
this.deviceId = getDeviceId(this.config.deviceIdKey);
|
|
930
|
-
this.
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
952
|
+
if (this.config.scoped) {
|
|
953
|
+
this.startScopedMode();
|
|
954
|
+
} else {
|
|
955
|
+
this.startSession(document.body);
|
|
956
|
+
this.attachActivityListeners(document);
|
|
957
|
+
}
|
|
934
958
|
}
|
|
935
959
|
setUser(userId) {
|
|
936
960
|
this.userId = userId;
|
|
@@ -939,10 +963,86 @@ var DataClient = class {
|
|
|
939
963
|
excludeSession(reason = "") {
|
|
940
964
|
this.sender?.add({ event: "exclude", timestamp: (/* @__PURE__ */ new Date()).toISOString(), reason });
|
|
941
965
|
this.stopSession();
|
|
966
|
+
if (this.scopeObserver) {
|
|
967
|
+
this.scopeObserver.disconnect();
|
|
968
|
+
this.scopeObserver = null;
|
|
969
|
+
}
|
|
970
|
+
this.detachActivityListeners();
|
|
971
|
+
}
|
|
972
|
+
normalizeScoped(value) {
|
|
973
|
+
if (!value) {
|
|
974
|
+
return "";
|
|
975
|
+
}
|
|
976
|
+
if (!VALID_ATTR_NAME.test(value)) {
|
|
977
|
+
if (this.config.debug) {
|
|
978
|
+
console.warn(`[dataclient] invalid "scoped" attribute name: ${JSON.stringify(value)}. Falling back to non-scoped mode.`);
|
|
979
|
+
}
|
|
980
|
+
return "";
|
|
981
|
+
}
|
|
982
|
+
return value;
|
|
983
|
+
}
|
|
984
|
+
startScopedMode() {
|
|
985
|
+
const initial = this.findRoot();
|
|
986
|
+
if (initial) {
|
|
987
|
+
this.onRootAppeared(initial);
|
|
988
|
+
}
|
|
989
|
+
this.scopeObserver = new MutationObserver(() => this.scheduleScopeCheck());
|
|
990
|
+
this.scopeObserver.observe(document.body, { childList: true, subtree: true });
|
|
991
|
+
if (this.config.debug) {
|
|
992
|
+
console.log(`[dataclient] scoped mode: watching for [${this.config.scoped}]`);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
findRoot() {
|
|
996
|
+
return document.querySelector(`[${this.config.scoped}]`);
|
|
997
|
+
}
|
|
998
|
+
scheduleScopeCheck() {
|
|
999
|
+
if (this.scopeCheckScheduled) {
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
this.scopeCheckScheduled = true;
|
|
1003
|
+
queueMicrotask(() => {
|
|
1004
|
+
this.scopeCheckScheduled = false;
|
|
1005
|
+
this.handleScopeChange();
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
handleScopeChange() {
|
|
1009
|
+
const current = this.findRoot();
|
|
1010
|
+
if (current === this.rootEl) {
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
if (this.rootEl) {
|
|
1014
|
+
this.onRootDisappeared();
|
|
1015
|
+
}
|
|
1016
|
+
if (current) {
|
|
1017
|
+
this.onRootAppeared(current);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
onRootAppeared(root) {
|
|
1021
|
+
this.rootEl = root;
|
|
1022
|
+
this.attachActivityListeners(root);
|
|
1023
|
+
this.startSession(root);
|
|
1024
|
+
if (this.config.debug) {
|
|
1025
|
+
console.log(`[dataclient] root [${this.config.scoped}] appeared`);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
onRootDisappeared() {
|
|
1029
|
+
if (this.config.debug) {
|
|
1030
|
+
console.log(`[dataclient] root [${this.config.scoped}] removed`);
|
|
1031
|
+
}
|
|
1032
|
+
this.detachActivityListeners();
|
|
1033
|
+
this.stopSession();
|
|
1034
|
+
this.rootEl = null;
|
|
942
1035
|
}
|
|
943
1036
|
onActivity() {
|
|
944
1037
|
if (!this.sender) {
|
|
945
|
-
this.
|
|
1038
|
+
if (this.config.scoped) {
|
|
1039
|
+
if (!this.rootEl) {
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
this.startSession(this.rootEl);
|
|
1043
|
+
} else {
|
|
1044
|
+
this.startSession(document.body);
|
|
1045
|
+
}
|
|
946
1046
|
}
|
|
947
1047
|
this.resetIdleTimer();
|
|
948
1048
|
}
|
|
@@ -951,7 +1051,26 @@ var DataClient = class {
|
|
|
951
1051
|
clearTimeout(this.idleTimer);
|
|
952
1052
|
this.idleTimer = setTimeout(() => this.stopSession(), this.config.idleTimeout);
|
|
953
1053
|
}
|
|
954
|
-
|
|
1054
|
+
attachActivityListeners(target) {
|
|
1055
|
+
this.detachActivityListeners();
|
|
1056
|
+
const handler = () => this.onActivity();
|
|
1057
|
+
target.addEventListener("click", handler, true);
|
|
1058
|
+
target.addEventListener("input", handler, true);
|
|
1059
|
+
target.addEventListener("change", handler, true);
|
|
1060
|
+
this.activityHandler = handler;
|
|
1061
|
+
this.activityTarget = target;
|
|
1062
|
+
}
|
|
1063
|
+
detachActivityListeners() {
|
|
1064
|
+
if (!this.activityHandler || !this.activityTarget) {
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
this.activityTarget.removeEventListener("click", this.activityHandler, true);
|
|
1068
|
+
this.activityTarget.removeEventListener("input", this.activityHandler, true);
|
|
1069
|
+
this.activityTarget.removeEventListener("change", this.activityHandler, true);
|
|
1070
|
+
this.activityHandler = null;
|
|
1071
|
+
this.activityTarget = null;
|
|
1072
|
+
}
|
|
1073
|
+
startSession(root) {
|
|
955
1074
|
const sessionId = generateId();
|
|
956
1075
|
this.sender = new Sender(
|
|
957
1076
|
this.config.endpoint,
|
|
@@ -961,9 +1080,9 @@ var DataClient = class {
|
|
|
961
1080
|
this.deviceId,
|
|
962
1081
|
this.config.flushInterval
|
|
963
1082
|
);
|
|
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);
|
|
1083
|
+
const snapshotTracker = new SnapshotTracker(this.config, this.sender, root);
|
|
1084
|
+
const mutationTracker = new MutationTracker(this.config, this.sender, root, () => snapshotTracker.markMutation());
|
|
1085
|
+
const actionTracker = new ActionTracker(this.config, this.sender, root);
|
|
967
1086
|
const rrwebTracker = new RrwebTracker(this.config, this.sender);
|
|
968
1087
|
this.trackers = [snapshotTracker, mutationTracker, actionTracker, rrwebTracker];
|
|
969
1088
|
this.trackers.forEach((t) => t.start());
|
|
@@ -989,6 +1108,7 @@ var DataClient = class {
|
|
|
989
1108
|
clearTimeout(this.idleTimer);
|
|
990
1109
|
this.idleTimer = null;
|
|
991
1110
|
}
|
|
1111
|
+
this.trackers.forEach((t) => t.beforeUnload?.());
|
|
992
1112
|
this.trackers.forEach((t) => t.stop());
|
|
993
1113
|
this.trackers = [];
|
|
994
1114
|
if (this.sender) {
|
|
@@ -996,7 +1116,7 @@ var DataClient = class {
|
|
|
996
1116
|
this.sender = null;
|
|
997
1117
|
}
|
|
998
1118
|
if (this.config.debug) {
|
|
999
|
-
console.log("[dataclient] Session stopped
|
|
1119
|
+
console.log("[dataclient] Session stopped");
|
|
1000
1120
|
}
|
|
1001
1121
|
}
|
|
1002
1122
|
};
|
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.findRoot()\n if (initial) {\n this.onRootAppeared(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: watching for [${this.config.scoped}]`)\n }\n }\n\n private findRoot(): HTMLElement | null {\n return document.querySelector<HTMLElement>(`[${this.config.scoped}]`)\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.findRoot()\n if (current === this.rootEl) {\n return\n }\n if (this.rootEl) {\n this.onRootDisappeared()\n }\n if (current) {\n this.onRootAppeared(current)\n }\n }\n\n private onRootAppeared(root: HTMLElement) {\n this.rootEl = root\n this.attachActivityListeners(root)\n this.startSession(root)\n if (this.config.debug) {\n console.log(`[dataclient] root [${this.config.scoped}] appeared`)\n }\n }\n\n private onRootDisappeared() {\n if (this.config.debug) {\n console.log(`[dataclient] root [${this.config.scoped}] removed`)\n }\n this.detachActivityListeners()\n this.stopSession()\n this.rootEl = null\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)\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 ) {}\n\n start() {\n const scoped = this.config.scoped\n const blockSelector = scoped\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,YAAY;AAClB,IAAM,gBAAgB,IAAI,SAAS;AAEnC,IAAM,YAAY,oBAAI,IAAI,CAAC,UAAU,SAAS,YAAY,OAAO,QAAQ,QAAQ,MAAM,CAAC;AAExF,IAAM,eAAe,oBAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ,CAAC;AAEM,IAAM,cAAc;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAEO,IAAM,mBAAmB,oBAAI,IAAI,CAAC,QAAQ,SAAS,YAAY,UAAU,OAAO,OAAO,QAAQ,CAAC;AAEhG,IAAM,mBAAmB,oBAAI,IAAI,CAAC,UAAU,KAAK,SAAS,UAAU,UAAU,CAAC;AAC/E,IAAM,oBAAoB,oBAAI,IAAI,CAAC,UAAU,QAAQ,OAAO,YAAY,YAAY,SAAS,UAAU,QAAQ,CAAC;;;AChDhH,SAAS,SAAS,IAAoC;AACzD,MAAI,CAAC;AACD,WAAO;AACX,QAAM,UAAU,GAAG,aAAa,KAAK,eAC/B,KACA,GAAG;AACT,SAAO,CAAC,CAAC,SAAS,QAAQ,aAAa;AAC3C;AAEO,SAAS,SAAS,MAAsB;AAC3C,MAAI,CAAC,MAAM;AACP,WAAO;AAAA,EACX;AACA,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,SAAS,GAAG,CAAC;AACxD,SAAO,KAAK,MAAM,GAAG,OAAO,IAAI,IAAI,OAAO,KAAK,SAAS,OAAO;AACpE;;;ACjBA,IAAM,sBAAsB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAEA,IAAM,yBAAyB;AAC/B,IAAM,uBAAuB;AAE7B,IAAM,oBAAoB;AAEnB,SAAS,eAAe,IAAgC;AAC3D,MAAI,CAAC,IAAI;AACL,WAAO;AAAA,EACX;AAEA,QAAM,SAAS,WAAW,EAAE;AAC5B,MAAI,QAAQ;AACR,WAAO;AAAA,EACX;AAEA,aAAW,SAAS,GAAG,UAAU;AAC7B,UAAM,OAAO,WAAW,KAAoB;AAC5C,QAAI,MAAM;AACN,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AACX;AAEA,SAAS,WAAW,IAAgC;AAChD,MAAI,CAAC,IAAI,SAAS;AACd,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,GAAG,QAAQ,YAAY;AAEnC,MAAI,QAAQ,OAAO;AACf,WAAO,cAAc,EAAE;AAAA,EAC3B;AAEA,MAAI,QAAQ,OAAO,QAAQ,UAAU,GAAG,UAAU,SAAS,SAAS,GAAG;AACnE,WAAO,eAAe,EAAE;AAAA,EAC5B;AAEA,MAAI,QAAQ,OAAO;AACf,WAAO,cAAc,EAAsB;AAAA,EAC/C;AAEA,QAAM,MAAM,GAAG,cAAc,KAAK;AAClC,MAAI,KAAK;AACL,WAAO,cAAc,GAAG;AAAA,EAC5B;AAEA,SAAO;AACX;AAEA,SAAS,cAAc,KAA6B;AAChD,QAAM,SAAS,IAAI,aAAa,aAAa;AAC7C,MAAI,QAAQ;AACR,WAAO;AAAA,EACX;AAEA,QAAM,WAAW,IAAI,aAAa,WAAW;AAC7C,MAAI,UAAU;AACV,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,IAAI,cAAc,KAAK;AACnC,MAAI,KAAK;AACL,UAAM,OAAO,IAAI,aAAa,MAAM,KAAK,IAAI,aAAa,YAAY;AACtE,QAAI,MAAM;AACN,YAAM,QAAQ,KAAK,MAAM,oBAAoB;AAC7C,UAAI,OAAO;AACP,eAAO,MAAM,CAAC;AAAA,MAClB;AAAA,IACJ;AAAA,EACJ;AAEA,QAAM,YAAY,IAAI,aAAa,YAAY;AAC/C,MAAI,WAAW;AACX,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,OAAO,IAAI,cAAc,WAAW,IAAI,YAAY,IAAI,aAAa,OAAO,KAAK;AAC7F,QAAM,YAAY,yBAAyB,GAAG;AAC9C,MAAI,WAAW;AACX,WAAO;AAAA,EACX;AAEA,SAAO;AACX;AAEA,SAAS,eAAe,IAAgC;AACpD,QAAM,MAAM,OAAO,GAAG,cAAc,WAAW,GAAG,YAAY,GAAG,aAAa,OAAO,KAAK;AAE1F,MAAI,uBAAuB,KAAK,GAAG,GAAG;AAClC,UAAM,OAAO,GAAG,aAAa,KAAK;AAClC,QAAI,MAAM;AACN,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO,yBAAyB,GAAG;AACvC;AAEA,SAAS,cAAc,KAAsC;AACzD,MAAI,IAAI,QAAQ,qBAAqB,IAAI,SAAS,mBAAmB;AACjE,WAAO;AAAA,EACX;AAEA,MAAI,IAAI,eAAe,qBAAqB,IAAI,gBAAgB,mBAAmB;AAC/E,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,IAAI,KAAK,KAAK;AAC1B,MAAI,KAAK;AACL,WAAO;AAAA,EACX;AAEA,QAAM,MAAM,IAAI,aAAa,KAAK;AAClC,MAAI,KAAK;AACL,UAAM,WAAW,IAAI,MAAM,GAAG,EAAE,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,GAAG,EAAE,CAAC;AAClE,QAAI,UAAU;AACV,aAAO;AAAA,IACX;AAAA,EACJ;AAEA,SAAO;AACX;AAEA,SAAS,yBAAyB,KAA4B;AAC1D,aAAW,WAAW,qBAAqB;AACvC,UAAM,QAAQ,IAAI,MAAM,OAAO;AAC/B,QAAI,OAAO;AACP,aAAO,MAAM,CAAC;AAAA,IAClB;AAAA,EACJ;AACA,SAAO;AACX;;;AC5IA,IAAI,SAAS;AACb,IAAM,WAAW,oBAAI,QAAsB;AAC3C,IAAM,WAAW,oBAAI,IAAkB;AAEhC,SAAS,WAAW;AACvB,WAAS;AACT,WAAS,MAAM;AACnB;AAEO,SAAS,SAAS,MAAoB;AACzC,QAAM,WAAW,SAAS,IAAI,IAAI;AAClC,MAAI,UAAU;AACV,WAAO;AAAA,EACX;AACA,QAAM,KAAK;AACX,WAAS,IAAI,MAAM,EAAE;AACrB,WAAS,IAAI,IAAI,IAAI;AACrB,SAAO;AACX;AAEO,SAAS,UAAU,MAA2B;AACjD,SAAO,SAAS,IAAI,IAAI,KAAK;AACjC;AAMO,SAAS,aAAa,IAAY;AACrC,QAAM,OAAO,SAAS,IAAI,EAAE;AAC5B,MAAI,MAAM;AACN,aAAS,OAAO,IAAI;AACpB,aAAS,OAAO,EAAE;AAAA,EACtB;AACJ;AAEA,SAAS,UAAU,IAA0B;AACzC,MAAI,GAAG;AACH,WAAO;AACX,MAAI,GAAG,aAAa,aAAa,MAAM;AACnC,WAAO;AACX,MAAI,CAAC,GAAG,gBAAgB,GAAG,YAAY,UAAU,iBAAiB,EAAE,EAAE,aAAa,SAAS;AACxF,WAAO;AAAA,EACX;AACA,SAAO;AACX;AAEA,SAAS,cAAc,IAAyB;AAC5C,MAAI,OAAO;AACX,aAAW,SAAS,GAAG,YAAY;AAC/B,QAAI,MAAM,aAAa,KAAK,WAAW;AACnC,YAAM,IAAI,MAAM,aAAa,KAAK;AAClC,UAAI,GAAG;AACH,iBAAS,OAAO,MAAM,MAAM;AAAA,MAChC;AAAA,IACJ;AAAA,EACJ;AACA,QAAM,YAAY,KAAK,MAAM,GAAG,GAAG;AACnC,SAAO,SAAS,EAAE,IAAI,SAAS,SAAS,IAAI;AAChD;AAEA,SAAS,SAAS,IAAqD;AACnE,QAAM,QAAgC,CAAC;AACvC,MAAI,WAAW;AACf,QAAM,SAAS,SAAS,EAAE;AAE1B,aAAW,QAAQ,cAAc;AAC7B,UAAM,QAAQ,GAAG,aAAa,IAAI;AAClC,QAAI,UAAU,QAAQ,UAAU,IAAI;AAChC,UAAI,IAAI,MAAM,MAAM,GAAG,GAAG;AAC1B,UAAI,WAAW,SAAS,WAAW,SAAS,gBAAgB;AACxD,YAAI,SAAS,CAAC;AAAA,MAClB;AACA,YAAM,IAAI,IAAI;AACd,iBAAW;AAAA,IACf;AAAA,EACJ;AAEA,SAAO,WAAW,QAAQ;AAC9B;AAEA,SAAS,QAAQ,IAAyC;AACtD,QAAM,IAAI,GAAG,sBAAsB;AACnC,MAAI,EAAE,UAAU,KAAK,EAAE,WAAW,GAAG;AACjC,WAAO;AAAA,EACX;AACA,SAAO;AAAA,IACH,GAAG,KAAK,MAAM,EAAE,OAAO,OAAO,OAAO;AAAA,IACrC,GAAG,KAAK,MAAM,EAAE,MAAM,OAAO,OAAO;AAAA,IACpC,GAAG,KAAK,MAAM,EAAE,KAAK;AAAA,IACrB,GAAG,KAAK,MAAM,EAAE,MAAM;AAAA,EAC1B;AACJ;AAEA,SAAS,YAAY,IAAiB,KAA8C;AAChF,QAAM,MAAM,GAAG,SAAS,YAAY;AACpC,MAAI,CAAC,OAAO,UAAU,IAAI,GAAG,GAAG;AAC5B,WAAO;AAAA,EACX;AACA,MAAI,CAAC,UAAU,EAAE,GAAG;AAChB,WAAO;AAAA,EACX;AAEA,QAAM,KAAK,SAAS,EAAE;AACtB,QAAM,OAAO,cAAc,EAAE;AAC7B,QAAM,OAAO,eAAe,EAAE;AAC9B,QAAM,QAAQ,SAAS,EAAE;AACzB,QAAM,OAAO,QAAQ,EAAE;AAEvB,QAAM,OAAuB,EAAE,IAAI,IAAI;AACvC,MAAI,MAAM;AACN,SAAK,OAAO;AAAA,EAChB;AACA,MAAI,MAAM;AACN,SAAK,OAAO;AAAA,EAChB;AACA,MAAI,OAAO;AACP,SAAK,QAAQ;AAAA,EACjB;AACA,MAAI,MAAM;AACN,SAAK,OAAO;AAAA,EAChB;AAEA,MAAI,KAAK,IAAI;AAEb,QAAM,cAAwB,CAAC;AAC/B,aAAW,SAAS,GAAG,UAAU;AAC7B,UAAM,YAAY,YAAY,OAAsB,GAAG;AACvD,QAAI,WAAW;AACX,kBAAY,KAAK,UAAU,EAAE;AAAA,IACjC;AAAA,EACJ;AACA,MAAI,YAAY,SAAS,GAAG;AACxB,SAAK,WAAW;AAAA,EACpB;AAEA,SAAO;AACX;AAEO,SAAS,iBAAiB,IAAmC;AAChE,QAAM,QAA0B,CAAC;AACjC,cAAY,IAAI,KAAK;AACrB,SAAO;AACX;AAEO,SAAS,cAAc,MAAqC;AAC/D,SAAO,iBAAiB,IAAI;AAChC;;;ACtJO,SAAS,cAAwB;AACpC,SAAO;AAAA,IACH,SAAS,KAAK,MAAM,OAAO,OAAO;AAAA,IAClC,SAAS,KAAK,MAAM,OAAO,OAAO;AAAA,IAClC,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,EACnB;AACJ;;;ACPA,mBAAuB;AAGhB,IAAM,eAAN,MAAsC;AAAA,EAGzC,YACY,QACA,QACV;AAFU;AACA;AAAA,EACT;AAAA,EAFS;AAAA,EACA;AAAA,EAJJ,SAA8B;AAAA,EAOtC,QAAQ;AACJ,UAAM,SAAS,KAAK,OAAO;AAC3B,UAAM,gBAAgB,SAChB,SAAS,MAAM,WAAW,MAAM,kBAAkB,MAAM,QACxD;AAEN,SAAK,aAAS,qBAAO;AAAA,MACjB,MAAM,CAAC,UAAU;AACb,cAAM,aAAyB;AAAA,UAC3B,OAAO;AAAA,UACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,UAClC,YAAY;AAAA,QAChB;AACA,aAAK,OAAO,IAAI,UAAU;AAAA,MAC9B;AAAA,MACA,0BAA0B;AAAA,MAC1B,cAAc;AAAA,MACd,kBAAkB;AAAA,MAClB,kBAAkB,EAAE,UAAU,KAAK;AAAA,MACnC,YAAY,UAAQ,IAAI,OAAO,KAAK,MAAM;AAAA,MAC1C;AAAA,MACA,UAAU;AAAA,QACN,WAAW;AAAA,QACX,kBAAkB;AAAA,QAClB,QAAQ;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,CAAC,KAAK;AAEN,QAAI,KAAK,OAAO;AACZ,cAAQ,IAAI,sCAAsC;AAAA,EAC1D;AAAA,EAEA,OAAO;AACH,QAAI,KAAK,QAAQ;AACb,WAAK,OAAO;AACZ,WAAK,SAAS;AAAA,IAClB;AAAA,EACJ;AAAA,EAEA,OAAO,UAAU,KAAa,SAAkB;AAC5C,wBAAO,eAAe,KAAK,OAAO;AAAA,EACtC;AACJ;;;AChDO,IAAM,gBAAN,MAAuC;AAAA,EAQ1C,YACY,QACA,QACA,MACV;AAHU;AACA;AACA;AAAA,EACT;AAAA,EAHS;AAAA,EACA;AAAA,EACA;AAAA,EAVJ,cAAc,oBAAI,QAAoD;AAAA,EACtE,gBAAgB,oBAAI,IAAiB;AAAA,EAErC,cAAc,CAAC,MAAa,KAAK,QAAQ,CAAC;AAAA,EAC1C,cAAc,CAAC,MAAa,KAAK,QAAQ,CAAC;AAAA,EAC1C,eAAe,CAAC,MAAa,KAAK,SAAS,CAAC;AAAA,EAQpD,IAAY,iBAA8B;AACtC,WAAO,KAAK,OAAO,SAAS,KAAK,OAAO;AAAA,EAC5C;AAAA,EAEA,QAAQ;AACJ,UAAM,SAAS,KAAK;AACpB,WAAO,iBAAiB,SAAS,KAAK,aAAa,IAAI;AACvD,WAAO,iBAAiB,SAAS,KAAK,aAAa,IAAI;AACvD,WAAO,iBAAiB,UAAU,KAAK,cAAc,IAAI;AAAA,EAC7D;AAAA,EAEA,OAAO;AACH,UAAM,SAAS,KAAK;AACpB,WAAO,oBAAoB,SAAS,KAAK,aAAa,IAAI;AAC1D,WAAO,oBAAoB,SAAS,KAAK,aAAa,IAAI;AAC1D,WAAO,oBAAoB,UAAU,KAAK,cAAc,IAAI;AAAA,EAChE;AAAA,EAEA,eAAe;AACX,eAAW,MAAM,KAAK,eAAe;AACjC,YAAM,QAAQ,KAAK,YAAY,IAAI,EAAE;AACrC,UAAI,OAAO;AACP,qBAAa,KAAK;AAAA,MACtB;AACA,WAAK,YAAY,OAAO,EAAE;AAC1B,WAAK,YAAY,EAAE;AAAA,IACvB;AACA,SAAK,cAAc,MAAM;AAAA,EAC7B;AAAA,EAEQ,QAAQ,GAAU;AACtB,UAAM,MAAM,EAAE;AACd,QAAI,CAAC,KAAK;AACN;AAAA,IACJ;AAEA,UAAM,MAAM,IAAI,SAAS,YAAY;AACrC,QAAI,QAAQ,UAAU,QAAQ,QAAQ;AAClC;AAAA,IACJ;AAEA,UAAM,KAAK,KAAK,sBAAsB,GAAG;AACzC,UAAM,OAAO,KAAK,eAAe,EAAE;AAEnC,UAAM,SAAsB;AAAA,MACxB,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,MAAM;AAAA,MACN,UAAU,KAAK;AAAA,MACf,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,KAAK,SAAS;AAAA,MACd,OAAO,KAAK;AAAA,MACZ,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,uBAAuB,KAAK,GAAG,KAAK,KAAK,IAAI,GAAG;AAAA,IAChE;AAEA,SAAK,OAAO,IAAI,MAAM;AACtB,iBAAa,UAAU,SAAS,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,UAAU,KAAK,QAAQ,KAAK,GAAG,GAAG,CAAC;AAAA,EAChH;AAAA,EAEQ,QAAQ,GAAU;AACtB,UAAM,SAAS,EAAE;AACjB,QAAI,CAAC,QAAQ;AACT;AAAA,IACJ;AAEA,UAAM,MAAM,OAAO,SAAS,YAAY;AACxC,QAAI,QAAQ,SAAS;AACjB,YAAM,OAAQ,OAA4B,MAAM,YAAY,KAAK;AACjE,UAAI,CAAC,iBAAiB,IAAI,IAAI,GAAG;AAC7B;AAAA,MACJ;AAAA,IACJ,WACS,QAAQ,YAAY;AACzB;AAAA,IACJ;AAEA,SAAK,cAAc,IAAI,MAAM;AAE7B,UAAM,OAAO,KAAK,YAAY,IAAI,MAAM;AACxC,QAAI,MAAM;AACN,mBAAa,IAAI;AAAA,IACrB;AAEA,SAAK,YAAY,IAAI,QAAQ,WAAW,MAAM;AAC1C,WAAK,YAAY,OAAO,MAAM;AAC9B,WAAK,cAAc,OAAO,MAAM;AAChC,WAAK,YAAY,MAAM;AAAA,IAC3B,GAAG,KAAK,OAAO,aAAa,CAAC;AAAA,EACjC;AAAA,EAEQ,YAAY,QAAqB;AACrC,UAAM,WAAY,OAA4B,SAAS;AACvD,QAAI,CAAC,UAAU;AACX;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,eAAe,MAAM;AACvC,UAAM,OAAQ,OAA4B,MAAM,YAAY,KAAK;AACjE,UAAM,QAAS,KAAK,UAAU,SAAS,aAAc,SAAS,QAAQ,IAAI;AAE1E,UAAM,SAAsB;AAAA,MACxB,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,MAAM;AAAA,MACN,UAAU,KAAK;AAAA,MACf,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,KAAK,SAAS;AAAA,MACd;AAAA,MACA,QAAQ,SAAS;AAAA,MACjB,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,uBAAuB,KAAK,GAAG,KAAK,KAAK,IAAI,YAAO,MAAM,MAAM,QAAQ;AAAA,IACxF;AAEA,SAAK,OAAO,IAAI,MAAM;AACtB,iBAAa,UAAU,SAAS,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,UAAU,KAAK,QAAQ,KAAK,GAAG,GAAG,CAAC;AAAA,EAChH;AAAA,EAEQ,SAAS,GAAU;AACvB,UAAM,SAAS,EAAE;AACjB,QAAI,CAAC,QAAQ;AACT;AAAA,IACJ;AAEA,UAAM,OAAO,KAAK,eAAe,MAAM;AACvC,UAAM,MAAM,OAAO,SAAS,YAAY;AAExC,UAAM,SAAsB;AAAA,MACxB,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,MAAM;AAAA,MACN,UAAU,KAAK;AAAA,MACf,KAAK,KAAK;AAAA,MACV,MAAM,KAAK;AAAA,MACX,KAAK,SAAS;AAAA,MACd,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,QAAQ,UAAU;AAClB,YAAM,cAAe,OAA6B;AAClD,aAAO,QAAQ,KAAK,SAAS,SAAS,WAAW,IAAI;AAAA,IACzD,WACS,QAAQ,SAAS;AACtB,YAAM,OAAQ,OAA4B;AAC1C,UAAI,SAAS,cAAc,SAAS,SAAS;AACzC,eAAO,UAAW,OAA4B;AAAA,MAClD;AAAA,IACJ;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,wBAAwB,KAAK,GAAG,KAAK,KAAK,IAAI,GAAG;AAAA,IACjE;AAEA,SAAK,OAAO,IAAI,MAAM;AACtB,iBAAa,UAAU,UAAU,EAAE,KAAK,KAAK,KAAK,MAAM,KAAK,MAAM,OAAO,WAAW,KAAK,QAAQ,KAAK,GAAG,GAAG,CAAC;AAAA,EAClH;AAAA,EAEQ,eAAe,IAAiB;AACpC,UAAM,MAAM,GAAG,SAAS,YAAY,KAAK;AACzC,QAAI,OAAO;AAEX,QAAI,QAAQ,WAAW,QAAQ,YAAY;AACvC,aAAQ,GAAwB,eAAe,GAAG,aAAa,YAAY,KAAK;AAAA,IACpF,OACK;AACD,iBAAW,SAAS,GAAG,YAAY;AAC/B,YAAI,MAAM,aAAa,KAAK,WAAW;AACnC,gBAAM,IAAI,MAAM,aAAa,KAAK;AAClC,cAAI,GAAG;AACH,qBAAS,OAAO,MAAM,MAAM;AAAA,UAChC;AAAA,QACJ;AAAA,MACJ;AACA,UAAI,CAAC,MAAM;AACP,eAAO,GAAG,aAAa,KAAK,EAAE,MAAM,GAAG,GAAG,KAAK;AAAA,MACnD;AAAA,IACJ;AAEA,UAAM,SAAS,SAAS,EAAE;AAC1B,UAAM,YAAY,SAAS,SAAS,IAAI,IAAI;AAE5C,WAAO;AAAA,MACH;AAAA,MACA,MAAM,UAAU,MAAM,GAAG,GAAG;AAAA,MAC5B,UAAU,UAAU,EAAE;AAAA,MACtB,OAAQ,GAAyB,WAAW,aAAa;AAAA,MACzD;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,sBAAsB,IAA8B;AACxD,QAAI,UAA8B;AAClC,WAAO,WAAW,YAAY,KAAK,QAAQ,YAAY,SAAS,MAAM;AAClE,UAAI,iBAAiB,IAAI,QAAQ,SAAS,YAAY,CAAC,GAAG;AACtD,eAAO;AAAA,MACX;AACA,YAAM,OAAO,QAAQ,aAAa,MAAM;AACxC,UAAI,QAAQ,kBAAkB,IAAI,IAAI,GAAG;AACrC,eAAO;AAAA,MACX;AACA,gBAAU,QAAQ;AAAA,IACtB;AACA,WAAO;AAAA,EACX;AACJ;;;ACrOO,IAAM,kBAAN,MAAyC;AAAA,EAS5C,YACY,QACA,QACA,MACA,YACV;AAJU;AACA;AACA;AACA;AAAA,EACT;AAAA,EAJS;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAZJ,WAAoC;AAAA,EACpC,gBAAsD;AAAA,EAEtD,cAA6B,CAAC;AAAA,EAC9B,iBAA2B,CAAC;AAAA,EAC5B,qBAAmC,CAAC;AAAA,EACpC,qBAAmC,CAAC;AAAA,EAS5C,QAAQ;AACJ,SAAK,WAAW,IAAI,iBAAiB,eAAa,KAAK,gBAAgB,SAAS,CAAC;AACjF,SAAK,SAAS,QAAQ,KAAK,MAAM;AAAA,MAC7B,WAAW;AAAA,MACX,SAAS;AAAA,MACT,eAAe;AAAA,MACf,YAAY;AAAA,MACZ,iBAAiB;AAAA,IACrB,CAAC;AAAA,EACL;AAAA,EAEA,OAAO;AACH,SAAK,UAAU,WAAW;AAC1B,SAAK,WAAW;AAChB,QAAI,KAAK,eAAe;AACpB,mBAAa,KAAK,aAAa;AAAA,IACnC;AAAA,EACJ;AAAA,EAEA,eAAe;AACX,QAAI,KAAK,eAAe;AACpB,mBAAa,KAAK,aAAa;AAC/B,WAAK,MAAM;AAAA,IACf;AAAA,EACJ;AAAA,EAEQ,QAAQ;AACZ,SAAK,gBAAgB;AAErB,QACI,KAAK,YAAY,WAAW,KACzB,KAAK,eAAe,WAAW,KAC/B,KAAK,mBAAmB,WAAW,KACnC,KAAK,mBAAmB,WAAW,GACxC;AACE;AAAA,IACJ;AAEA,UAAM,WAA0B;AAAA,MAC5B,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,KAAK,OAAO,SAAS;AAAA,MACrB,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd,cAAc,KAAK;AAAA,MACnB,cAAc,KAAK;AAAA,IACvB;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,2BAA2B,KAAK,YAAY,MAAM,KAAK,KAAK,eAAe,MAAM,SAAS,KAAK,mBAAmB,MAAM,SAAS,KAAK,mBAAmB,MAAM,EAAE;AAAA,IACjL;AAEA,SAAK,OAAO,IAAI,QAAQ;AACxB,SAAK,WAAW;AAEhB,SAAK,cAAc,CAAC;AACpB,SAAK,iBAAiB,CAAC;AACvB,SAAK,qBAAqB,CAAC;AAC3B,SAAK,qBAAqB,CAAC;AAAA,EAC/B;AAAA,EAEQ,gBAAgB;AACpB,QAAI,KAAK,eAAe;AACpB,mBAAa,KAAK,aAAa;AAAA,IACnC;AACA,SAAK,gBAAgB,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,OAAO,gBAAgB;AAAA,EACpF;AAAA,EAEQ,gBAAgB,WAA6B;AACjD,eAAW,KAAK,WAAW;AACvB,iBAAW,QAAQ,EAAE,YAAY;AAC7B,YAAI,KAAK,aAAa,KAAK,cAAc;AACrC;AAAA,QACJ;AACA,cAAM,KAAK;AACX,cAAM,QAAQ,iBAAiB,EAAE;AACjC,YAAI,MAAM,WAAW,GAAG;AACpB;AAAA,QACJ;AAEA,cAAM,WAAW,UAAU,GAAG,aAAc;AAC5C,YAAI,aAAa,MAAM;AACnB;AAAA,QACJ;AAEA,aAAK,YAAY,KAAK,EAAE,UAAU,MAAM,CAAC;AAAA,MAC7C;AAEA,iBAAW,QAAQ,EAAE,cAAc;AAC/B,YAAI,KAAK,aAAa,KAAK,cAAc;AACrC;AAAA,QACJ;AACA,cAAM,KAAK,UAAU,IAAI;AACzB,YAAI,OAAO,MAAM;AACb,eAAK,eAAe,KAAK,EAAE;AAC3B,uBAAa,EAAE;AAAA,QACnB;AAAA,MACJ;AAEA,UAAI,EAAE,SAAS,mBAAmB,EAAE,OAAO,eAAe;AACtD,cAAM,WAAW,UAAU,EAAE,OAAO,aAAa;AACjD,YAAI,aAAa,MAAM;AACnB,gBAAM,OAAO,EAAE,OAAO,aAAa,KAAK,EAAE,MAAM,GAAG,GAAG,KAAK;AAC3D,eAAK,mBAAmB,KAAK,EAAE,IAAI,UAAU,KAAK,CAAC;AAAA,QACvD;AAAA,MACJ;AAEA,UAAI,EAAE,SAAS,gBAAgB,EAAE,eAAe;AAC5C,cAAM,KAAK,UAAU,EAAE,MAAM;AAC7B,YAAI,OAAO,MAAM;AACb,gBAAM,QAAS,EAAE,OAAuB,aAAa,EAAE,aAAa;AACpE,eAAK,mBAAmB,KAAK;AAAA,YACzB;AAAA,YACA,MAAM,EAAE;AAAA,YACR,OAAO,OAAO,MAAM,GAAG,GAAG,KAAK;AAAA,UACnC,CAAC;AAAA,QACL;AAAA,MACJ;AAAA,IACJ;AAEA,QACI,KAAK,YAAY,SAAS,KACvB,KAAK,eAAe,SAAS,KAC7B,KAAK,mBAAmB,SAAS,KACjC,KAAK,mBAAmB,SAAS,GACtC;AACE,WAAK,cAAc;AAAA,IACvB;AAAA,EACJ;AACJ;;;ACjJO,IAAM,kBAAN,MAAyC;AAAA,EAM5C,YACY,QACA,QACA,MACV;AAHU;AACA;AACA;AAAA,EACT;AAAA,EAHS;AAAA,EACA;AAAA,EACA;AAAA,EARJ,UAAU;AAAA,EACV,eAAsD;AAAA,EACtD,kBAAyD;AAAA,EACzD,eAAe;AAAA,EAQvB,QAAQ;AACJ,SAAK,UAAU,SAAS;AACxB,SAAK,eAAe;AAEpB,SAAK,eAAe,YAAY,MAAM,KAAK,QAAQ,GAAG,GAAG;AACzD,SAAK,kBAAkB,YAAY,MAAM,KAAK,WAAW,GAAG,KAAK,OAAO,kBAAkB;AAAA,EAC9F;AAAA,EAEA,OAAO;AACH,QAAI,KAAK,cAAc;AACnB,oBAAc,KAAK,YAAY;AAAA,IACnC;AACA,QAAI,KAAK,iBAAiB;AACtB,oBAAc,KAAK,eAAe;AAAA,IACtC;AAAA,EACJ;AAAA,EAEA,eAAe;AACX,QAAI,KAAK,cAAc;AACnB,WAAK,eAAe;AAAA,IACxB;AAAA,EACJ;AAAA,EAEA,eAAe;AACX,SAAK,eAAe;AAAA,EACxB;AAAA,EAEQ,iBAAiB;AACrB,aAAS;AAET,UAAM,WAA0B;AAAA,MAC5B,OAAO;AAAA,MACP,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,KAAK,SAAS;AAAA,MACd,OAAO,SAAS;AAAA,MAChB,MAAM,cAAc,KAAK,IAAI;AAAA,MAC7B,UAAU,YAAY;AAAA,IAC1B;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,0BAA0B,SAAS,IAAI,KAAK,SAAS,KAAK,MAAM,SAAS;AAAA,IACzF;AAEA,SAAK,OAAO,IAAI,QAAQ;AACxB,SAAK,OAAO,MAAM;AAClB,SAAK,eAAe;AAAA,EACxB;AAAA,EAEQ,UAAU;AACd,QAAI,SAAS,SAAS,KAAK,SAAS;AAChC,YAAM,OAAO,KAAK;AAClB,WAAK,UAAU,SAAS;AACxB,UAAI,MAAM;AACN,YAAI,KAAK,OAAO,OAAO;AACnB,kBAAQ,IAAI,6BAA6B,IAAI,WAAM,SAAS,IAAI,EAAE;AAAA,QACtE;AACA,aAAK,eAAe;AAAA,MACxB;AAAA,IACJ;AAAA,EACJ;AAAA,EAEQ,aAAa;AACjB,QAAI,KAAK,cAAc;AACnB,UAAI,KAAK,OAAO,OAAO;AACnB,gBAAQ,IAAI,kCAAkC;AAAA,MAClD;AACA,WAAK,eAAe;AAAA,IACxB;AAAA,EACJ;AACJ;;;ACtFO,SAAS,YAAY,KAAqB;AAC7C,MAAI,KAAK,aAAa,QAAQ,GAAG;AACjC,MAAI,CAAC,IAAI;AACL,SAAK,WAAW;AAChB,iBAAa,QAAQ,KAAK,EAAE;AAAA,EAChC;AACA,SAAO;AACX;AAEO,SAAS,aAAqB;AACjC,QAAM,KAAK,KAAK,IAAI,EAAE,SAAS,EAAE;AACjC,QAAM,OAAO,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,GAAG,EAAE;AACvD,SAAO,GAAG,EAAE,IAAI,IAAI;AACxB;;;ACXA,IAAM,cAAc;AACpB,IAAM,kBAAkB;AAEjB,IAAM,SAAN,MAAa;AAAA,EAKhB,YACY,UACA,QACA,WACA,WACA,UACR,eACF;AANU;AACA;AACA;AACA;AACA;AAGR,SAAK,QAAQ,YAAY,MAAM,KAAK,MAAM,GAAG,aAAa;AAAA,EAC9D;AAAA,EARY;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EATJ,QAAsB,CAAC;AAAA,EACvB,QAA+C;AAAA,EAC/C,eAA8B,QAAQ,QAAQ;AAAA,EAatD,IAAI,OAAmB;AACnB,SAAK,MAAM,KAAK,KAAK;AAErB,UAAM,kBAAkB,MAAM,UAAU,WACjC,MAAM,WAAW,SAAS;AAEjC,QAAI,mBAAmB,KAAK,MAAM,UAAU,KAAK,WAAW;AACxD,WAAK,MAAM;AAAA,IACf;AAAA,EACJ;AAAA,EAEA,QAAQ;AACJ,SAAK,eAAe,KAAK,aAAa,KAAK,MAAM,KAAK,QAAQ,CAAC;AAAA,EACnE;AAAA,EAEA,YAAY;AACR,QAAI,KAAK,MAAM,WAAW;AACtB;AAEJ,UAAM,SAAS,KAAK,MAAM,OAAO,CAAC;AAClC,UAAM,MAAM,KAAK,SAAS;AAE1B,QAAI,QAAsB,CAAC;AAC3B,QAAI,YAAY;AAEhB,eAAW,SAAS,QAAQ;AACxB,YAAM,YAAY,KAAK,UAAU,KAAK;AACtC,UAAI,YAAY,UAAU,SAAS,mBAAmB,MAAM,SAAS,GAAG;AACpE,aAAK,WAAW,OAAO,GAAG;AAC1B,gBAAQ,CAAC;AACT,oBAAY;AAAA,MAChB;AACA,YAAM,KAAK,KAAK;AAChB,mBAAa,UAAU;AAAA,IAC3B;AAEA,QAAI,MAAM,SAAS,GAAG;AAClB,WAAK,WAAW,OAAO,GAAG;AAAA,IAC9B;AAAA,EACJ;AAAA,EAEA,UAAU;AACN,QAAI,KAAK,OAAO;AACZ,oBAAc,KAAK,KAAK;AAAA,IAC5B;AACA,SAAK,UAAU;AAAA,EACnB;AAAA,EAEA,MAAc,UAAU;AACpB,QAAI,KAAK,MAAM,WAAW;AACtB;AAEJ,UAAM,SAAS,KAAK,MAAM,OAAO,CAAC;AAClC,UAAM,QAAQ,KAAK,WAAW,MAAM;AACpC,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,UAAM,MAAM,KAAK,SAAS;AAE1B,UAAM,UAAU,MAAM,KAAK,KAAK,MAAM,GAAG;AAEzC,QAAI,CAAC,SAAS;AACV,WAAK,MAAM,QAAQ,GAAG,MAAM;AAAA,IAChC;AAAA,EACJ;AAAA,EAEQ,WAAW,QAAsB,KAAa;AAClD,UAAM,QAAQ,KAAK,WAAW,MAAM;AACpC,UAAM,OAAO,KAAK,UAAU,KAAK;AACjC,UAAM,OAAO,IAAI,KAAK,CAAC,IAAI,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAC1D,cAAU,WAAW,KAAK,IAAI;AAAA,EAClC;AAAA,EAEQ,WAAmB;AACvB,WAAO,GAAG,KAAK,QAAQ,QAAQ,mBAAmB,KAAK,MAAM,CAAC;AAAA,EAClE;AAAA,EAEQ,WAAW,QAAkC;AACjD,WAAO;AAAA,MACH,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB;AAAA,MACA,UAAS,oBAAI,KAAK,GAAE,YAAY;AAAA,MAChC,UAAU,SAAS;AAAA,MACnB,YAAY,UAAU;AAAA,MACtB,QAAQ;AAAA,QACJ,OAAO,OAAO;AAAA,QACd,QAAQ,OAAO;AAAA,QACf,gBAAgB,OAAO;AAAA,QACvB,iBAAiB,OAAO;AAAA,MAC5B;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAc,KAAK,MAAc,KAA+B;AAC5D,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AACrD,UAAI;AACA,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAC9B,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM;AAAA,QACV,CAAC;AACD,YAAI,SAAS;AACT,iBAAO;AAAA,MACf,QACM;AAAA,MAAC;AAEP,UAAI,UAAU,aAAa;AACvB,cAAM,IAAI,QAAQ,OAAK,WAAW,IAAI,UAAU,KAAK,GAAG,CAAC;AAAA,MAC7D;AAAA,IACJ;AACA,WAAO;AAAA,EACX;AACJ;;;AX1HA,IAAM,WAAmB;AAAA,EACrB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AAAA,EACX,eAAe;AAAA,EACf,oBAAoB;AAAA,EACpB,aAAa,KAAK,KAAK;AAAA,EACvB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,QAAQ;AACZ;AAEA,IAAM,kBAAkB;AAEjB,IAAM,aAAN,MAAiB;AAAA,EACZ,SAAwB;AAAA,EACxB,WAAsB,CAAC;AAAA,EACvB;AAAA,EACA;AAAA,EACA,YAAkD;AAAA,EAClD,SAAwB;AAAA,EACxB,SAA6B;AAAA,EAC7B,gBAAyC;AAAA,EACzC,sBAAsB;AAAA,EACtB,kBAAuC;AAAA,EACvC,iBAAqC;AAAA,EAE7C,YAAY,SAA2B;AACnC,SAAK,SAAS,EAAE,GAAG,UAAU,GAAG,QAAQ;AACxC,SAAK,OAAO,SAAS,KAAK,gBAAgB,KAAK,OAAO,MAAM;AAC5D,SAAK,WAAW,YAAY,KAAK,OAAO,WAAW;AAEnD,QAAI,KAAK,OAAO,QAAQ;AACpB,WAAK,gBAAgB;AAAA,IACzB,OACK;AACD,WAAK,aAAa,SAAS,IAAI;AAC/B,WAAK,wBAAwB,QAAQ;AAAA,IACzC;AAAA,EACJ;AAAA,EAEA,QAAQ,QAAgB;AACpB,SAAK,SAAS;AACd,SAAK,QAAQ,IAAI,EAAE,OAAO,YAAY,YAAW,oBAAI,KAAK,GAAE,YAAY,GAAG,SAAS,OAAO,CAAC;AAAA,EAChG;AAAA,EAEA,eAAe,SAAS,IAAI;AACxB,SAAK,QAAQ,IAAI,EAAE,OAAO,WAAW,YAAW,oBAAI,KAAK,GAAE,YAAY,GAAG,OAAO,CAAC;AAClF,SAAK,YAAY;AACjB,QAAI,KAAK,eAAe;AACpB,WAAK,cAAc,WAAW;AAC9B,WAAK,gBAAgB;AAAA,IACzB;AACA,SAAK,wBAAwB;AAAA,EACjC;AAAA,EAEQ,gBAAgB,OAAuB;AAC3C,QAAI,CAAC,OAAO;AACR,aAAO;AAAA,IACX;AACA,QAAI,CAAC,gBAAgB,KAAK,KAAK,GAAG;AAC9B,UAAI,KAAK,OAAO,OAAO;AACnB,gBAAQ,KAAK,iDAAiD,KAAK,UAAU,KAAK,CAAC,oCAAoC;AAAA,MAC3H;AACA,aAAO;AAAA,IACX;AACA,WAAO;AAAA,EACX;AAAA,EAEQ,kBAAkB;AACtB,UAAM,UAAU,KAAK,SAAS;AAC9B,QAAI,SAAS;AACT,WAAK,eAAe,OAAO;AAAA,IAC/B;AACA,SAAK,gBAAgB,IAAI,iBAAiB,MAAM,KAAK,mBAAmB,CAAC;AACzE,SAAK,cAAc,QAAQ,SAAS,MAAM,EAAE,WAAW,MAAM,SAAS,KAAK,CAAC;AAE5E,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,2CAA2C,KAAK,OAAO,MAAM,GAAG;AAAA,IAChF;AAAA,EACJ;AAAA,EAEQ,WAA+B;AACnC,WAAO,SAAS,cAA2B,IAAI,KAAK,OAAO,MAAM,GAAG;AAAA,EACxE;AAAA,EAEQ,qBAAqB;AACzB,QAAI,KAAK,qBAAqB;AAC1B;AAAA,IACJ;AACA,SAAK,sBAAsB;AAC3B,mBAAe,MAAM;AACjB,WAAK,sBAAsB;AAC3B,WAAK,kBAAkB;AAAA,IAC3B,CAAC;AAAA,EACL;AAAA,EAEQ,oBAAoB;AACxB,UAAM,UAAU,KAAK,SAAS;AAC9B,QAAI,YAAY,KAAK,QAAQ;AACzB;AAAA,IACJ;AACA,QAAI,KAAK,QAAQ;AACb,WAAK,kBAAkB;AAAA,IAC3B;AACA,QAAI,SAAS;AACT,WAAK,eAAe,OAAO;AAAA,IAC/B;AAAA,EACJ;AAAA,EAEQ,eAAe,MAAmB;AACtC,SAAK,SAAS;AACd,SAAK,wBAAwB,IAAI;AACjC,SAAK,aAAa,IAAI;AACtB,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,sBAAsB,KAAK,OAAO,MAAM,YAAY;AAAA,IACpE;AAAA,EACJ;AAAA,EAEQ,oBAAoB;AACxB,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,sBAAsB,KAAK,OAAO,MAAM,WAAW;AAAA,IACnE;AACA,SAAK,wBAAwB;AAC7B,SAAK,YAAY;AACjB,SAAK,SAAS;AAAA,EAClB;AAAA,EAEQ,aAAa;AACjB,QAAI,CAAC,KAAK,QAAQ;AACd,UAAI,KAAK,OAAO,QAAQ;AACpB,YAAI,CAAC,KAAK,QAAQ;AACd;AAAA,QACJ;AACA,aAAK,aAAa,KAAK,MAAM;AAAA,MACjC,OACK;AACD,aAAK,aAAa,SAAS,IAAI;AAAA,MACnC;AAAA,IACJ;AACA,SAAK,eAAe;AAAA,EACxB;AAAA,EAEQ,iBAAiB;AACrB,QAAI,KAAK;AACL,mBAAa,KAAK,SAAS;AAC/B,SAAK,YAAY,WAAW,MAAM,KAAK,YAAY,GAAG,KAAK,OAAO,WAAW;AAAA,EACjF;AAAA,EAEQ,wBAAwB,QAAqB;AACjD,SAAK,wBAAwB;AAC7B,UAAM,UAAU,MAAM,KAAK,WAAW;AACtC,WAAO,iBAAiB,SAAS,SAAS,IAAI;AAC9C,WAAO,iBAAiB,SAAS,SAAS,IAAI;AAC9C,WAAO,iBAAiB,UAAU,SAAS,IAAI;AAC/C,SAAK,kBAAkB;AACvB,SAAK,iBAAiB;AAAA,EAC1B;AAAA,EAEQ,0BAA0B;AAC9B,QAAI,CAAC,KAAK,mBAAmB,CAAC,KAAK,gBAAgB;AAC/C;AAAA,IACJ;AACA,SAAK,eAAe,oBAAoB,SAAS,KAAK,iBAAiB,IAAI;AAC3E,SAAK,eAAe,oBAAoB,SAAS,KAAK,iBAAiB,IAAI;AAC3E,SAAK,eAAe,oBAAoB,UAAU,KAAK,iBAAiB,IAAI;AAC5E,SAAK,kBAAkB;AACvB,SAAK,iBAAiB;AAAA,EAC1B;AAAA,EAEQ,aAAa,MAAmB;AACpC,UAAM,YAAY,WAAW;AAE7B,SAAK,SAAS,IAAI;AAAA,MACd,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,MACZ,KAAK,OAAO;AAAA,MACZ;AAAA,MACA,KAAK;AAAA,MACL,KAAK,OAAO;AAAA,IAChB;AAEA,UAAM,kBAAkB,IAAI,gBAAgB,KAAK,QAAQ,KAAK,QAAQ,IAAI;AAC1E,UAAM,kBAAkB,IAAI,gBAAgB,KAAK,QAAQ,KAAK,QAAQ,MAAM,MAAM,gBAAgB,aAAa,CAAC;AAChH,UAAM,gBAAgB,IAAI,cAAc,KAAK,QAAQ,KAAK,QAAQ,IAAI;AACtE,UAAM,eAAe,IAAI,aAAa,KAAK,QAAQ,KAAK,MAAM;AAE9D,SAAK,WAAW,CAAC,iBAAiB,iBAAiB,eAAe,YAAY;AAC9E,SAAK,SAAS,QAAQ,OAAK,EAAE,MAAM,CAAC;AAEpC,QAAI,KAAK,QAAQ;AACb,WAAK,OAAO,IAAI,EAAE,OAAO,YAAY,YAAW,oBAAI,KAAK,GAAE,YAAY,GAAG,SAAS,KAAK,OAAO,CAAC;AAAA,IACpG;AAEA,SAAK,eAAe;AAEpB,UAAM,UAAU,MAAM;AAClB,WAAK,SAAS,QAAQ,OAAK,EAAE,eAAe,CAAC;AAC7C,WAAK,QAAQ,UAAU;AAAA,IAC3B;AAEA,aAAS,iBAAiB,oBAAoB,MAAM;AAChD,UAAI,SAAS,oBAAoB;AAC7B,gBAAQ;AAAA,IAChB,CAAC;AACD,WAAO,iBAAiB,YAAY,OAAO;AAE3C,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,iCAAiC,SAAS,EAAE;AAAA,IAC5D;AAAA,EACJ;AAAA,EAEQ,cAAc;AAClB,QAAI,KAAK,WAAW;AAChB,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IACrB;AACA,SAAK,SAAS,QAAQ,OAAK,EAAE,eAAe,CAAC;AAC7C,SAAK,SAAS,QAAQ,OAAK,EAAE,KAAK,CAAC;AACnC,SAAK,WAAW,CAAC;AACjB,QAAI,KAAK,QAAQ;AACb,WAAK,OAAO,QAAQ;AACpB,WAAK,SAAS;AAAA,IAClB;AAEA,QAAI,KAAK,OAAO,OAAO;AACnB,cAAQ,IAAI,8BAA8B;AAAA,IAC9C;AAAA,EACJ;AACJ;","names":[]}
|