@tamsensedev/dataclient 0.1.1 → 0.1.4

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/dist/index.d.cts CHANGED
@@ -75,7 +75,10 @@ interface ExcludeEvent {
75
75
  interface RrwebEvent {
76
76
  event: 'rrweb';
77
77
  timestamp: string;
78
- rrwebEvent: unknown;
78
+ rrwebEvent: {
79
+ type: number;
80
+ [key: string]: unknown;
81
+ };
79
82
  }
80
83
  type SceneEvent = SnapshotEvent | MutationEvent | ActionEvent | RrwebEvent | IdentifyEvent | ExcludeEvent;
81
84
  interface Config {
@@ -84,6 +87,7 @@ interface Config {
84
87
  batchSize: number;
85
88
  flushInterval: number;
86
89
  checkpointInterval: number;
90
+ idleTimeout: number;
87
91
  mutationDebounce: number;
88
92
  inputDebounce: number;
89
93
  sessionIdKey: string;
@@ -115,10 +119,16 @@ declare class DataClient {
115
119
  private sender;
116
120
  private trackers;
117
121
  private config;
122
+ private deviceId;
123
+ private idleTimer;
124
+ private userId;
118
125
  constructor(options?: Partial<Config>);
119
126
  setUser(userId: string): void;
120
127
  excludeSession(reason?: string): void;
121
- destroy(): void;
128
+ private onActivity;
129
+ private resetIdleTimer;
130
+ private startSession;
131
+ private stopSession;
122
132
  }
123
133
 
124
134
  export { type ActionEvent, type AttrChange, type Config, DataClient, type ExcludeEvent, type IdentifyEvent, type MutationAdd, type MutationEvent, type Rect, type RrwebEvent, type SceneBatch, type SceneEvent, type SerializedNode, type SnapshotEvent, type TextChange, type Tracker, type Viewport };
package/dist/index.d.ts CHANGED
@@ -75,7 +75,10 @@ interface ExcludeEvent {
75
75
  interface RrwebEvent {
76
76
  event: 'rrweb';
77
77
  timestamp: string;
78
- rrwebEvent: unknown;
78
+ rrwebEvent: {
79
+ type: number;
80
+ [key: string]: unknown;
81
+ };
79
82
  }
80
83
  type SceneEvent = SnapshotEvent | MutationEvent | ActionEvent | RrwebEvent | IdentifyEvent | ExcludeEvent;
81
84
  interface Config {
@@ -84,6 +87,7 @@ interface Config {
84
87
  batchSize: number;
85
88
  flushInterval: number;
86
89
  checkpointInterval: number;
90
+ idleTimeout: number;
87
91
  mutationDebounce: number;
88
92
  inputDebounce: number;
89
93
  sessionIdKey: string;
@@ -115,10 +119,16 @@ declare class DataClient {
115
119
  private sender;
116
120
  private trackers;
117
121
  private config;
122
+ private deviceId;
123
+ private idleTimer;
124
+ private userId;
118
125
  constructor(options?: Partial<Config>);
119
126
  setUser(userId: string): void;
120
127
  excludeSession(reason?: string): void;
121
- destroy(): void;
128
+ private onActivity;
129
+ private resetIdleTimer;
130
+ private startSession;
131
+ private stopSession;
122
132
  }
123
133
 
124
134
  export { type ActionEvent, type AttrChange, type Config, DataClient, type ExcludeEvent, type IdentifyEvent, type MutationAdd, type MutationEvent, type Rect, type RrwebEvent, type SceneBatch, type SceneEvent, type SerializedNode, type SnapshotEvent, type TextChange, type Tracker, type Viewport };
@@ -13111,34 +13111,21 @@ var dataclient = (() => {
13111
13111
  deviceId;
13112
13112
  queue = [];
13113
13113
  timer = null;
13114
- isFlushing = false;
13114
+ flushPromise = Promise.resolve();
13115
13115
  add(event) {
13116
13116
  this.queue.push(event);
13117
- if (event.event === "rrweb" || this.queue.length >= this.batchSize) {
13117
+ const isRrwebSnapshot = event.event === "rrweb" && event.rrwebEvent.type === 2;
13118
+ if (isRrwebSnapshot || this.queue.length >= this.batchSize) {
13118
13119
  this.flush();
13119
13120
  }
13120
13121
  }
13121
- async flush() {
13122
- if (this.queue.length === 0 || this.isFlushing) {
13123
- return;
13124
- }
13125
- this.isFlushing = true;
13126
- const events = this.queue.splice(0);
13127
- const batch = this.buildBatch(events);
13128
- const json = JSON.stringify(batch);
13129
- const url = this.buildUrl();
13130
- const success = await this.send(json, url);
13131
- if (!success) {
13132
- this.queue.unshift(...events);
13133
- }
13134
- this.isFlushing = false;
13122
+ flush() {
13123
+ this.flushPromise = this.flushPromise.then(() => this.doFlush());
13135
13124
  }
13136
13125
  flushSync() {
13137
- if (this.queue.length === 0) {
13126
+ if (this.queue.length === 0)
13138
13127
  return;
13139
- }
13140
13128
  const events = this.queue.splice(0);
13141
- if (events.length === 0) return;
13142
13129
  const url = this.buildUrl();
13143
13130
  let chunk = [];
13144
13131
  let chunkSize = 0;
@@ -13162,6 +13149,18 @@ var dataclient = (() => {
13162
13149
  }
13163
13150
  this.flushSync();
13164
13151
  }
13152
+ async doFlush() {
13153
+ if (this.queue.length === 0)
13154
+ return;
13155
+ const events = this.queue.splice(0);
13156
+ const batch = this.buildBatch(events);
13157
+ const json = JSON.stringify(batch);
13158
+ const url = this.buildUrl();
13159
+ const success = await this.send(json, url);
13160
+ if (!success) {
13161
+ this.queue.unshift(...events);
13162
+ }
13163
+ }
13165
13164
  sendBeacon(events, url) {
13166
13165
  const batch = this.buildBatch(events);
13167
13166
  const json = JSON.stringify(batch);
@@ -13193,12 +13192,10 @@ var dataclient = (() => {
13193
13192
  const response = await fetch(url, {
13194
13193
  method: "POST",
13195
13194
  headers: { "Content-Type": "application/json" },
13196
- body: json,
13197
- keepalive: true
13195
+ body: json
13198
13196
  });
13199
- if (response.ok) {
13197
+ if (response.ok)
13200
13198
  return true;
13201
- }
13202
13199
  } catch {
13203
13200
  }
13204
13201
  if (attempt < MAX_RETRIES) {
@@ -13216,6 +13213,7 @@ var dataclient = (() => {
13216
13213
  batchSize: 5,
13217
13214
  flushInterval: 5e3,
13218
13215
  checkpointInterval: 3e4,
13216
+ idleTimeout: 60 * 60 * 1e3,
13219
13217
  mutationDebounce: 200,
13220
13218
  inputDebounce: 1e3,
13221
13219
  sessionIdKey: "sc2_sid",
@@ -13223,19 +13221,47 @@ var dataclient = (() => {
13223
13221
  apiKey: ""
13224
13222
  };
13225
13223
  var DataClient = class {
13226
- sender;
13224
+ sender = null;
13227
13225
  trackers = [];
13228
13226
  config;
13227
+ deviceId;
13228
+ idleTimer = null;
13229
+ userId = null;
13229
13230
  constructor(options) {
13230
13231
  this.config = { ...defaults, ...options };
13232
+ this.deviceId = getDeviceId(this.config.deviceIdKey);
13233
+ this.startSession();
13234
+ document.addEventListener("click", () => this.onActivity(), true);
13235
+ document.addEventListener("input", () => this.onActivity(), true);
13236
+ document.addEventListener("change", () => this.onActivity(), true);
13237
+ }
13238
+ setUser(userId) {
13239
+ this.userId = userId;
13240
+ this.sender?.add({ event: "identify", timestamp: (/* @__PURE__ */ new Date()).toISOString(), user_id: userId });
13241
+ }
13242
+ excludeSession(reason = "") {
13243
+ this.sender?.add({ event: "exclude", timestamp: (/* @__PURE__ */ new Date()).toISOString(), reason });
13244
+ this.stopSession();
13245
+ }
13246
+ onActivity() {
13247
+ if (!this.sender) {
13248
+ this.startSession();
13249
+ }
13250
+ this.resetIdleTimer();
13251
+ }
13252
+ resetIdleTimer() {
13253
+ if (this.idleTimer)
13254
+ clearTimeout(this.idleTimer);
13255
+ this.idleTimer = setTimeout(() => this.stopSession(), this.config.idleTimeout);
13256
+ }
13257
+ startSession() {
13231
13258
  const sessionId = generateId();
13232
- const deviceId = getDeviceId(this.config.deviceIdKey);
13233
13259
  this.sender = new Sender(
13234
13260
  this.config.endpoint,
13235
13261
  this.config.apiKey,
13236
13262
  this.config.batchSize,
13237
13263
  sessionId,
13238
- deviceId,
13264
+ this.deviceId,
13239
13265
  this.config.flushInterval
13240
13266
  );
13241
13267
  const snapshotTracker = new SnapshotTracker(this.config, this.sender);
@@ -13244,26 +13270,37 @@ var dataclient = (() => {
13244
13270
  const rrwebTracker = new RrwebTracker(this.config, this.sender);
13245
13271
  this.trackers = [snapshotTracker, mutationTracker, actionTracker, rrwebTracker];
13246
13272
  this.trackers.forEach((t) => t.start());
13273
+ if (this.userId) {
13274
+ this.sender.add({ event: "identify", timestamp: (/* @__PURE__ */ new Date()).toISOString(), user_id: this.userId });
13275
+ }
13276
+ this.resetIdleTimer();
13247
13277
  const onLeave = () => {
13248
13278
  this.trackers.forEach((t) => t.beforeUnload?.());
13249
- this.sender.flushSync();
13279
+ this.sender?.flushSync();
13250
13280
  };
13251
13281
  document.addEventListener("visibilitychange", () => {
13252
- if (document.visibilityState === "hidden") onLeave();
13282
+ if (document.visibilityState === "hidden")
13283
+ onLeave();
13253
13284
  });
13254
13285
  window.addEventListener("pagehide", onLeave);
13286
+ if (this.config.debug) {
13287
+ console.log(`[dataclient] Session started: ${sessionId}`);
13288
+ }
13255
13289
  }
13256
- setUser(userId) {
13257
- this.sender.add({ event: "identify", timestamp: (/* @__PURE__ */ new Date()).toISOString(), user_id: userId });
13258
- }
13259
- excludeSession(reason = "") {
13260
- this.sender.add({ event: "exclude", timestamp: (/* @__PURE__ */ new Date()).toISOString(), reason });
13261
- this.destroy();
13262
- }
13263
- destroy() {
13290
+ stopSession() {
13291
+ if (this.idleTimer) {
13292
+ clearTimeout(this.idleTimer);
13293
+ this.idleTimer = null;
13294
+ }
13264
13295
  this.trackers.forEach((t) => t.stop());
13265
13296
  this.trackers = [];
13266
- this.sender.destroy();
13297
+ if (this.sender) {
13298
+ this.sender.destroy();
13299
+ this.sender = null;
13300
+ }
13301
+ if (this.config.debug) {
13302
+ console.log("[dataclient] Session stopped (idle timeout)");
13303
+ }
13267
13304
  }
13268
13305
  };
13269
13306
  return __toCommonJS(index_exports);