@tamsensedev/dataclient 0.1.10 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/dist/index.cjs +139 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.global.js +139 -25
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +139 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -94,6 +94,7 @@ interface Config {
|
|
|
94
94
|
sessionIdKey: string;
|
|
95
95
|
deviceIdKey: string;
|
|
96
96
|
apiKey: string;
|
|
97
|
+
scoped: string;
|
|
97
98
|
}
|
|
98
99
|
interface Tracker {
|
|
99
100
|
start: () => void;
|
|
@@ -123,11 +124,25 @@ declare class DataClient {
|
|
|
123
124
|
private deviceId;
|
|
124
125
|
private idleTimer;
|
|
125
126
|
private userId;
|
|
127
|
+
private rootEl;
|
|
128
|
+
private scopeObserver;
|
|
129
|
+
private scopeCheckScheduled;
|
|
130
|
+
private activityHandler;
|
|
131
|
+
private activityTarget;
|
|
126
132
|
constructor(options?: Partial<Config>);
|
|
127
133
|
setUser(userId: string): void;
|
|
128
134
|
excludeSession(reason?: string): void;
|
|
135
|
+
private normalizeScoped;
|
|
136
|
+
private startScopedMode;
|
|
137
|
+
private findRoot;
|
|
138
|
+
private resolveScope;
|
|
139
|
+
private scheduleScopeCheck;
|
|
140
|
+
private handleScopeChange;
|
|
141
|
+
private activateScope;
|
|
129
142
|
private onActivity;
|
|
130
143
|
private resetIdleTimer;
|
|
144
|
+
private attachActivityListeners;
|
|
145
|
+
private detachActivityListeners;
|
|
131
146
|
private startSession;
|
|
132
147
|
private stopSession;
|
|
133
148
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -94,6 +94,7 @@ interface Config {
|
|
|
94
94
|
sessionIdKey: string;
|
|
95
95
|
deviceIdKey: string;
|
|
96
96
|
apiKey: string;
|
|
97
|
+
scoped: string;
|
|
97
98
|
}
|
|
98
99
|
interface Tracker {
|
|
99
100
|
start: () => void;
|
|
@@ -123,11 +124,25 @@ declare class DataClient {
|
|
|
123
124
|
private deviceId;
|
|
124
125
|
private idleTimer;
|
|
125
126
|
private userId;
|
|
127
|
+
private rootEl;
|
|
128
|
+
private scopeObserver;
|
|
129
|
+
private scopeCheckScheduled;
|
|
130
|
+
private activityHandler;
|
|
131
|
+
private activityTarget;
|
|
126
132
|
constructor(options?: Partial<Config>);
|
|
127
133
|
setUser(userId: string): void;
|
|
128
134
|
excludeSession(reason?: string): void;
|
|
135
|
+
private normalizeScoped;
|
|
136
|
+
private startScopedMode;
|
|
137
|
+
private findRoot;
|
|
138
|
+
private resolveScope;
|
|
139
|
+
private scheduleScopeCheck;
|
|
140
|
+
private handleScopeChange;
|
|
141
|
+
private activateScope;
|
|
129
142
|
private onActivity;
|
|
130
143
|
private resetIdleTimer;
|
|
144
|
+
private attachActivityListeners;
|
|
145
|
+
private detachActivityListeners;
|
|
131
146
|
private startSession;
|
|
132
147
|
private stopSession;
|
|
133
148
|
}
|
package/dist/index.global.js
CHANGED
|
@@ -12646,14 +12646,19 @@ var dataclient = (() => {
|
|
|
12646
12646
|
|
|
12647
12647
|
// src/trackers/rrweb.ts
|
|
12648
12648
|
var RrwebTracker = class {
|
|
12649
|
-
constructor(config, sender) {
|
|
12649
|
+
constructor(config, sender, root2) {
|
|
12650
12650
|
this.config = config;
|
|
12651
12651
|
this.sender = sender;
|
|
12652
|
+
this.root = root2;
|
|
12652
12653
|
}
|
|
12653
12654
|
config;
|
|
12654
12655
|
sender;
|
|
12656
|
+
root;
|
|
12655
12657
|
stopFn = null;
|
|
12656
12658
|
start() {
|
|
12659
|
+
const scoped = this.config.scoped;
|
|
12660
|
+
const rootIsScoped = !!scoped && this.root !== document.body && this.root.hasAttribute(scoped);
|
|
12661
|
+
const blockSelector = rootIsScoped ? `:not([${scoped}]):not([${scoped}] *):not(:has([${scoped}]))` : void 0;
|
|
12657
12662
|
this.stopFn = record({
|
|
12658
12663
|
emit: (event) => {
|
|
12659
12664
|
const rrwebEvent = {
|
|
@@ -12668,6 +12673,7 @@ var dataclient = (() => {
|
|
|
12668
12673
|
maskTextSelector: MASK_SELECTOR,
|
|
12669
12674
|
maskInputOptions: { password: true },
|
|
12670
12675
|
maskTextFn: (text) => "*".repeat(text.length),
|
|
12676
|
+
blockSelector,
|
|
12671
12677
|
sampling: {
|
|
12672
12678
|
mousemove: false,
|
|
12673
12679
|
mouseInteraction: true,
|
|
@@ -12691,26 +12697,33 @@ var dataclient = (() => {
|
|
|
12691
12697
|
|
|
12692
12698
|
// src/trackers/action.ts
|
|
12693
12699
|
var ActionTracker = class {
|
|
12694
|
-
constructor(config, sender) {
|
|
12700
|
+
constructor(config, sender, root2) {
|
|
12695
12701
|
this.config = config;
|
|
12696
12702
|
this.sender = sender;
|
|
12703
|
+
this.root = root2;
|
|
12697
12704
|
}
|
|
12698
12705
|
config;
|
|
12699
12706
|
sender;
|
|
12707
|
+
root;
|
|
12700
12708
|
inputTimers = /* @__PURE__ */ new WeakMap();
|
|
12701
12709
|
pendingInputs = /* @__PURE__ */ new Set();
|
|
12702
12710
|
handleClick = (e) => this.onClick(e);
|
|
12703
12711
|
handleInput = (e) => this.onInput(e);
|
|
12704
12712
|
handleChange = (e) => this.onChange(e);
|
|
12713
|
+
get listenerTarget() {
|
|
12714
|
+
return this.config.scoped ? this.root : document;
|
|
12715
|
+
}
|
|
12705
12716
|
start() {
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12717
|
+
const target = this.listenerTarget;
|
|
12718
|
+
target.addEventListener("click", this.handleClick, true);
|
|
12719
|
+
target.addEventListener("input", this.handleInput, true);
|
|
12720
|
+
target.addEventListener("change", this.handleChange, true);
|
|
12709
12721
|
}
|
|
12710
12722
|
stop() {
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12723
|
+
const target = this.listenerTarget;
|
|
12724
|
+
target.removeEventListener("click", this.handleClick, true);
|
|
12725
|
+
target.removeEventListener("input", this.handleInput, true);
|
|
12726
|
+
target.removeEventListener("change", this.handleChange, true);
|
|
12714
12727
|
}
|
|
12715
12728
|
beforeUnload() {
|
|
12716
12729
|
for (const el of this.pendingInputs) {
|
|
@@ -12864,7 +12877,7 @@ var dataclient = (() => {
|
|
|
12864
12877
|
}
|
|
12865
12878
|
findMeaningfulElement(el) {
|
|
12866
12879
|
let current = el;
|
|
12867
|
-
while (current && current !== document.body) {
|
|
12880
|
+
while (current && current !== this.root && current !== document.body) {
|
|
12868
12881
|
if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {
|
|
12869
12882
|
return current;
|
|
12870
12883
|
}
|
|
@@ -12880,13 +12893,15 @@ var dataclient = (() => {
|
|
|
12880
12893
|
|
|
12881
12894
|
// src/trackers/mutation.ts
|
|
12882
12895
|
var MutationTracker = class {
|
|
12883
|
-
constructor(config, sender, onMutation) {
|
|
12896
|
+
constructor(config, sender, root2, onMutation) {
|
|
12884
12897
|
this.config = config;
|
|
12885
12898
|
this.sender = sender;
|
|
12899
|
+
this.root = root2;
|
|
12886
12900
|
this.onMutation = onMutation;
|
|
12887
12901
|
}
|
|
12888
12902
|
config;
|
|
12889
12903
|
sender;
|
|
12904
|
+
root;
|
|
12890
12905
|
onMutation;
|
|
12891
12906
|
observer = null;
|
|
12892
12907
|
debounceTimer = null;
|
|
@@ -12896,7 +12911,7 @@ var dataclient = (() => {
|
|
|
12896
12911
|
pendingAttrChanges = [];
|
|
12897
12912
|
start() {
|
|
12898
12913
|
this.observer = new MutationObserver((mutations) => this.handleMutations(mutations));
|
|
12899
|
-
this.observer.observe(
|
|
12914
|
+
this.observer.observe(this.root, {
|
|
12900
12915
|
childList: true,
|
|
12901
12916
|
subtree: true,
|
|
12902
12917
|
characterData: true,
|
|
@@ -13001,12 +13016,14 @@ var dataclient = (() => {
|
|
|
13001
13016
|
|
|
13002
13017
|
// src/trackers/snapshot.ts
|
|
13003
13018
|
var SnapshotTracker = class {
|
|
13004
|
-
constructor(config, sender) {
|
|
13019
|
+
constructor(config, sender, root2) {
|
|
13005
13020
|
this.config = config;
|
|
13006
13021
|
this.sender = sender;
|
|
13022
|
+
this.root = root2;
|
|
13007
13023
|
}
|
|
13008
13024
|
config;
|
|
13009
13025
|
sender;
|
|
13026
|
+
root;
|
|
13010
13027
|
lastUrl = "";
|
|
13011
13028
|
urlPollTimer = null;
|
|
13012
13029
|
checkpointTimer = null;
|
|
@@ -13040,7 +13057,7 @@ var dataclient = (() => {
|
|
|
13040
13057
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13041
13058
|
url: location.href,
|
|
13042
13059
|
title: document.title,
|
|
13043
|
-
tree: serializeTree(
|
|
13060
|
+
tree: serializeTree(this.root),
|
|
13044
13061
|
viewport: getViewport()
|
|
13045
13062
|
};
|
|
13046
13063
|
if (this.config.debug) {
|
|
@@ -13213,8 +13230,10 @@ var dataclient = (() => {
|
|
|
13213
13230
|
inputDebounce: 1e3,
|
|
13214
13231
|
sessionIdKey: "sc2_sid",
|
|
13215
13232
|
deviceIdKey: "sc2_did",
|
|
13216
|
-
apiKey: ""
|
|
13233
|
+
apiKey: "",
|
|
13234
|
+
scoped: ""
|
|
13217
13235
|
};
|
|
13236
|
+
var VALID_ATTR_NAME = /^[a-z][\w-]*$/i;
|
|
13218
13237
|
var DataClient = class {
|
|
13219
13238
|
sender = null;
|
|
13220
13239
|
trackers = [];
|
|
@@ -13222,13 +13241,21 @@ var dataclient = (() => {
|
|
|
13222
13241
|
deviceId;
|
|
13223
13242
|
idleTimer = null;
|
|
13224
13243
|
userId = null;
|
|
13244
|
+
rootEl = null;
|
|
13245
|
+
scopeObserver = null;
|
|
13246
|
+
scopeCheckScheduled = false;
|
|
13247
|
+
activityHandler = null;
|
|
13248
|
+
activityTarget = null;
|
|
13225
13249
|
constructor(options) {
|
|
13226
13250
|
this.config = { ...defaults, ...options };
|
|
13251
|
+
this.config.scoped = this.normalizeScoped(this.config.scoped);
|
|
13227
13252
|
this.deviceId = getDeviceId(this.config.deviceIdKey);
|
|
13228
|
-
this.
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13253
|
+
if (this.config.scoped) {
|
|
13254
|
+
this.startScopedMode();
|
|
13255
|
+
} else {
|
|
13256
|
+
this.startSession(document.body);
|
|
13257
|
+
this.attachActivityListeners(document);
|
|
13258
|
+
}
|
|
13232
13259
|
}
|
|
13233
13260
|
setUser(userId) {
|
|
13234
13261
|
this.userId = userId;
|
|
@@ -13237,10 +13264,77 @@ var dataclient = (() => {
|
|
|
13237
13264
|
excludeSession(reason = "") {
|
|
13238
13265
|
this.sender?.add({ event: "exclude", timestamp: (/* @__PURE__ */ new Date()).toISOString(), reason });
|
|
13239
13266
|
this.stopSession();
|
|
13267
|
+
if (this.scopeObserver) {
|
|
13268
|
+
this.scopeObserver.disconnect();
|
|
13269
|
+
this.scopeObserver = null;
|
|
13270
|
+
}
|
|
13271
|
+
this.detachActivityListeners();
|
|
13272
|
+
}
|
|
13273
|
+
normalizeScoped(value) {
|
|
13274
|
+
if (!value) {
|
|
13275
|
+
return "";
|
|
13276
|
+
}
|
|
13277
|
+
if (!VALID_ATTR_NAME.test(value)) {
|
|
13278
|
+
if (this.config.debug) {
|
|
13279
|
+
console.warn(`[dataclient] invalid "scoped" attribute name: ${JSON.stringify(value)}. Falling back to non-scoped mode.`);
|
|
13280
|
+
}
|
|
13281
|
+
return "";
|
|
13282
|
+
}
|
|
13283
|
+
return value;
|
|
13284
|
+
}
|
|
13285
|
+
startScopedMode() {
|
|
13286
|
+
const initial = this.resolveScope();
|
|
13287
|
+
this.activateScope(initial);
|
|
13288
|
+
this.scopeObserver = new MutationObserver(() => this.scheduleScopeCheck());
|
|
13289
|
+
this.scopeObserver.observe(document.body, { childList: true, subtree: true });
|
|
13290
|
+
if (this.config.debug) {
|
|
13291
|
+
console.log(`[dataclient] scoped mode: looking for [${this.config.scoped}], fallback to body`);
|
|
13292
|
+
}
|
|
13293
|
+
}
|
|
13294
|
+
findRoot() {
|
|
13295
|
+
return document.querySelector(`[${this.config.scoped}]`);
|
|
13296
|
+
}
|
|
13297
|
+
resolveScope() {
|
|
13298
|
+
return this.findRoot() ?? document.body;
|
|
13299
|
+
}
|
|
13300
|
+
scheduleScopeCheck() {
|
|
13301
|
+
if (this.scopeCheckScheduled) {
|
|
13302
|
+
return;
|
|
13303
|
+
}
|
|
13304
|
+
this.scopeCheckScheduled = true;
|
|
13305
|
+
queueMicrotask(() => {
|
|
13306
|
+
this.scopeCheckScheduled = false;
|
|
13307
|
+
this.handleScopeChange();
|
|
13308
|
+
});
|
|
13309
|
+
}
|
|
13310
|
+
handleScopeChange() {
|
|
13311
|
+
const current = this.resolveScope();
|
|
13312
|
+
if (current === this.rootEl) {
|
|
13313
|
+
return;
|
|
13314
|
+
}
|
|
13315
|
+
this.detachActivityListeners();
|
|
13316
|
+
this.stopSession();
|
|
13317
|
+
this.activateScope(current);
|
|
13318
|
+
}
|
|
13319
|
+
activateScope(root2) {
|
|
13320
|
+
this.rootEl = root2;
|
|
13321
|
+
this.attachActivityListeners(root2);
|
|
13322
|
+
this.startSession(root2);
|
|
13323
|
+
if (this.config.debug) {
|
|
13324
|
+
const kind = root2 === document.body ? "document.body (fallback)" : `[${this.config.scoped}]`;
|
|
13325
|
+
console.log(`[dataclient] scope: ${kind}`);
|
|
13326
|
+
}
|
|
13240
13327
|
}
|
|
13241
13328
|
onActivity() {
|
|
13242
13329
|
if (!this.sender) {
|
|
13243
|
-
this.
|
|
13330
|
+
if (this.config.scoped) {
|
|
13331
|
+
if (!this.rootEl) {
|
|
13332
|
+
return;
|
|
13333
|
+
}
|
|
13334
|
+
this.startSession(this.rootEl);
|
|
13335
|
+
} else {
|
|
13336
|
+
this.startSession(document.body);
|
|
13337
|
+
}
|
|
13244
13338
|
}
|
|
13245
13339
|
this.resetIdleTimer();
|
|
13246
13340
|
}
|
|
@@ -13249,7 +13343,26 @@ var dataclient = (() => {
|
|
|
13249
13343
|
clearTimeout(this.idleTimer);
|
|
13250
13344
|
this.idleTimer = setTimeout(() => this.stopSession(), this.config.idleTimeout);
|
|
13251
13345
|
}
|
|
13252
|
-
|
|
13346
|
+
attachActivityListeners(target) {
|
|
13347
|
+
this.detachActivityListeners();
|
|
13348
|
+
const handler = () => this.onActivity();
|
|
13349
|
+
target.addEventListener("click", handler, true);
|
|
13350
|
+
target.addEventListener("input", handler, true);
|
|
13351
|
+
target.addEventListener("change", handler, true);
|
|
13352
|
+
this.activityHandler = handler;
|
|
13353
|
+
this.activityTarget = target;
|
|
13354
|
+
}
|
|
13355
|
+
detachActivityListeners() {
|
|
13356
|
+
if (!this.activityHandler || !this.activityTarget) {
|
|
13357
|
+
return;
|
|
13358
|
+
}
|
|
13359
|
+
this.activityTarget.removeEventListener("click", this.activityHandler, true);
|
|
13360
|
+
this.activityTarget.removeEventListener("input", this.activityHandler, true);
|
|
13361
|
+
this.activityTarget.removeEventListener("change", this.activityHandler, true);
|
|
13362
|
+
this.activityHandler = null;
|
|
13363
|
+
this.activityTarget = null;
|
|
13364
|
+
}
|
|
13365
|
+
startSession(root2) {
|
|
13253
13366
|
const sessionId = generateId();
|
|
13254
13367
|
this.sender = new Sender(
|
|
13255
13368
|
this.config.endpoint,
|
|
@@ -13259,10 +13372,10 @@ var dataclient = (() => {
|
|
|
13259
13372
|
this.deviceId,
|
|
13260
13373
|
this.config.flushInterval
|
|
13261
13374
|
);
|
|
13262
|
-
const snapshotTracker = new SnapshotTracker(this.config, this.sender);
|
|
13263
|
-
const mutationTracker = new MutationTracker(this.config, this.sender, () => snapshotTracker.markMutation());
|
|
13264
|
-
const actionTracker = new ActionTracker(this.config, this.sender);
|
|
13265
|
-
const rrwebTracker = new RrwebTracker(this.config, this.sender);
|
|
13375
|
+
const snapshotTracker = new SnapshotTracker(this.config, this.sender, root2);
|
|
13376
|
+
const mutationTracker = new MutationTracker(this.config, this.sender, root2, () => snapshotTracker.markMutation());
|
|
13377
|
+
const actionTracker = new ActionTracker(this.config, this.sender, root2);
|
|
13378
|
+
const rrwebTracker = new RrwebTracker(this.config, this.sender, root2);
|
|
13266
13379
|
this.trackers = [snapshotTracker, mutationTracker, actionTracker, rrwebTracker];
|
|
13267
13380
|
this.trackers.forEach((t) => t.start());
|
|
13268
13381
|
if (this.userId) {
|
|
@@ -13287,6 +13400,7 @@ var dataclient = (() => {
|
|
|
13287
13400
|
clearTimeout(this.idleTimer);
|
|
13288
13401
|
this.idleTimer = null;
|
|
13289
13402
|
}
|
|
13403
|
+
this.trackers.forEach((t) => t.beforeUnload?.());
|
|
13290
13404
|
this.trackers.forEach((t) => t.stop());
|
|
13291
13405
|
this.trackers = [];
|
|
13292
13406
|
if (this.sender) {
|
|
@@ -13294,7 +13408,7 @@ var dataclient = (() => {
|
|
|
13294
13408
|
this.sender = null;
|
|
13295
13409
|
}
|
|
13296
13410
|
if (this.config.debug) {
|
|
13297
|
-
console.log("[dataclient] Session stopped
|
|
13411
|
+
console.log("[dataclient] Session stopped");
|
|
13298
13412
|
}
|
|
13299
13413
|
}
|
|
13300
13414
|
};
|