@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.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
  /**
@@ -7247,18 +7261,21 @@ class SessionReplayEngine {
7247
7261
  * Send batch of events
7248
7262
  */
7249
7263
  sendBatch() {
7250
- if (this.events.length === 0)
7264
+ if (this.events.length === 0) {
7265
+ console.log("[Zaplier] No events to send in batch");
7251
7266
  return;
7267
+ }
7252
7268
  const batch = [...this.events];
7253
7269
  this.events = [];
7270
+ console.log(`[Zaplier] Sending batch with ${batch.length} events for session ${this.sessionId}`);
7254
7271
  const payload = {
7255
7272
  sessionId: this.sessionId,
7256
7273
  events: batch,
7257
7274
  metadata: {
7258
7275
  userAgent: navigator.userAgent,
7259
7276
  timestamp: Date.now(),
7260
- compression: this.config.compressionEnabled
7261
- }
7277
+ compression: this.config.compressionEnabled,
7278
+ },
7262
7279
  };
7263
7280
  // This will be handled by the anti-adblock manager
7264
7281
  this.sendToBackend(payload);
@@ -7276,39 +7293,55 @@ class SessionReplayEngine {
7276
7293
  try {
7277
7294
  // Try WebRTC first if anti-adblock manager is available
7278
7295
  if (this.antiAdblockManager) {
7279
- 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");
7280
7298
  if (result.success) {
7299
+ console.log("[Zaplier] Session replay sent via WebRTC");
7281
7300
  return; // Success via WebRTC
7282
7301
  }
7302
+ console.log("[Zaplier] WebRTC failed, falling back to fetch");
7283
7303
  }
7284
7304
  // Get API endpoint from global config
7285
- const apiEndpoint = window.zaplier_config?.apiEndpoint || 'http://localhost:3001';
7286
- const token = window.zaplier_config?.token || '';
7305
+ const apiEndpoint = window.zaplier_config?.apiEndpoint || "http://localhost:3001";
7306
+ const token = window.zaplier_config?.token || "ws_demo_token";
7307
+ const url = `${apiEndpoint}/replays/record?token=${token}`;
7308
+ console.log(`[Zaplier] Sending replay batch to ${url}`);
7309
+ console.log(`[Zaplier] Payload:`, {
7310
+ sessionId: payload.sessionId,
7311
+ eventCount: payload.events.length,
7312
+ hasMetadata: !!payload.metadata,
7313
+ });
7314
+ const requestBody = {
7315
+ sessionId: payload.sessionId,
7316
+ events: payload.events,
7317
+ metadata: {
7318
+ startUrl: window.location.href,
7319
+ duration: Date.now() - (performance.timeOrigin || Date.now()),
7320
+ funnelSteps: [],
7321
+ hasConversion: false,
7322
+ ...payload.metadata,
7323
+ },
7324
+ };
7287
7325
  // Fallback to standard fetch
7288
- const response = await fetch(`${apiEndpoint}/replays/record?token=${token}`, {
7289
- method: 'POST',
7290
- headers: { 'Content-Type': 'application/json' },
7291
- body: JSON.stringify({
7292
- sessionId: payload.sessionId,
7293
- events: payload.events,
7294
- metadata: {
7295
- startUrl: window.location.href,
7296
- duration: Date.now() - (performance.timeOrigin || Date.now()),
7297
- funnelSteps: [],
7298
- hasConversion: false,
7299
- ...payload.metadata
7300
- }
7301
- })
7326
+ const response = await fetch(url, {
7327
+ method: "POST",
7328
+ headers: { "Content-Type": "application/json" },
7329
+ body: JSON.stringify(requestBody),
7302
7330
  });
7331
+ const responseText = await response.text();
7303
7332
  if (!response.ok) {
7304
- console.warn('[Zaplier] Session replay batch failed to send:', response.status);
7333
+ console.error("[Zaplier] Session replay batch failed:", {
7334
+ status: response.status,
7335
+ statusText: response.statusText,
7336
+ response: responseText,
7337
+ });
7305
7338
  }
7306
7339
  else {
7307
- console.log('[Zaplier] Session replay batch sent successfully');
7340
+ console.log("[Zaplier] Session replay batch sent successfully:", responseText);
7308
7341
  }
7309
7342
  }
7310
7343
  catch (error) {
7311
- console.warn('[Zaplier] Session replay error:', error);
7344
+ console.error("[Zaplier] Session replay error:", error);
7312
7345
  }
7313
7346
  }
7314
7347
  /**
@@ -7426,8 +7459,9 @@ class ZaplierSDK {
7426
7459
  this.sessionId = this.generateSessionId();
7427
7460
  }
7428
7461
  const sessionId = this.sessionId;
7462
+ // When explicitly enabled, use 100% sample rate
7429
7463
  this.replayEngine = new SessionReplayEngine(sessionId, {
7430
- sampleRate: this.config.replaySampling,
7464
+ sampleRate: 1.0, // Force 100% when explicitly enabled
7431
7465
  maskSensitiveFields: this.config.replayMaskInputs,
7432
7466
  compressionEnabled: true,
7433
7467
  });
@@ -7435,10 +7469,23 @@ class ZaplierSDK {
7435
7469
  if (this.antiAdblockManager) {
7436
7470
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7437
7471
  }
7438
- 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
+ }
7439
7479
  }
7440
- if (this.config.debug) {
7441
- 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)");
7442
7489
  }
7443
7490
  },
7444
7491
  disable: () => {
@@ -7561,8 +7608,11 @@ class ZaplierSDK {
7561
7608
  }
7562
7609
  // Initialize Session Replay Engine
7563
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;
7564
7614
  this.replayEngine = new SessionReplayEngine(this.sessionId, {
7565
- sampleRate: this.config.replaySampling,
7615
+ sampleRate: sampleRate,
7566
7616
  maskSensitiveFields: this.config.replayMaskInputs,
7567
7617
  compressionEnabled: true,
7568
7618
  });
@@ -7570,9 +7620,13 @@ class ZaplierSDK {
7570
7620
  if (this.antiAdblockManager) {
7571
7621
  this.replayEngine.setAntiAdblockManager(this.antiAdblockManager);
7572
7622
  }
7573
- this.replayEngine.start();
7623
+ const started = this.replayEngine.start();
7574
7624
  if (this.config.debug) {
7575
- console.log("[Zaplier] Session Replay started");
7625
+ console.log("[Zaplier] Session Replay started", {
7626
+ started,
7627
+ sampleRate,
7628
+ isRecording: this.replayEngine.isRecording(),
7629
+ });
7576
7630
  }
7577
7631
  }
7578
7632
  // Initialize Heatmap Engine