@zaplier/sdk 1.2.5 → 1.2.7

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.cjs CHANGED
@@ -6878,7 +6878,7 @@ class SessionReplayEngine {
6878
6878
  const target = event.target;
6879
6879
  const path = this.getElementPath(target);
6880
6880
  this.addEvent({
6881
- type: 'click',
6881
+ type: "click",
6882
6882
  timestamp: Date.now(),
6883
6883
  data: {
6884
6884
  x: event.clientX,
@@ -6887,9 +6887,9 @@ class SessionReplayEngine {
6887
6887
  button: event.button,
6888
6888
  ctrlKey: event.ctrlKey,
6889
6889
  shiftKey: event.shiftKey,
6890
- altKey: event.altKey
6890
+ altKey: event.altKey,
6891
6891
  },
6892
- sequence: this.sequence++
6892
+ sequence: this.sequence++,
6893
6893
  });
6894
6894
  };
6895
6895
  /**
@@ -6897,13 +6897,13 @@ class SessionReplayEngine {
6897
6897
  */
6898
6898
  this.handleScroll = () => {
6899
6899
  this.addEvent({
6900
- type: 'scroll',
6900
+ type: "scroll",
6901
6901
  timestamp: Date.now(),
6902
6902
  data: {
6903
6903
  x: window.pageXOffset || document.documentElement.scrollLeft,
6904
- y: window.pageYOffset || document.documentElement.scrollTop
6904
+ y: window.pageYOffset || document.documentElement.scrollTop,
6905
6905
  },
6906
- sequence: this.sequence++
6906
+ sequence: this.sequence++,
6907
6907
  });
6908
6908
  };
6909
6909
  /**
@@ -6915,17 +6915,17 @@ class SessionReplayEngine {
6915
6915
  let value = target.value;
6916
6916
  // Mask sensitive fields
6917
6917
  if (this.config.maskSensitiveFields && this.isSensitiveField(target)) {
6918
- value = '*'.repeat(value.length);
6918
+ value = "*".repeat(value.length);
6919
6919
  }
6920
6920
  this.addEvent({
6921
- type: 'input',
6921
+ type: "input",
6922
6922
  timestamp: Date.now(),
6923
6923
  data: {
6924
6924
  element: path,
6925
6925
  value: value,
6926
- inputType: event.inputType
6926
+ inputType: event.inputType,
6927
6927
  },
6928
- sequence: this.sequence++
6928
+ sequence: this.sequence++,
6929
6929
  });
6930
6930
  };
6931
6931
  /**
@@ -6936,17 +6936,17 @@ class SessionReplayEngine {
6936
6936
  const path = this.getElementPath(target);
6937
6937
  let value = target.value;
6938
6938
  if (this.config.maskSensitiveFields && this.isSensitiveField(target)) {
6939
- value = '*'.repeat(value.length);
6939
+ value = "*".repeat(value.length);
6940
6940
  }
6941
6941
  this.addEvent({
6942
- type: 'input',
6942
+ type: "input",
6943
6943
  timestamp: Date.now(),
6944
6944
  data: {
6945
6945
  element: path,
6946
6946
  value: value,
6947
- type: 'change'
6947
+ type: "change",
6948
6948
  },
6949
- sequence: this.sequence++
6949
+ sequence: this.sequence++,
6950
6950
  });
6951
6951
  };
6952
6952
  /**
@@ -6959,13 +6959,13 @@ class SessionReplayEngine {
6959
6959
  }
6960
6960
  this.lastMouseMove = now;
6961
6961
  this.addEvent({
6962
- type: 'mouse',
6962
+ type: "mouse",
6963
6963
  timestamp: now,
6964
6964
  data: {
6965
6965
  x: event.clientX,
6966
- y: event.clientY
6966
+ y: event.clientY,
6967
6967
  },
6968
- sequence: this.sequence++
6968
+ sequence: this.sequence++,
6969
6969
  });
6970
6970
  };
6971
6971
  /**
@@ -6973,13 +6973,13 @@ class SessionReplayEngine {
6973
6973
  */
6974
6974
  this.handleViewportChange = () => {
6975
6975
  this.addEvent({
6976
- type: 'viewport',
6976
+ type: "viewport",
6977
6977
  timestamp: Date.now(),
6978
6978
  data: {
6979
6979
  width: window.innerWidth,
6980
- height: window.innerHeight
6980
+ height: window.innerHeight,
6981
6981
  },
6982
- sequence: this.sequence++
6982
+ sequence: this.sequence++,
6983
6983
  });
6984
6984
  };
6985
6985
  /**
@@ -7001,7 +7001,7 @@ class SessionReplayEngine {
7001
7001
  captureInputs: true,
7002
7002
  captureMouseMoves: false, // Disabled by default for performance
7003
7003
  compressionEnabled: true,
7004
- ...config
7004
+ ...config,
7005
7005
  };
7006
7006
  }
7007
7007
  /**
@@ -7046,18 +7046,18 @@ class SessionReplayEngine {
7046
7046
  * Setup DOM mutation observer
7047
7047
  */
7048
7048
  setupDOMObserver() {
7049
- if (typeof MutationObserver === 'undefined')
7049
+ if (typeof MutationObserver === "undefined")
7050
7050
  return;
7051
7051
  this.observer = new MutationObserver((mutations) => {
7052
7052
  const serializedMutations = mutations
7053
- .map(mutation => this.serializeMutation(mutation))
7053
+ .map((mutation) => this.serializeMutation(mutation))
7054
7054
  .filter(Boolean);
7055
7055
  if (serializedMutations.length > 0) {
7056
7056
  this.addEvent({
7057
- type: 'mutation',
7057
+ type: "mutation",
7058
7058
  timestamp: Date.now(),
7059
7059
  data: { mutations: serializedMutations },
7060
- sequence: this.sequence++
7060
+ sequence: this.sequence++,
7061
7061
  });
7062
7062
  }
7063
7063
  });
@@ -7067,7 +7067,7 @@ class SessionReplayEngine {
7067
7067
  attributes: true,
7068
7068
  attributeOldValue: true,
7069
7069
  characterData: true,
7070
- characterDataOldValue: true
7070
+ characterDataOldValue: true,
7071
7071
  });
7072
7072
  }
7073
7073
  /**
@@ -7075,47 +7075,49 @@ class SessionReplayEngine {
7075
7075
  */
7076
7076
  setupEventListeners() {
7077
7077
  if (this.config.captureClicks) {
7078
- document.addEventListener('click', this.handleClick, true);
7078
+ document.addEventListener("click", this.handleClick, true);
7079
7079
  }
7080
7080
  if (this.config.captureScrolls) {
7081
- window.addEventListener('scroll', this.handleScroll, { passive: true });
7082
- document.addEventListener('scroll', this.handleScroll, { passive: true });
7081
+ window.addEventListener("scroll", this.handleScroll, { passive: true });
7082
+ document.addEventListener("scroll", this.handleScroll, { passive: true });
7083
7083
  }
7084
7084
  if (this.config.captureInputs) {
7085
- document.addEventListener('input', this.handleInput, true);
7086
- document.addEventListener('change', this.handleChange, true);
7085
+ document.addEventListener("input", this.handleInput, true);
7086
+ document.addEventListener("change", this.handleChange, true);
7087
7087
  }
7088
7088
  if (this.config.captureMouseMoves) {
7089
- document.addEventListener('mousemove', this.handleMouseMove, { passive: true });
7089
+ document.addEventListener("mousemove", this.handleMouseMove, {
7090
+ passive: true,
7091
+ });
7090
7092
  }
7091
7093
  // Viewport changes
7092
- window.addEventListener('resize', this.handleViewportChange);
7093
- window.addEventListener('orientationchange', this.handleViewportChange);
7094
+ window.addEventListener("resize", this.handleViewportChange);
7095
+ window.addEventListener("orientationchange", this.handleViewportChange);
7094
7096
  // Navigation
7095
- window.addEventListener('beforeunload', this.handleNavigation);
7096
- window.addEventListener('pagehide', this.handleNavigation);
7097
+ window.addEventListener("beforeunload", this.handleNavigation);
7098
+ window.addEventListener("pagehide", this.handleNavigation);
7097
7099
  }
7098
7100
  /**
7099
7101
  * Remove event listeners
7100
7102
  */
7101
7103
  removeEventListeners() {
7102
- document.removeEventListener('click', this.handleClick, true);
7103
- window.removeEventListener('scroll', this.handleScroll);
7104
- document.removeEventListener('scroll', this.handleScroll);
7105
- document.removeEventListener('input', this.handleInput, true);
7106
- document.removeEventListener('change', this.handleChange, true);
7107
- document.removeEventListener('mousemove', this.handleMouseMove);
7108
- window.removeEventListener('resize', this.handleViewportChange);
7109
- window.removeEventListener('orientationchange', this.handleViewportChange);
7110
- window.removeEventListener('beforeunload', this.handleNavigation);
7111
- window.removeEventListener('pagehide', this.handleNavigation);
7104
+ document.removeEventListener("click", this.handleClick, true);
7105
+ window.removeEventListener("scroll", this.handleScroll);
7106
+ document.removeEventListener("scroll", this.handleScroll);
7107
+ document.removeEventListener("input", this.handleInput, true);
7108
+ document.removeEventListener("change", this.handleChange, true);
7109
+ document.removeEventListener("mousemove", this.handleMouseMove);
7110
+ window.removeEventListener("resize", this.handleViewportChange);
7111
+ window.removeEventListener("orientationchange", this.handleViewportChange);
7112
+ window.removeEventListener("beforeunload", this.handleNavigation);
7113
+ window.removeEventListener("pagehide", this.handleNavigation);
7112
7114
  }
7113
7115
  /**
7114
7116
  * Record initial DOM state
7115
7117
  */
7116
7118
  recordInitialState() {
7117
7119
  this.addEvent({
7118
- type: 'navigation',
7120
+ type: "navigation",
7119
7121
  timestamp: Date.now(),
7120
7122
  data: {
7121
7123
  url: window.location.href,
@@ -7123,14 +7125,14 @@ class SessionReplayEngine {
7123
7125
  title: document.title,
7124
7126
  viewport: {
7125
7127
  width: window.innerWidth,
7126
- height: window.innerHeight
7128
+ height: window.innerHeight,
7127
7129
  },
7128
7130
  screen: {
7129
7131
  width: window.screen?.width || 0,
7130
- height: window.screen?.height || 0
7131
- }
7132
+ height: window.screen?.height || 0,
7133
+ },
7132
7134
  },
7133
- sequence: this.sequence++
7135
+ sequence: this.sequence++,
7134
7136
  });
7135
7137
  }
7136
7138
  /**
@@ -7139,25 +7141,25 @@ class SessionReplayEngine {
7139
7141
  serializeMutation(mutation) {
7140
7142
  const result = {
7141
7143
  type: mutation.type,
7142
- target: this.getElementPath(mutation.target)
7144
+ target: this.getElementPath(mutation.target),
7143
7145
  };
7144
7146
  switch (mutation.type) {
7145
- case 'childList':
7147
+ case "childList":
7146
7148
  result.addedNodes = Array.from(mutation.addedNodes)
7147
- .filter(node => node.nodeType === Node.ELEMENT_NODE)
7148
- .map(node => this.serializeElement(node))
7149
+ .filter((node) => node.nodeType === Node.ELEMENT_NODE)
7150
+ .map((node) => this.serializeElement(node))
7149
7151
  .slice(0, 10); // Limit to prevent huge payloads
7150
7152
  result.removedNodes = Array.from(mutation.removedNodes)
7151
- .filter(node => node.nodeType === Node.ELEMENT_NODE)
7152
- .map(node => this.getElementPath(node))
7153
+ .filter((node) => node.nodeType === Node.ELEMENT_NODE)
7154
+ .map((node) => this.getElementPath(node))
7153
7155
  .slice(0, 10);
7154
7156
  break;
7155
- case 'attributes':
7157
+ case "attributes":
7156
7158
  result.attributeName = mutation.attributeName;
7157
7159
  result.oldValue = mutation.oldValue;
7158
7160
  result.newValue = mutation.target.getAttribute(mutation.attributeName);
7159
7161
  break;
7160
- case 'characterData':
7162
+ case "characterData":
7161
7163
  result.oldValue = mutation.oldValue;
7162
7164
  result.newValue = mutation.target.textContent;
7163
7165
  break;
@@ -7178,7 +7180,7 @@ class SessionReplayEngine {
7178
7180
  return {
7179
7181
  tagName: element.tagName.toLowerCase(),
7180
7182
  attributes,
7181
- textContent: element.textContent?.substring(0, 1000) || '' // Limit text content
7183
+ textContent: element.textContent?.substring(0, 1000) || "", // Limit text content
7182
7184
  };
7183
7185
  }
7184
7186
  /**
@@ -7196,7 +7198,7 @@ class SessionReplayEngine {
7196
7198
  }
7197
7199
  if (current.className) {
7198
7200
  const classes = current.className.toString().split(/\s+/).slice(0, 2);
7199
- selector += `.${classes.join('.')}`;
7201
+ selector += `.${classes.join(".")}`;
7200
7202
  }
7201
7203
  // Add nth-child if no unique identifier
7202
7204
  if (!current.id && !current.className) {
@@ -7207,26 +7209,38 @@ class SessionReplayEngine {
7207
7209
  path.unshift(selector);
7208
7210
  current = current.parentElement;
7209
7211
  }
7210
- return path.join(' > ');
7212
+ return path.join(" > ");
7211
7213
  }
7212
7214
  /**
7213
7215
  * Check if field should be masked
7214
7216
  */
7215
7217
  isSensitiveField(element) {
7216
- const sensitiveTypes = ['password', 'email', 'tel', 'ssn', 'cc'];
7217
- const sensitiveNames = ['password', 'email', 'phone', 'credit', 'card', 'ssn', 'social'];
7218
- const type = element.getAttribute('type')?.toLowerCase();
7219
- const name = element.getAttribute('name')?.toLowerCase();
7218
+ const sensitiveTypes = ["password", "email", "tel", "ssn", "cc"];
7219
+ const sensitiveNames = [
7220
+ "password",
7221
+ "email",
7222
+ "phone",
7223
+ "credit",
7224
+ "card",
7225
+ "ssn",
7226
+ "social",
7227
+ ];
7228
+ const type = element.getAttribute("type")?.toLowerCase();
7229
+ const name = element.getAttribute("name")?.toLowerCase();
7220
7230
  const className = element.className.toLowerCase();
7221
- return sensitiveTypes.includes(type || '') ||
7222
- sensitiveNames.some(term => (name && name.includes(term)) ||
7223
- className.includes(term));
7231
+ return (sensitiveTypes.includes(type || "") ||
7232
+ sensitiveNames.some((term) => (name && name.includes(term)) || className.includes(term)));
7224
7233
  }
7225
7234
  /**
7226
7235
  * Check if attribute should be masked
7227
7236
  */
7228
7237
  isSensitiveAttribute(attrName) {
7229
- const sensitiveAttrs = ['data-token', 'data-key', 'data-secret', 'authorization'];
7238
+ const sensitiveAttrs = [
7239
+ "data-token",
7240
+ "data-key",
7241
+ "data-secret",
7242
+ "authorization",
7243
+ ];
7230
7244
  return sensitiveAttrs.includes(attrName.toLowerCase());
7231
7245
  }
7232
7246
  /**
@@ -7251,18 +7265,21 @@ class SessionReplayEngine {
7251
7265
  * Send batch of events
7252
7266
  */
7253
7267
  sendBatch() {
7254
- if (this.events.length === 0)
7268
+ if (this.events.length === 0) {
7269
+ console.log("[Zaplier] No events to send in batch");
7255
7270
  return;
7271
+ }
7256
7272
  const batch = [...this.events];
7257
7273
  this.events = [];
7274
+ console.log(`[Zaplier] Sending batch with ${batch.length} events for session ${this.sessionId}`);
7258
7275
  const payload = {
7259
7276
  sessionId: this.sessionId,
7260
7277
  events: batch,
7261
7278
  metadata: {
7262
7279
  userAgent: navigator.userAgent,
7263
7280
  timestamp: Date.now(),
7264
- compression: this.config.compressionEnabled
7265
- }
7281
+ compression: this.config.compressionEnabled,
7282
+ },
7266
7283
  };
7267
7284
  // This will be handled by the anti-adblock manager
7268
7285
  this.sendToBackend(payload);
@@ -7280,39 +7297,55 @@ class SessionReplayEngine {
7280
7297
  try {
7281
7298
  // Try WebRTC first if anti-adblock manager is available
7282
7299
  if (this.antiAdblockManager) {
7283
- const result = await this.antiAdblockManager.send(payload, '/replays/record');
7300
+ console.log("[Zaplier] Trying WebRTC transport...");
7301
+ const result = await this.antiAdblockManager.send(payload, "/replays/record");
7284
7302
  if (result.success) {
7303
+ console.log("[Zaplier] Session replay sent via WebRTC");
7285
7304
  return; // Success via WebRTC
7286
7305
  }
7306
+ console.log("[Zaplier] WebRTC failed, falling back to fetch");
7287
7307
  }
7288
7308
  // Get API endpoint from global config
7289
- const apiEndpoint = window.zaplier_config?.apiEndpoint || 'http://localhost:3001';
7290
- const token = window.zaplier_config?.token || '';
7309
+ const apiEndpoint = window.zaplier_config?.apiEndpoint || "http://localhost:3001";
7310
+ const token = window.zaplier_config?.token || "ws_demo_token";
7311
+ const url = `${apiEndpoint}/replays/record?token=${token}`;
7312
+ console.log(`[Zaplier] Sending replay batch to ${url}`);
7313
+ console.log(`[Zaplier] Payload:`, {
7314
+ sessionId: payload.sessionId,
7315
+ eventCount: payload.events.length,
7316
+ hasMetadata: !!payload.metadata,
7317
+ });
7318
+ const requestBody = {
7319
+ sessionId: payload.sessionId,
7320
+ events: payload.events,
7321
+ metadata: {
7322
+ startUrl: window.location.href,
7323
+ duration: Date.now() - (performance.timeOrigin || Date.now()),
7324
+ funnelSteps: [],
7325
+ hasConversion: false,
7326
+ ...payload.metadata,
7327
+ },
7328
+ };
7291
7329
  // Fallback to standard fetch
7292
- const response = await fetch(`${apiEndpoint}/replays/record?token=${token}`, {
7293
- method: 'POST',
7294
- headers: { 'Content-Type': 'application/json' },
7295
- body: JSON.stringify({
7296
- sessionId: payload.sessionId,
7297
- events: payload.events,
7298
- metadata: {
7299
- startUrl: window.location.href,
7300
- duration: Date.now() - (performance.timeOrigin || Date.now()),
7301
- funnelSteps: [],
7302
- hasConversion: false,
7303
- ...payload.metadata
7304
- }
7305
- })
7330
+ const response = await fetch(url, {
7331
+ method: "POST",
7332
+ headers: { "Content-Type": "application/json" },
7333
+ body: JSON.stringify(requestBody),
7306
7334
  });
7335
+ const responseText = await response.text();
7307
7336
  if (!response.ok) {
7308
- console.warn('[Zaplier] Session replay batch failed to send:', response.status);
7337
+ console.error("[Zaplier] Session replay batch failed:", {
7338
+ status: response.status,
7339
+ statusText: response.statusText,
7340
+ response: responseText,
7341
+ });
7309
7342
  }
7310
7343
  else {
7311
- console.log('[Zaplier] Session replay batch sent successfully');
7344
+ console.log("[Zaplier] Session replay batch sent successfully:", responseText);
7312
7345
  }
7313
7346
  }
7314
7347
  catch (error) {
7315
- console.warn('[Zaplier] Session replay error:', error);
7348
+ console.error("[Zaplier] Session replay error:", error);
7316
7349
  }
7317
7350
  }
7318
7351
  /**
@@ -7430,8 +7463,9 @@ class ZaplierSDK {
7430
7463
  this.sessionId = this.generateSessionId();
7431
7464
  }
7432
7465
  const sessionId = this.sessionId;
7466
+ // When explicitly enabled, use 100% sample rate
7433
7467
  this.replayEngine = new SessionReplayEngine(sessionId, {
7434
- sampleRate: this.config.replaySampling,
7468
+ sampleRate: 1.0, // Force 100% when explicitly enabled
7435
7469
  maskSensitiveFields: this.config.replayMaskInputs,
7436
7470
  compressionEnabled: true,
7437
7471
  });
@@ -7439,10 +7473,23 @@ class ZaplierSDK {
7439
7473
  if (this.antiAdblockManager) {
7440
7474
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7441
7475
  }
7442
- this.replayEngine.start();
7476
+ const started = this.replayEngine.start();
7477
+ if (this.config.debug) {
7478
+ console.log("[Zaplier] Replay enabled", {
7479
+ started,
7480
+ isRecording: this.replayEngine.isRecording(),
7481
+ });
7482
+ }
7443
7483
  }
7444
- if (this.config.debug) {
7445
- console.log("[Zaplier] Replay enabled");
7484
+ else if (this.replayEngine) {
7485
+ // If engine already exists, try to start it (might have failed due to sampling before)
7486
+ const started = this.replayEngine.start();
7487
+ if (this.config.debug) {
7488
+ console.log("[Zaplier] Replay engine already exists, attempting start", { started });
7489
+ }
7490
+ }
7491
+ if (this.config.debug && !this.replayEngine) {
7492
+ console.log("[Zaplier] Replay enabled (will start when SDK initializes)");
7446
7493
  }
7447
7494
  },
7448
7495
  disable: () => {
@@ -7565,8 +7612,11 @@ class ZaplierSDK {
7565
7612
  }
7566
7613
  // Initialize Session Replay Engine
7567
7614
  if (this.config.replay) {
7615
+ // When replay is explicitly enabled, use 100% sample rate to ensure recording
7616
+ // The default replaySampling (0.1) is only for automatic/production sampling
7617
+ const sampleRate = this.config.replay === true ? 1.0 : this.config.replaySampling;
7568
7618
  this.replayEngine = new SessionReplayEngine(this.sessionId, {
7569
- sampleRate: this.config.replaySampling,
7619
+ sampleRate: sampleRate,
7570
7620
  maskSensitiveFields: this.config.replayMaskInputs,
7571
7621
  compressionEnabled: true,
7572
7622
  });
@@ -7574,9 +7624,13 @@ class ZaplierSDK {
7574
7624
  if (this.antiAdblockManager) {
7575
7625
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7576
7626
  }
7577
- this.replayEngine.start();
7627
+ const started = this.replayEngine.start();
7578
7628
  if (this.config.debug) {
7579
- console.log("[Zaplier] Session Replay started");
7629
+ console.log("[Zaplier] Session Replay started", {
7630
+ started,
7631
+ sampleRate,
7632
+ isRecording: this.replayEngine.isRecording(),
7633
+ });
7580
7634
  }
7581
7635
  }
7582
7636
  // Initialize Heatmap Engine