@zaplier/sdk 1.2.6 → 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.esm.js CHANGED
@@ -6874,7 +6874,7 @@ class SessionReplayEngine {
6874
6874
  const target = event.target;
6875
6875
  const path = this.getElementPath(target);
6876
6876
  this.addEvent({
6877
- type: 'click',
6877
+ type: "click",
6878
6878
  timestamp: Date.now(),
6879
6879
  data: {
6880
6880
  x: event.clientX,
@@ -6883,9 +6883,9 @@ class SessionReplayEngine {
6883
6883
  button: event.button,
6884
6884
  ctrlKey: event.ctrlKey,
6885
6885
  shiftKey: event.shiftKey,
6886
- altKey: event.altKey
6886
+ altKey: event.altKey,
6887
6887
  },
6888
- sequence: this.sequence++
6888
+ sequence: this.sequence++,
6889
6889
  });
6890
6890
  };
6891
6891
  /**
@@ -6893,13 +6893,13 @@ class SessionReplayEngine {
6893
6893
  */
6894
6894
  this.handleScroll = () => {
6895
6895
  this.addEvent({
6896
- type: 'scroll',
6896
+ type: "scroll",
6897
6897
  timestamp: Date.now(),
6898
6898
  data: {
6899
6899
  x: window.pageXOffset || document.documentElement.scrollLeft,
6900
- y: window.pageYOffset || document.documentElement.scrollTop
6900
+ y: window.pageYOffset || document.documentElement.scrollTop,
6901
6901
  },
6902
- sequence: this.sequence++
6902
+ sequence: this.sequence++,
6903
6903
  });
6904
6904
  };
6905
6905
  /**
@@ -6911,17 +6911,17 @@ class SessionReplayEngine {
6911
6911
  let value = target.value;
6912
6912
  // Mask sensitive fields
6913
6913
  if (this.config.maskSensitiveFields && this.isSensitiveField(target)) {
6914
- value = '*'.repeat(value.length);
6914
+ value = "*".repeat(value.length);
6915
6915
  }
6916
6916
  this.addEvent({
6917
- type: 'input',
6917
+ type: "input",
6918
6918
  timestamp: Date.now(),
6919
6919
  data: {
6920
6920
  element: path,
6921
6921
  value: value,
6922
- inputType: event.inputType
6922
+ inputType: event.inputType,
6923
6923
  },
6924
- sequence: this.sequence++
6924
+ sequence: this.sequence++,
6925
6925
  });
6926
6926
  };
6927
6927
  /**
@@ -6932,17 +6932,17 @@ class SessionReplayEngine {
6932
6932
  const path = this.getElementPath(target);
6933
6933
  let value = target.value;
6934
6934
  if (this.config.maskSensitiveFields && this.isSensitiveField(target)) {
6935
- value = '*'.repeat(value.length);
6935
+ value = "*".repeat(value.length);
6936
6936
  }
6937
6937
  this.addEvent({
6938
- type: 'input',
6938
+ type: "input",
6939
6939
  timestamp: Date.now(),
6940
6940
  data: {
6941
6941
  element: path,
6942
6942
  value: value,
6943
- type: 'change'
6943
+ type: "change",
6944
6944
  },
6945
- sequence: this.sequence++
6945
+ sequence: this.sequence++,
6946
6946
  });
6947
6947
  };
6948
6948
  /**
@@ -6955,13 +6955,13 @@ class SessionReplayEngine {
6955
6955
  }
6956
6956
  this.lastMouseMove = now;
6957
6957
  this.addEvent({
6958
- type: 'mouse',
6958
+ type: "mouse",
6959
6959
  timestamp: now,
6960
6960
  data: {
6961
6961
  x: event.clientX,
6962
- y: event.clientY
6962
+ y: event.clientY,
6963
6963
  },
6964
- sequence: this.sequence++
6964
+ sequence: this.sequence++,
6965
6965
  });
6966
6966
  };
6967
6967
  /**
@@ -6969,13 +6969,13 @@ class SessionReplayEngine {
6969
6969
  */
6970
6970
  this.handleViewportChange = () => {
6971
6971
  this.addEvent({
6972
- type: 'viewport',
6972
+ type: "viewport",
6973
6973
  timestamp: Date.now(),
6974
6974
  data: {
6975
6975
  width: window.innerWidth,
6976
- height: window.innerHeight
6976
+ height: window.innerHeight,
6977
6977
  },
6978
- sequence: this.sequence++
6978
+ sequence: this.sequence++,
6979
6979
  });
6980
6980
  };
6981
6981
  /**
@@ -6997,7 +6997,7 @@ class SessionReplayEngine {
6997
6997
  captureInputs: true,
6998
6998
  captureMouseMoves: false, // Disabled by default for performance
6999
6999
  compressionEnabled: true,
7000
- ...config
7000
+ ...config,
7001
7001
  };
7002
7002
  }
7003
7003
  /**
@@ -7042,18 +7042,18 @@ class SessionReplayEngine {
7042
7042
  * Setup DOM mutation observer
7043
7043
  */
7044
7044
  setupDOMObserver() {
7045
- if (typeof MutationObserver === 'undefined')
7045
+ if (typeof MutationObserver === "undefined")
7046
7046
  return;
7047
7047
  this.observer = new MutationObserver((mutations) => {
7048
7048
  const serializedMutations = mutations
7049
- .map(mutation => this.serializeMutation(mutation))
7049
+ .map((mutation) => this.serializeMutation(mutation))
7050
7050
  .filter(Boolean);
7051
7051
  if (serializedMutations.length > 0) {
7052
7052
  this.addEvent({
7053
- type: 'mutation',
7053
+ type: "mutation",
7054
7054
  timestamp: Date.now(),
7055
7055
  data: { mutations: serializedMutations },
7056
- sequence: this.sequence++
7056
+ sequence: this.sequence++,
7057
7057
  });
7058
7058
  }
7059
7059
  });
@@ -7063,7 +7063,7 @@ class SessionReplayEngine {
7063
7063
  attributes: true,
7064
7064
  attributeOldValue: true,
7065
7065
  characterData: true,
7066
- characterDataOldValue: true
7066
+ characterDataOldValue: true,
7067
7067
  });
7068
7068
  }
7069
7069
  /**
@@ -7071,47 +7071,49 @@ class SessionReplayEngine {
7071
7071
  */
7072
7072
  setupEventListeners() {
7073
7073
  if (this.config.captureClicks) {
7074
- document.addEventListener('click', this.handleClick, true);
7074
+ document.addEventListener("click", this.handleClick, true);
7075
7075
  }
7076
7076
  if (this.config.captureScrolls) {
7077
- window.addEventListener('scroll', this.handleScroll, { passive: true });
7078
- document.addEventListener('scroll', this.handleScroll, { passive: true });
7077
+ window.addEventListener("scroll", this.handleScroll, { passive: true });
7078
+ document.addEventListener("scroll", this.handleScroll, { passive: true });
7079
7079
  }
7080
7080
  if (this.config.captureInputs) {
7081
- document.addEventListener('input', this.handleInput, true);
7082
- document.addEventListener('change', this.handleChange, true);
7081
+ document.addEventListener("input", this.handleInput, true);
7082
+ document.addEventListener("change", this.handleChange, true);
7083
7083
  }
7084
7084
  if (this.config.captureMouseMoves) {
7085
- document.addEventListener('mousemove', this.handleMouseMove, { passive: true });
7085
+ document.addEventListener("mousemove", this.handleMouseMove, {
7086
+ passive: true,
7087
+ });
7086
7088
  }
7087
7089
  // Viewport changes
7088
- window.addEventListener('resize', this.handleViewportChange);
7089
- window.addEventListener('orientationchange', this.handleViewportChange);
7090
+ window.addEventListener("resize", this.handleViewportChange);
7091
+ window.addEventListener("orientationchange", this.handleViewportChange);
7090
7092
  // Navigation
7091
- window.addEventListener('beforeunload', this.handleNavigation);
7092
- window.addEventListener('pagehide', this.handleNavigation);
7093
+ window.addEventListener("beforeunload", this.handleNavigation);
7094
+ window.addEventListener("pagehide", this.handleNavigation);
7093
7095
  }
7094
7096
  /**
7095
7097
  * Remove event listeners
7096
7098
  */
7097
7099
  removeEventListeners() {
7098
- document.removeEventListener('click', this.handleClick, true);
7099
- window.removeEventListener('scroll', this.handleScroll);
7100
- document.removeEventListener('scroll', this.handleScroll);
7101
- document.removeEventListener('input', this.handleInput, true);
7102
- document.removeEventListener('change', this.handleChange, true);
7103
- document.removeEventListener('mousemove', this.handleMouseMove);
7104
- window.removeEventListener('resize', this.handleViewportChange);
7105
- window.removeEventListener('orientationchange', this.handleViewportChange);
7106
- window.removeEventListener('beforeunload', this.handleNavigation);
7107
- window.removeEventListener('pagehide', this.handleNavigation);
7100
+ document.removeEventListener("click", this.handleClick, true);
7101
+ window.removeEventListener("scroll", this.handleScroll);
7102
+ document.removeEventListener("scroll", this.handleScroll);
7103
+ document.removeEventListener("input", this.handleInput, true);
7104
+ document.removeEventListener("change", this.handleChange, true);
7105
+ document.removeEventListener("mousemove", this.handleMouseMove);
7106
+ window.removeEventListener("resize", this.handleViewportChange);
7107
+ window.removeEventListener("orientationchange", this.handleViewportChange);
7108
+ window.removeEventListener("beforeunload", this.handleNavigation);
7109
+ window.removeEventListener("pagehide", this.handleNavigation);
7108
7110
  }
7109
7111
  /**
7110
7112
  * Record initial DOM state
7111
7113
  */
7112
7114
  recordInitialState() {
7113
7115
  this.addEvent({
7114
- type: 'navigation',
7116
+ type: "navigation",
7115
7117
  timestamp: Date.now(),
7116
7118
  data: {
7117
7119
  url: window.location.href,
@@ -7119,14 +7121,14 @@ class SessionReplayEngine {
7119
7121
  title: document.title,
7120
7122
  viewport: {
7121
7123
  width: window.innerWidth,
7122
- height: window.innerHeight
7124
+ height: window.innerHeight,
7123
7125
  },
7124
7126
  screen: {
7125
7127
  width: window.screen?.width || 0,
7126
- height: window.screen?.height || 0
7127
- }
7128
+ height: window.screen?.height || 0,
7129
+ },
7128
7130
  },
7129
- sequence: this.sequence++
7131
+ sequence: this.sequence++,
7130
7132
  });
7131
7133
  }
7132
7134
  /**
@@ -7135,25 +7137,25 @@ class SessionReplayEngine {
7135
7137
  serializeMutation(mutation) {
7136
7138
  const result = {
7137
7139
  type: mutation.type,
7138
- target: this.getElementPath(mutation.target)
7140
+ target: this.getElementPath(mutation.target),
7139
7141
  };
7140
7142
  switch (mutation.type) {
7141
- case 'childList':
7143
+ case "childList":
7142
7144
  result.addedNodes = Array.from(mutation.addedNodes)
7143
- .filter(node => node.nodeType === Node.ELEMENT_NODE)
7144
- .map(node => this.serializeElement(node))
7145
+ .filter((node) => node.nodeType === Node.ELEMENT_NODE)
7146
+ .map((node) => this.serializeElement(node))
7145
7147
  .slice(0, 10); // Limit to prevent huge payloads
7146
7148
  result.removedNodes = Array.from(mutation.removedNodes)
7147
- .filter(node => node.nodeType === Node.ELEMENT_NODE)
7148
- .map(node => this.getElementPath(node))
7149
+ .filter((node) => node.nodeType === Node.ELEMENT_NODE)
7150
+ .map((node) => this.getElementPath(node))
7149
7151
  .slice(0, 10);
7150
7152
  break;
7151
- case 'attributes':
7153
+ case "attributes":
7152
7154
  result.attributeName = mutation.attributeName;
7153
7155
  result.oldValue = mutation.oldValue;
7154
7156
  result.newValue = mutation.target.getAttribute(mutation.attributeName);
7155
7157
  break;
7156
- case 'characterData':
7158
+ case "characterData":
7157
7159
  result.oldValue = mutation.oldValue;
7158
7160
  result.newValue = mutation.target.textContent;
7159
7161
  break;
@@ -7174,7 +7176,7 @@ class SessionReplayEngine {
7174
7176
  return {
7175
7177
  tagName: element.tagName.toLowerCase(),
7176
7178
  attributes,
7177
- textContent: element.textContent?.substring(0, 1000) || '' // Limit text content
7179
+ textContent: element.textContent?.substring(0, 1000) || "", // Limit text content
7178
7180
  };
7179
7181
  }
7180
7182
  /**
@@ -7192,7 +7194,7 @@ class SessionReplayEngine {
7192
7194
  }
7193
7195
  if (current.className) {
7194
7196
  const classes = current.className.toString().split(/\s+/).slice(0, 2);
7195
- selector += `.${classes.join('.')}`;
7197
+ selector += `.${classes.join(".")}`;
7196
7198
  }
7197
7199
  // Add nth-child if no unique identifier
7198
7200
  if (!current.id && !current.className) {
@@ -7203,26 +7205,38 @@ class SessionReplayEngine {
7203
7205
  path.unshift(selector);
7204
7206
  current = current.parentElement;
7205
7207
  }
7206
- return path.join(' > ');
7208
+ return path.join(" > ");
7207
7209
  }
7208
7210
  /**
7209
7211
  * Check if field should be masked
7210
7212
  */
7211
7213
  isSensitiveField(element) {
7212
- const sensitiveTypes = ['password', 'email', 'tel', 'ssn', 'cc'];
7213
- const sensitiveNames = ['password', 'email', 'phone', 'credit', 'card', 'ssn', 'social'];
7214
- const type = element.getAttribute('type')?.toLowerCase();
7215
- const name = element.getAttribute('name')?.toLowerCase();
7214
+ const sensitiveTypes = ["password", "email", "tel", "ssn", "cc"];
7215
+ const sensitiveNames = [
7216
+ "password",
7217
+ "email",
7218
+ "phone",
7219
+ "credit",
7220
+ "card",
7221
+ "ssn",
7222
+ "social",
7223
+ ];
7224
+ const type = element.getAttribute("type")?.toLowerCase();
7225
+ const name = element.getAttribute("name")?.toLowerCase();
7216
7226
  const className = element.className.toLowerCase();
7217
- return sensitiveTypes.includes(type || '') ||
7218
- sensitiveNames.some(term => (name && name.includes(term)) ||
7219
- className.includes(term));
7227
+ return (sensitiveTypes.includes(type || "") ||
7228
+ sensitiveNames.some((term) => (name && name.includes(term)) || className.includes(term)));
7220
7229
  }
7221
7230
  /**
7222
7231
  * Check if attribute should be masked
7223
7232
  */
7224
7233
  isSensitiveAttribute(attrName) {
7225
- const sensitiveAttrs = ['data-token', 'data-key', 'data-secret', 'authorization'];
7234
+ const sensitiveAttrs = [
7235
+ "data-token",
7236
+ "data-key",
7237
+ "data-secret",
7238
+ "authorization",
7239
+ ];
7226
7240
  return sensitiveAttrs.includes(attrName.toLowerCase());
7227
7241
  }
7228
7242
  /**
@@ -7248,7 +7262,7 @@ class SessionReplayEngine {
7248
7262
  */
7249
7263
  sendBatch() {
7250
7264
  if (this.events.length === 0) {
7251
- console.log('[Zaplier] No events to send in batch');
7265
+ console.log("[Zaplier] No events to send in batch");
7252
7266
  return;
7253
7267
  }
7254
7268
  const batch = [...this.events];
@@ -7260,8 +7274,8 @@ class SessionReplayEngine {
7260
7274
  metadata: {
7261
7275
  userAgent: navigator.userAgent,
7262
7276
  timestamp: Date.now(),
7263
- compression: this.config.compressionEnabled
7264
- }
7277
+ compression: this.config.compressionEnabled,
7278
+ },
7265
7279
  };
7266
7280
  // This will be handled by the anti-adblock manager
7267
7281
  this.sendToBackend(payload);
@@ -7279,23 +7293,23 @@ class SessionReplayEngine {
7279
7293
  try {
7280
7294
  // Try WebRTC first if anti-adblock manager is available
7281
7295
  if (this.antiAdblockManager) {
7282
- console.log('[Zaplier] Trying WebRTC transport...');
7283
- const result = await this.antiAdblockManager.send(payload, '/replays/record');
7296
+ console.log("[Zaplier] Trying WebRTC transport...");
7297
+ const result = await this.antiAdblockManager.send(payload, "/replays/record");
7284
7298
  if (result.success) {
7285
- console.log('[Zaplier] Session replay sent via WebRTC');
7299
+ console.log("[Zaplier] Session replay sent via WebRTC");
7286
7300
  return; // Success via WebRTC
7287
7301
  }
7288
- console.log('[Zaplier] WebRTC failed, falling back to fetch');
7302
+ console.log("[Zaplier] WebRTC failed, falling back to fetch");
7289
7303
  }
7290
7304
  // Get API endpoint from global config
7291
- const apiEndpoint = window.zaplier_config?.apiEndpoint || 'http://localhost:3001';
7292
- const token = window.zaplier_config?.token || 'ws_demo_token';
7305
+ const apiEndpoint = window.zaplier_config?.apiEndpoint || "http://localhost:3001";
7306
+ const token = window.zaplier_config?.token || "ws_demo_token";
7293
7307
  const url = `${apiEndpoint}/replays/record?token=${token}`;
7294
7308
  console.log(`[Zaplier] Sending replay batch to ${url}`);
7295
7309
  console.log(`[Zaplier] Payload:`, {
7296
7310
  sessionId: payload.sessionId,
7297
7311
  eventCount: payload.events.length,
7298
- hasMetadata: !!payload.metadata
7312
+ hasMetadata: !!payload.metadata,
7299
7313
  });
7300
7314
  const requestBody = {
7301
7315
  sessionId: payload.sessionId,
@@ -7305,29 +7319,29 @@ class SessionReplayEngine {
7305
7319
  duration: Date.now() - (performance.timeOrigin || Date.now()),
7306
7320
  funnelSteps: [],
7307
7321
  hasConversion: false,
7308
- ...payload.metadata
7309
- }
7322
+ ...payload.metadata,
7323
+ },
7310
7324
  };
7311
7325
  // Fallback to standard fetch
7312
7326
  const response = await fetch(url, {
7313
- method: 'POST',
7314
- headers: { 'Content-Type': 'application/json' },
7315
- body: JSON.stringify(requestBody)
7327
+ method: "POST",
7328
+ headers: { "Content-Type": "application/json" },
7329
+ body: JSON.stringify(requestBody),
7316
7330
  });
7317
7331
  const responseText = await response.text();
7318
7332
  if (!response.ok) {
7319
- console.error('[Zaplier] Session replay batch failed:', {
7333
+ console.error("[Zaplier] Session replay batch failed:", {
7320
7334
  status: response.status,
7321
7335
  statusText: response.statusText,
7322
- response: responseText
7336
+ response: responseText,
7323
7337
  });
7324
7338
  }
7325
7339
  else {
7326
- console.log('[Zaplier] Session replay batch sent successfully:', responseText);
7340
+ console.log("[Zaplier] Session replay batch sent successfully:", responseText);
7327
7341
  }
7328
7342
  }
7329
7343
  catch (error) {
7330
- console.error('[Zaplier] Session replay error:', error);
7344
+ console.error("[Zaplier] Session replay error:", error);
7331
7345
  }
7332
7346
  }
7333
7347
  /**
@@ -7445,8 +7459,9 @@ class ZaplierSDK {
7445
7459
  this.sessionId = this.generateSessionId();
7446
7460
  }
7447
7461
  const sessionId = this.sessionId;
7462
+ // When explicitly enabled, use 100% sample rate
7448
7463
  this.replayEngine = new SessionReplayEngine(sessionId, {
7449
- sampleRate: this.config.replaySampling,
7464
+ sampleRate: 1.0, // Force 100% when explicitly enabled
7450
7465
  maskSensitiveFields: this.config.replayMaskInputs,
7451
7466
  compressionEnabled: true,
7452
7467
  });
@@ -7454,10 +7469,23 @@ class ZaplierSDK {
7454
7469
  if (this.antiAdblockManager) {
7455
7470
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7456
7471
  }
7457
- this.replayEngine.start();
7472
+ const started = this.replayEngine.start();
7473
+ if (this.config.debug) {
7474
+ console.log("[Zaplier] Replay enabled", {
7475
+ started,
7476
+ isRecording: this.replayEngine.isRecording(),
7477
+ });
7478
+ }
7458
7479
  }
7459
- if (this.config.debug) {
7460
- console.log("[Zaplier] Replay enabled");
7480
+ else if (this.replayEngine) {
7481
+ // If engine already exists, try to start it (might have failed due to sampling before)
7482
+ const started = this.replayEngine.start();
7483
+ if (this.config.debug) {
7484
+ console.log("[Zaplier] Replay engine already exists, attempting start", { started });
7485
+ }
7486
+ }
7487
+ if (this.config.debug && !this.replayEngine) {
7488
+ console.log("[Zaplier] Replay enabled (will start when SDK initializes)");
7461
7489
  }
7462
7490
  },
7463
7491
  disable: () => {
@@ -7580,8 +7608,11 @@ class ZaplierSDK {
7580
7608
  }
7581
7609
  // Initialize Session Replay Engine
7582
7610
  if (this.config.replay) {
7611
+ // When replay is explicitly enabled, use 100% sample rate to ensure recording
7612
+ // The default replaySampling (0.1) is only for automatic/production sampling
7613
+ const sampleRate = this.config.replay === true ? 1.0 : this.config.replaySampling;
7583
7614
  this.replayEngine = new SessionReplayEngine(this.sessionId, {
7584
- sampleRate: this.config.replaySampling,
7615
+ sampleRate: sampleRate,
7585
7616
  maskSensitiveFields: this.config.replayMaskInputs,
7586
7617
  compressionEnabled: true,
7587
7618
  });
@@ -7589,9 +7620,13 @@ class ZaplierSDK {
7589
7620
  if (this.antiAdblockManager) {
7590
7621
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7591
7622
  }
7592
- this.replayEngine.start();
7623
+ const started = this.replayEngine.start();
7593
7624
  if (this.config.debug) {
7594
- console.log("[Zaplier] Session Replay started");
7625
+ console.log("[Zaplier] Session Replay started", {
7626
+ started,
7627
+ sampleRate,
7628
+ isRecording: this.replayEngine.isRecording(),
7629
+ });
7595
7630
  }
7596
7631
  }
7597
7632
  // Initialize Heatmap Engine