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