@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.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
  /**
@@ -7252,7 +7266,7 @@ class SessionReplayEngine {
7252
7266
  */
7253
7267
  sendBatch() {
7254
7268
  if (this.events.length === 0) {
7255
- console.log('[Zaplier] No events to send in batch');
7269
+ console.log("[Zaplier] No events to send in batch");
7256
7270
  return;
7257
7271
  }
7258
7272
  const batch = [...this.events];
@@ -7264,8 +7278,8 @@ class SessionReplayEngine {
7264
7278
  metadata: {
7265
7279
  userAgent: navigator.userAgent,
7266
7280
  timestamp: Date.now(),
7267
- compression: this.config.compressionEnabled
7268
- }
7281
+ compression: this.config.compressionEnabled,
7282
+ },
7269
7283
  };
7270
7284
  // This will be handled by the anti-adblock manager
7271
7285
  this.sendToBackend(payload);
@@ -7283,23 +7297,23 @@ class SessionReplayEngine {
7283
7297
  try {
7284
7298
  // Try WebRTC first if anti-adblock manager is available
7285
7299
  if (this.antiAdblockManager) {
7286
- console.log('[Zaplier] Trying WebRTC transport...');
7287
- 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");
7288
7302
  if (result.success) {
7289
- console.log('[Zaplier] Session replay sent via WebRTC');
7303
+ console.log("[Zaplier] Session replay sent via WebRTC");
7290
7304
  return; // Success via WebRTC
7291
7305
  }
7292
- console.log('[Zaplier] WebRTC failed, falling back to fetch');
7306
+ console.log("[Zaplier] WebRTC failed, falling back to fetch");
7293
7307
  }
7294
7308
  // Get API endpoint from global config
7295
- const apiEndpoint = window.zaplier_config?.apiEndpoint || 'http://localhost:3001';
7296
- const token = window.zaplier_config?.token || 'ws_demo_token';
7309
+ const apiEndpoint = window.zaplier_config?.apiEndpoint || "http://localhost:3001";
7310
+ const token = window.zaplier_config?.token || "ws_demo_token";
7297
7311
  const url = `${apiEndpoint}/replays/record?token=${token}`;
7298
7312
  console.log(`[Zaplier] Sending replay batch to ${url}`);
7299
7313
  console.log(`[Zaplier] Payload:`, {
7300
7314
  sessionId: payload.sessionId,
7301
7315
  eventCount: payload.events.length,
7302
- hasMetadata: !!payload.metadata
7316
+ hasMetadata: !!payload.metadata,
7303
7317
  });
7304
7318
  const requestBody = {
7305
7319
  sessionId: payload.sessionId,
@@ -7309,29 +7323,29 @@ class SessionReplayEngine {
7309
7323
  duration: Date.now() - (performance.timeOrigin || Date.now()),
7310
7324
  funnelSteps: [],
7311
7325
  hasConversion: false,
7312
- ...payload.metadata
7313
- }
7326
+ ...payload.metadata,
7327
+ },
7314
7328
  };
7315
7329
  // Fallback to standard fetch
7316
7330
  const response = await fetch(url, {
7317
- method: 'POST',
7318
- headers: { 'Content-Type': 'application/json' },
7319
- body: JSON.stringify(requestBody)
7331
+ method: "POST",
7332
+ headers: { "Content-Type": "application/json" },
7333
+ body: JSON.stringify(requestBody),
7320
7334
  });
7321
7335
  const responseText = await response.text();
7322
7336
  if (!response.ok) {
7323
- console.error('[Zaplier] Session replay batch failed:', {
7337
+ console.error("[Zaplier] Session replay batch failed:", {
7324
7338
  status: response.status,
7325
7339
  statusText: response.statusText,
7326
- response: responseText
7340
+ response: responseText,
7327
7341
  });
7328
7342
  }
7329
7343
  else {
7330
- console.log('[Zaplier] Session replay batch sent successfully:', responseText);
7344
+ console.log("[Zaplier] Session replay batch sent successfully:", responseText);
7331
7345
  }
7332
7346
  }
7333
7347
  catch (error) {
7334
- console.error('[Zaplier] Session replay error:', error);
7348
+ console.error("[Zaplier] Session replay error:", error);
7335
7349
  }
7336
7350
  }
7337
7351
  /**
@@ -7449,8 +7463,9 @@ class ZaplierSDK {
7449
7463
  this.sessionId = this.generateSessionId();
7450
7464
  }
7451
7465
  const sessionId = this.sessionId;
7466
+ // When explicitly enabled, use 100% sample rate
7452
7467
  this.replayEngine = new SessionReplayEngine(sessionId, {
7453
- sampleRate: this.config.replaySampling,
7468
+ sampleRate: 1.0, // Force 100% when explicitly enabled
7454
7469
  maskSensitiveFields: this.config.replayMaskInputs,
7455
7470
  compressionEnabled: true,
7456
7471
  });
@@ -7458,10 +7473,23 @@ class ZaplierSDK {
7458
7473
  if (this.antiAdblockManager) {
7459
7474
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7460
7475
  }
7461
- 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
+ }
7462
7483
  }
7463
- if (this.config.debug) {
7464
- 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)");
7465
7493
  }
7466
7494
  },
7467
7495
  disable: () => {
@@ -7584,8 +7612,11 @@ class ZaplierSDK {
7584
7612
  }
7585
7613
  // Initialize Session Replay Engine
7586
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;
7587
7618
  this.replayEngine = new SessionReplayEngine(this.sessionId, {
7588
- sampleRate: this.config.replaySampling,
7619
+ sampleRate: sampleRate,
7589
7620
  maskSensitiveFields: this.config.replayMaskInputs,
7590
7621
  compressionEnabled: true,
7591
7622
  });
@@ -7593,9 +7624,13 @@ class ZaplierSDK {
7593
7624
  if (this.antiAdblockManager) {
7594
7625
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7595
7626
  }
7596
- this.replayEngine.start();
7627
+ const started = this.replayEngine.start();
7597
7628
  if (this.config.debug) {
7598
- console.log("[Zaplier] Session Replay started");
7629
+ console.log("[Zaplier] Session Replay started", {
7630
+ started,
7631
+ sampleRate,
7632
+ isRecording: this.replayEngine.isRecording(),
7633
+ });
7599
7634
  }
7600
7635
  }
7601
7636
  // Initialize Heatmap Engine