@tamsensedev/dataclient 0.1.9 → 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 +144 -24
- 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 +144 -24
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +144 -24
- 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 scheduleScopeCheck;
|
|
139
|
+
private handleScopeChange;
|
|
140
|
+
private onRootAppeared;
|
|
141
|
+
private onRootDisappeared;
|
|
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 scheduleScopeCheck;
|
|
139
|
+
private handleScopeChange;
|
|
140
|
+
private onRootAppeared;
|
|
141
|
+
private onRootDisappeared;
|
|
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
|
@@ -12654,6 +12654,8 @@ var dataclient = (() => {
|
|
|
12654
12654
|
sender;
|
|
12655
12655
|
stopFn = null;
|
|
12656
12656
|
start() {
|
|
12657
|
+
const scoped = this.config.scoped;
|
|
12658
|
+
const blockSelector = scoped ? `:not([${scoped}]):not([${scoped}] *):not(:has([${scoped}]))` : void 0;
|
|
12657
12659
|
this.stopFn = record({
|
|
12658
12660
|
emit: (event) => {
|
|
12659
12661
|
const rrwebEvent = {
|
|
@@ -12668,6 +12670,7 @@ var dataclient = (() => {
|
|
|
12668
12670
|
maskTextSelector: MASK_SELECTOR,
|
|
12669
12671
|
maskInputOptions: { password: true },
|
|
12670
12672
|
maskTextFn: (text) => "*".repeat(text.length),
|
|
12673
|
+
blockSelector,
|
|
12671
12674
|
sampling: {
|
|
12672
12675
|
mousemove: false,
|
|
12673
12676
|
mouseInteraction: true,
|
|
@@ -12691,26 +12694,33 @@ var dataclient = (() => {
|
|
|
12691
12694
|
|
|
12692
12695
|
// src/trackers/action.ts
|
|
12693
12696
|
var ActionTracker = class {
|
|
12694
|
-
constructor(config, sender) {
|
|
12697
|
+
constructor(config, sender, root2) {
|
|
12695
12698
|
this.config = config;
|
|
12696
12699
|
this.sender = sender;
|
|
12700
|
+
this.root = root2;
|
|
12697
12701
|
}
|
|
12698
12702
|
config;
|
|
12699
12703
|
sender;
|
|
12704
|
+
root;
|
|
12700
12705
|
inputTimers = /* @__PURE__ */ new WeakMap();
|
|
12701
12706
|
pendingInputs = /* @__PURE__ */ new Set();
|
|
12702
12707
|
handleClick = (e) => this.onClick(e);
|
|
12703
12708
|
handleInput = (e) => this.onInput(e);
|
|
12704
12709
|
handleChange = (e) => this.onChange(e);
|
|
12710
|
+
get listenerTarget() {
|
|
12711
|
+
return this.config.scoped ? this.root : document;
|
|
12712
|
+
}
|
|
12705
12713
|
start() {
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12714
|
+
const target = this.listenerTarget;
|
|
12715
|
+
target.addEventListener("click", this.handleClick, true);
|
|
12716
|
+
target.addEventListener("input", this.handleInput, true);
|
|
12717
|
+
target.addEventListener("change", this.handleChange, true);
|
|
12709
12718
|
}
|
|
12710
12719
|
stop() {
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12720
|
+
const target = this.listenerTarget;
|
|
12721
|
+
target.removeEventListener("click", this.handleClick, true);
|
|
12722
|
+
target.removeEventListener("input", this.handleInput, true);
|
|
12723
|
+
target.removeEventListener("change", this.handleChange, true);
|
|
12714
12724
|
}
|
|
12715
12725
|
beforeUnload() {
|
|
12716
12726
|
for (const el of this.pendingInputs) {
|
|
@@ -12864,7 +12874,7 @@ var dataclient = (() => {
|
|
|
12864
12874
|
}
|
|
12865
12875
|
findMeaningfulElement(el) {
|
|
12866
12876
|
let current = el;
|
|
12867
|
-
while (current && current !== document.body) {
|
|
12877
|
+
while (current && current !== this.root && current !== document.body) {
|
|
12868
12878
|
if (INTERACTIVE_TAGS.has(current.tagName?.toLowerCase())) {
|
|
12869
12879
|
return current;
|
|
12870
12880
|
}
|
|
@@ -12880,13 +12890,15 @@ var dataclient = (() => {
|
|
|
12880
12890
|
|
|
12881
12891
|
// src/trackers/mutation.ts
|
|
12882
12892
|
var MutationTracker = class {
|
|
12883
|
-
constructor(config, sender, onMutation) {
|
|
12893
|
+
constructor(config, sender, root2, onMutation) {
|
|
12884
12894
|
this.config = config;
|
|
12885
12895
|
this.sender = sender;
|
|
12896
|
+
this.root = root2;
|
|
12886
12897
|
this.onMutation = onMutation;
|
|
12887
12898
|
}
|
|
12888
12899
|
config;
|
|
12889
12900
|
sender;
|
|
12901
|
+
root;
|
|
12890
12902
|
onMutation;
|
|
12891
12903
|
observer = null;
|
|
12892
12904
|
debounceTimer = null;
|
|
@@ -12896,7 +12908,7 @@ var dataclient = (() => {
|
|
|
12896
12908
|
pendingAttrChanges = [];
|
|
12897
12909
|
start() {
|
|
12898
12910
|
this.observer = new MutationObserver((mutations) => this.handleMutations(mutations));
|
|
12899
|
-
this.observer.observe(
|
|
12911
|
+
this.observer.observe(this.root, {
|
|
12900
12912
|
childList: true,
|
|
12901
12913
|
subtree: true,
|
|
12902
12914
|
characterData: true,
|
|
@@ -13001,12 +13013,14 @@ var dataclient = (() => {
|
|
|
13001
13013
|
|
|
13002
13014
|
// src/trackers/snapshot.ts
|
|
13003
13015
|
var SnapshotTracker = class {
|
|
13004
|
-
constructor(config, sender) {
|
|
13016
|
+
constructor(config, sender, root2) {
|
|
13005
13017
|
this.config = config;
|
|
13006
13018
|
this.sender = sender;
|
|
13019
|
+
this.root = root2;
|
|
13007
13020
|
}
|
|
13008
13021
|
config;
|
|
13009
13022
|
sender;
|
|
13023
|
+
root;
|
|
13010
13024
|
lastUrl = "";
|
|
13011
13025
|
urlPollTimer = null;
|
|
13012
13026
|
checkpointTimer = null;
|
|
@@ -13040,7 +13054,7 @@ var dataclient = (() => {
|
|
|
13040
13054
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13041
13055
|
url: location.href,
|
|
13042
13056
|
title: document.title,
|
|
13043
|
-
tree: serializeTree(
|
|
13057
|
+
tree: serializeTree(this.root),
|
|
13044
13058
|
viewport: getViewport()
|
|
13045
13059
|
};
|
|
13046
13060
|
if (this.config.debug) {
|
|
@@ -13203,7 +13217,7 @@ var dataclient = (() => {
|
|
|
13203
13217
|
|
|
13204
13218
|
// src/index.ts
|
|
13205
13219
|
var defaults = {
|
|
13206
|
-
endpoint: "https://my.tamsense.com/api/
|
|
13220
|
+
endpoint: "https://my.tamsense.com/api/events",
|
|
13207
13221
|
debug: false,
|
|
13208
13222
|
batchSize: 5,
|
|
13209
13223
|
flushInterval: 5e3,
|
|
@@ -13213,8 +13227,10 @@ var dataclient = (() => {
|
|
|
13213
13227
|
inputDebounce: 1e3,
|
|
13214
13228
|
sessionIdKey: "sc2_sid",
|
|
13215
13229
|
deviceIdKey: "sc2_did",
|
|
13216
|
-
apiKey: ""
|
|
13230
|
+
apiKey: "",
|
|
13231
|
+
scoped: ""
|
|
13217
13232
|
};
|
|
13233
|
+
var VALID_ATTR_NAME = /^[a-z][\w-]*$/i;
|
|
13218
13234
|
var DataClient = class {
|
|
13219
13235
|
sender = null;
|
|
13220
13236
|
trackers = [];
|
|
@@ -13222,13 +13238,21 @@ var dataclient = (() => {
|
|
|
13222
13238
|
deviceId;
|
|
13223
13239
|
idleTimer = null;
|
|
13224
13240
|
userId = null;
|
|
13241
|
+
rootEl = null;
|
|
13242
|
+
scopeObserver = null;
|
|
13243
|
+
scopeCheckScheduled = false;
|
|
13244
|
+
activityHandler = null;
|
|
13245
|
+
activityTarget = null;
|
|
13225
13246
|
constructor(options) {
|
|
13226
13247
|
this.config = { ...defaults, ...options };
|
|
13248
|
+
this.config.scoped = this.normalizeScoped(this.config.scoped);
|
|
13227
13249
|
this.deviceId = getDeviceId(this.config.deviceIdKey);
|
|
13228
|
-
this.
|
|
13229
|
-
|
|
13230
|
-
|
|
13231
|
-
|
|
13250
|
+
if (this.config.scoped) {
|
|
13251
|
+
this.startScopedMode();
|
|
13252
|
+
} else {
|
|
13253
|
+
this.startSession(document.body);
|
|
13254
|
+
this.attachActivityListeners(document);
|
|
13255
|
+
}
|
|
13232
13256
|
}
|
|
13233
13257
|
setUser(userId) {
|
|
13234
13258
|
this.userId = userId;
|
|
@@ -13237,10 +13261,86 @@ var dataclient = (() => {
|
|
|
13237
13261
|
excludeSession(reason = "") {
|
|
13238
13262
|
this.sender?.add({ event: "exclude", timestamp: (/* @__PURE__ */ new Date()).toISOString(), reason });
|
|
13239
13263
|
this.stopSession();
|
|
13264
|
+
if (this.scopeObserver) {
|
|
13265
|
+
this.scopeObserver.disconnect();
|
|
13266
|
+
this.scopeObserver = null;
|
|
13267
|
+
}
|
|
13268
|
+
this.detachActivityListeners();
|
|
13269
|
+
}
|
|
13270
|
+
normalizeScoped(value) {
|
|
13271
|
+
if (!value) {
|
|
13272
|
+
return "";
|
|
13273
|
+
}
|
|
13274
|
+
if (!VALID_ATTR_NAME.test(value)) {
|
|
13275
|
+
if (this.config.debug) {
|
|
13276
|
+
console.warn(`[dataclient] invalid "scoped" attribute name: ${JSON.stringify(value)}. Falling back to non-scoped mode.`);
|
|
13277
|
+
}
|
|
13278
|
+
return "";
|
|
13279
|
+
}
|
|
13280
|
+
return value;
|
|
13281
|
+
}
|
|
13282
|
+
startScopedMode() {
|
|
13283
|
+
const initial = this.findRoot();
|
|
13284
|
+
if (initial) {
|
|
13285
|
+
this.onRootAppeared(initial);
|
|
13286
|
+
}
|
|
13287
|
+
this.scopeObserver = new MutationObserver(() => this.scheduleScopeCheck());
|
|
13288
|
+
this.scopeObserver.observe(document.body, { childList: true, subtree: true });
|
|
13289
|
+
if (this.config.debug) {
|
|
13290
|
+
console.log(`[dataclient] scoped mode: watching for [${this.config.scoped}]`);
|
|
13291
|
+
}
|
|
13292
|
+
}
|
|
13293
|
+
findRoot() {
|
|
13294
|
+
return document.querySelector(`[${this.config.scoped}]`);
|
|
13295
|
+
}
|
|
13296
|
+
scheduleScopeCheck() {
|
|
13297
|
+
if (this.scopeCheckScheduled) {
|
|
13298
|
+
return;
|
|
13299
|
+
}
|
|
13300
|
+
this.scopeCheckScheduled = true;
|
|
13301
|
+
queueMicrotask(() => {
|
|
13302
|
+
this.scopeCheckScheduled = false;
|
|
13303
|
+
this.handleScopeChange();
|
|
13304
|
+
});
|
|
13305
|
+
}
|
|
13306
|
+
handleScopeChange() {
|
|
13307
|
+
const current = this.findRoot();
|
|
13308
|
+
if (current === this.rootEl) {
|
|
13309
|
+
return;
|
|
13310
|
+
}
|
|
13311
|
+
if (this.rootEl) {
|
|
13312
|
+
this.onRootDisappeared();
|
|
13313
|
+
}
|
|
13314
|
+
if (current) {
|
|
13315
|
+
this.onRootAppeared(current);
|
|
13316
|
+
}
|
|
13317
|
+
}
|
|
13318
|
+
onRootAppeared(root2) {
|
|
13319
|
+
this.rootEl = root2;
|
|
13320
|
+
this.attachActivityListeners(root2);
|
|
13321
|
+
this.startSession(root2);
|
|
13322
|
+
if (this.config.debug) {
|
|
13323
|
+
console.log(`[dataclient] root [${this.config.scoped}] appeared`);
|
|
13324
|
+
}
|
|
13325
|
+
}
|
|
13326
|
+
onRootDisappeared() {
|
|
13327
|
+
if (this.config.debug) {
|
|
13328
|
+
console.log(`[dataclient] root [${this.config.scoped}] removed`);
|
|
13329
|
+
}
|
|
13330
|
+
this.detachActivityListeners();
|
|
13331
|
+
this.stopSession();
|
|
13332
|
+
this.rootEl = null;
|
|
13240
13333
|
}
|
|
13241
13334
|
onActivity() {
|
|
13242
13335
|
if (!this.sender) {
|
|
13243
|
-
this.
|
|
13336
|
+
if (this.config.scoped) {
|
|
13337
|
+
if (!this.rootEl) {
|
|
13338
|
+
return;
|
|
13339
|
+
}
|
|
13340
|
+
this.startSession(this.rootEl);
|
|
13341
|
+
} else {
|
|
13342
|
+
this.startSession(document.body);
|
|
13343
|
+
}
|
|
13244
13344
|
}
|
|
13245
13345
|
this.resetIdleTimer();
|
|
13246
13346
|
}
|
|
@@ -13249,7 +13349,26 @@ var dataclient = (() => {
|
|
|
13249
13349
|
clearTimeout(this.idleTimer);
|
|
13250
13350
|
this.idleTimer = setTimeout(() => this.stopSession(), this.config.idleTimeout);
|
|
13251
13351
|
}
|
|
13252
|
-
|
|
13352
|
+
attachActivityListeners(target) {
|
|
13353
|
+
this.detachActivityListeners();
|
|
13354
|
+
const handler = () => this.onActivity();
|
|
13355
|
+
target.addEventListener("click", handler, true);
|
|
13356
|
+
target.addEventListener("input", handler, true);
|
|
13357
|
+
target.addEventListener("change", handler, true);
|
|
13358
|
+
this.activityHandler = handler;
|
|
13359
|
+
this.activityTarget = target;
|
|
13360
|
+
}
|
|
13361
|
+
detachActivityListeners() {
|
|
13362
|
+
if (!this.activityHandler || !this.activityTarget) {
|
|
13363
|
+
return;
|
|
13364
|
+
}
|
|
13365
|
+
this.activityTarget.removeEventListener("click", this.activityHandler, true);
|
|
13366
|
+
this.activityTarget.removeEventListener("input", this.activityHandler, true);
|
|
13367
|
+
this.activityTarget.removeEventListener("change", this.activityHandler, true);
|
|
13368
|
+
this.activityHandler = null;
|
|
13369
|
+
this.activityTarget = null;
|
|
13370
|
+
}
|
|
13371
|
+
startSession(root2) {
|
|
13253
13372
|
const sessionId = generateId();
|
|
13254
13373
|
this.sender = new Sender(
|
|
13255
13374
|
this.config.endpoint,
|
|
@@ -13259,9 +13378,9 @@ var dataclient = (() => {
|
|
|
13259
13378
|
this.deviceId,
|
|
13260
13379
|
this.config.flushInterval
|
|
13261
13380
|
);
|
|
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);
|
|
13381
|
+
const snapshotTracker = new SnapshotTracker(this.config, this.sender, root2);
|
|
13382
|
+
const mutationTracker = new MutationTracker(this.config, this.sender, root2, () => snapshotTracker.markMutation());
|
|
13383
|
+
const actionTracker = new ActionTracker(this.config, this.sender, root2);
|
|
13265
13384
|
const rrwebTracker = new RrwebTracker(this.config, this.sender);
|
|
13266
13385
|
this.trackers = [snapshotTracker, mutationTracker, actionTracker, rrwebTracker];
|
|
13267
13386
|
this.trackers.forEach((t) => t.start());
|
|
@@ -13287,6 +13406,7 @@ var dataclient = (() => {
|
|
|
13287
13406
|
clearTimeout(this.idleTimer);
|
|
13288
13407
|
this.idleTimer = null;
|
|
13289
13408
|
}
|
|
13409
|
+
this.trackers.forEach((t) => t.beforeUnload?.());
|
|
13290
13410
|
this.trackers.forEach((t) => t.stop());
|
|
13291
13411
|
this.trackers = [];
|
|
13292
13412
|
if (this.sender) {
|
|
@@ -13294,7 +13414,7 @@ var dataclient = (() => {
|
|
|
13294
13414
|
this.sender = null;
|
|
13295
13415
|
}
|
|
13296
13416
|
if (this.config.debug) {
|
|
13297
|
-
console.log("[dataclient] Session stopped
|
|
13417
|
+
console.log("[dataclient] Session stopped");
|
|
13298
13418
|
}
|
|
13299
13419
|
}
|
|
13300
13420
|
};
|