@zaplier/sdk 1.2.3 → 1.2.5

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
@@ -6121,16 +6121,16 @@ function getAvailableComponents() {
6121
6121
  */
6122
6122
  class FetchTransport {
6123
6123
  constructor() {
6124
- this.name = 'fetch';
6125
- this.available = typeof fetch !== 'undefined';
6124
+ this.name = "fetch";
6125
+ this.available = typeof fetch !== "undefined";
6126
6126
  }
6127
6127
  async send(data, endpoint) {
6128
6128
  const start = Date.now();
6129
6129
  try {
6130
6130
  const response = await fetch(endpoint, {
6131
- method: 'POST',
6132
- headers: { 'Content-Type': 'application/json' },
6133
- body: JSON.stringify(data)
6131
+ method: "POST",
6132
+ headers: { "Content-Type": "application/json" },
6133
+ body: JSON.stringify(data),
6134
6134
  });
6135
6135
  if (!response.ok) {
6136
6136
  throw new Error(`HTTP ${response.status}`);
@@ -6138,56 +6138,7 @@ class FetchTransport {
6138
6138
  return {
6139
6139
  success: true,
6140
6140
  method: this.name,
6141
- latency: Date.now() - start
6142
- };
6143
- }
6144
- catch (error) {
6145
- return {
6146
- success: false,
6147
- method: this.name,
6148
6141
  latency: Date.now() - start,
6149
- error: error instanceof Error ? error.message : String(error)
6150
- };
6151
- }
6152
- }
6153
- }
6154
- /**
6155
- * Resource Spoofing Transport (GET requests disguised as assets)
6156
- */
6157
- class ResourceSpoofTransport {
6158
- constructor(baseUrl) {
6159
- this.name = 'resource';
6160
- this.available = typeof fetch !== 'undefined';
6161
- this.baseUrl = baseUrl;
6162
- }
6163
- async send(data, endpoint) {
6164
- const start = Date.now();
6165
- try {
6166
- // Compress and encode data
6167
- const encoded = this.encodeData(data);
6168
- // Try different resource types
6169
- const resourceTypes = [
6170
- { path: '/assets/styles.css', type: 'text/css' },
6171
- { path: '/images/pixel.gif', type: 'image/gif' },
6172
- { path: '/fonts/roboto.woff2', type: 'font/woff2' },
6173
- { path: '/js/analytics.js', type: 'application/javascript' }
6174
- ];
6175
- const randomType = resourceTypes[Math.floor(Math.random() * resourceTypes.length)];
6176
- const url = `${this.baseUrl}${randomType.path}?d=${encoded}&t=${Date.now()}`;
6177
- const response = await fetch(url, {
6178
- method: 'GET',
6179
- headers: {
6180
- 'Accept': randomType.type,
6181
- 'Cache-Control': 'no-cache',
6182
- 'User-Agent': navigator.userAgent
6183
- }
6184
- });
6185
- // Even if status is not OK, adblockers might have blocked it
6186
- // But the data might still reach the server
6187
- return {
6188
- success: true, // Optimistic success
6189
- method: this.name,
6190
- latency: Date.now() - start
6191
6142
  };
6192
6143
  }
6193
6144
  catch (error) {
@@ -6195,31 +6146,18 @@ class ResourceSpoofTransport {
6195
6146
  success: false,
6196
6147
  method: this.name,
6197
6148
  latency: Date.now() - start,
6198
- error: error instanceof Error ? error.message : String(error)
6149
+ error: error instanceof Error ? error.message : String(error),
6199
6150
  };
6200
6151
  }
6201
6152
  }
6202
- encodeData(data) {
6203
- try {
6204
- const json = JSON.stringify(data);
6205
- return btoa(encodeURIComponent(json)).replace(/[+/=]/g, (match) => ({
6206
- '+': '-',
6207
- '/': '_',
6208
- '=': ''
6209
- }[match] || match));
6210
- }
6211
- catch (error) {
6212
- return '';
6213
- }
6214
- }
6215
6153
  }
6216
6154
  /**
6217
6155
  * Elysia WebSocket Transport (Native WebSocket for anti-adblock)
6218
6156
  */
6219
6157
  class ElysiaWebSocketTransport {
6220
6158
  constructor(baseUrl, token) {
6221
- this.name = 'elysia-websocket';
6222
- this.available = typeof WebSocket !== 'undefined';
6159
+ this.name = "elysia-websocket";
6160
+ this.available = typeof WebSocket !== "undefined";
6223
6161
  this.connected = false;
6224
6162
  this.baseUrl = baseUrl;
6225
6163
  this.token = token;
@@ -6228,29 +6166,29 @@ class ElysiaWebSocketTransport {
6228
6166
  const start = Date.now();
6229
6167
  try {
6230
6168
  if (!this.token) {
6231
- throw new Error('Missing token for WebSocket connection');
6169
+ throw new Error("Missing token for WebSocket connection");
6232
6170
  }
6233
6171
  await this.connect();
6234
6172
  if (!this.ws || !this.connected) {
6235
- throw new Error('WebSocket not connected');
6173
+ throw new Error("WebSocket not connected");
6236
6174
  }
6237
6175
  return new Promise((resolve, reject) => {
6238
6176
  const messageHandler = (event) => {
6239
6177
  try {
6240
6178
  const response = JSON.parse(event.data);
6241
- this.ws?.removeEventListener('message', messageHandler);
6179
+ this.ws?.removeEventListener("message", messageHandler);
6242
6180
  resolve({
6243
6181
  success: response.success || false,
6244
6182
  method: this.name,
6245
- latency: Date.now() - start
6183
+ latency: Date.now() - start,
6246
6184
  });
6247
6185
  }
6248
6186
  catch (error) {
6249
- this.ws?.removeEventListener('message', messageHandler);
6250
- reject(new Error('Failed to parse WebSocket response'));
6187
+ this.ws?.removeEventListener("message", messageHandler);
6188
+ reject(new Error("Failed to parse WebSocket response"));
6251
6189
  }
6252
6190
  };
6253
- this.ws?.addEventListener('message', messageHandler);
6191
+ this.ws?.addEventListener("message", messageHandler);
6254
6192
  // Send the complete tracking data payload (includes fingerprintHash, stableCoreHash, etc.)
6255
6193
  this.ws?.send(JSON.stringify({
6256
6194
  // Include all data from the SDK payload
@@ -6258,7 +6196,7 @@ class ElysiaWebSocketTransport {
6258
6196
  // Override/ensure essential fields for WebSocket transport
6259
6197
  eventId: data.eventId || crypto.randomUUID(),
6260
6198
  sessionId: data.sessionId || data.s,
6261
- eventType: data.eventType || data.type || 'websocket_event',
6199
+ eventType: data.eventType || data.type || "websocket_event",
6262
6200
  eventName: data.eventName || data.name,
6263
6201
  url: data.url,
6264
6202
  userAgent: data.userAgent || navigator.userAgent,
@@ -6266,12 +6204,12 @@ class ElysiaWebSocketTransport {
6266
6204
  // Ensure these critical fields are included for identity resolution
6267
6205
  fingerprintHash: data.fingerprintHash,
6268
6206
  stableCoreHash: data.stableCoreHash,
6269
- stableCoreVector: data.stableCoreVector
6207
+ stableCoreVector: data.stableCoreVector,
6270
6208
  }));
6271
6209
  // Timeout after 5 seconds
6272
6210
  setTimeout(() => {
6273
- this.ws?.removeEventListener('message', messageHandler);
6274
- reject(new Error('WebSocket response timeout'));
6211
+ this.ws?.removeEventListener("message", messageHandler);
6212
+ reject(new Error("WebSocket response timeout"));
6275
6213
  }, 5000);
6276
6214
  });
6277
6215
  }
@@ -6280,7 +6218,7 @@ class ElysiaWebSocketTransport {
6280
6218
  success: false,
6281
6219
  method: this.name,
6282
6220
  latency: Date.now() - start,
6283
- error: error instanceof Error ? error.message : String(error)
6221
+ error: error instanceof Error ? error.message : String(error),
6284
6222
  };
6285
6223
  }
6286
6224
  }
@@ -6292,7 +6230,7 @@ class ElysiaWebSocketTransport {
6292
6230
  try {
6293
6231
  // Convert HTTP/HTTPS to WS/WSS
6294
6232
  const url = new URL(this.baseUrl);
6295
- const protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
6233
+ const protocol = url.protocol === "https:" ? "wss:" : "ws:";
6296
6234
  const wsUrl = `${protocol}//${url.host}/ws/track?token=${encodeURIComponent(this.token)}`;
6297
6235
  this.ws = new WebSocket(wsUrl);
6298
6236
  this.ws.onopen = () => {
@@ -6301,7 +6239,7 @@ class ElysiaWebSocketTransport {
6301
6239
  };
6302
6240
  this.ws.onerror = () => {
6303
6241
  this.connected = false;
6304
- reject(new Error('WebSocket connection failed'));
6242
+ reject(new Error("WebSocket connection failed"));
6305
6243
  };
6306
6244
  this.ws.onclose = () => {
6307
6245
  this.connected = false;
@@ -6310,7 +6248,7 @@ class ElysiaWebSocketTransport {
6310
6248
  setTimeout(() => {
6311
6249
  if (!this.connected) {
6312
6250
  this.ws?.close();
6313
- reject(new Error('WebSocket connection timeout'));
6251
+ reject(new Error("WebSocket connection timeout"));
6314
6252
  }
6315
6253
  }, 5000);
6316
6254
  }
@@ -6331,14 +6269,18 @@ class ElysiaWebSocketTransport {
6331
6269
  */
6332
6270
  class WebRTCTransport {
6333
6271
  constructor() {
6334
- this.name = 'webrtc';
6335
- this.available = typeof globalThis.RTCPeerConnection !== 'undefined';
6272
+ this.name = "webrtc";
6273
+ this.available = typeof globalThis.RTCPeerConnection !== "undefined";
6336
6274
  this.connected = false;
6337
6275
  }
6338
6276
  async send(data, _endpoint) {
6339
6277
  const start = Date.now();
6340
6278
  if (!this.available) {
6341
- return { success: false, method: this.name, error: 'WebRTC not available' };
6279
+ return {
6280
+ success: false,
6281
+ method: this.name,
6282
+ error: "WebRTC not available",
6283
+ };
6342
6284
  }
6343
6285
  try {
6344
6286
  await this.setupConnection();
@@ -6347,14 +6289,14 @@ class WebRTCTransport {
6347
6289
  return {
6348
6290
  success: true,
6349
6291
  method: this.name,
6350
- latency: Date.now() - start
6292
+ latency: Date.now() - start,
6351
6293
  };
6352
6294
  }
6353
6295
  else {
6354
6296
  return {
6355
6297
  success: false,
6356
6298
  method: this.name,
6357
- error: 'DataChannel not ready'
6299
+ error: "DataChannel not ready",
6358
6300
  };
6359
6301
  }
6360
6302
  }
@@ -6362,7 +6304,7 @@ class WebRTCTransport {
6362
6304
  return {
6363
6305
  success: false,
6364
6306
  method: this.name,
6365
- error: error instanceof Error ? error.message : String(error)
6307
+ error: error instanceof Error ? error.message : String(error),
6366
6308
  };
6367
6309
  }
6368
6310
  }
@@ -6372,11 +6314,11 @@ class WebRTCTransport {
6372
6314
  return new Promise((resolve, reject) => {
6373
6315
  try {
6374
6316
  this.pc = new globalThis.RTCPeerConnection({
6375
- iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
6317
+ iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
6376
6318
  });
6377
- this.dataChannel = this.pc.createDataChannel('tracking', {
6319
+ this.dataChannel = this.pc.createDataChannel("tracking", {
6378
6320
  ordered: false,
6379
- maxRetransmits: 0
6321
+ maxRetransmits: 0,
6380
6322
  });
6381
6323
  this.dataChannel.onopen = () => {
6382
6324
  this.connected = true;
@@ -6413,43 +6355,46 @@ class AntiAdblockManager {
6413
6355
  totalRequests: 0,
6414
6356
  successfulRequests: 0,
6415
6357
  methodSuccess: new Map(),
6416
- methodFailures: new Map()
6358
+ methodFailures: new Map(),
6417
6359
  };
6418
6360
  this.baseUrl = baseUrl;
6419
6361
  this.token = token;
6420
6362
  this.config = {
6421
6363
  enabled: true,
6422
- methods: ['elysia-websocket', 'fetch', 'resource'], // Elysia WebSocket as primary
6364
+ methods: ["elysia-websocket", "fetch"],
6423
6365
  fallbackDelay: 100,
6424
6366
  maxRetries: 2,
6425
6367
  debug: false,
6426
- ...config
6368
+ ...config,
6427
6369
  };
6428
6370
  this.initializeTransports();
6429
6371
  }
6430
6372
  initializeTransports() {
6431
6373
  const transportMap = {
6432
- 'elysia-websocket': () => new ElysiaWebSocketTransport(this.baseUrl, this.token),
6374
+ "elysia-websocket": () => new ElysiaWebSocketTransport(this.baseUrl, this.token),
6433
6375
  fetch: () => new FetchTransport(),
6434
- resource: () => new ResourceSpoofTransport(this.baseUrl),
6435
- webrtc: () => new WebRTCTransport()
6376
+ webrtc: () => new WebRTCTransport(),
6436
6377
  };
6437
6378
  this.transports = this.config.methods
6438
- .map(method => transportMap[method]?.())
6379
+ .map((method) => transportMap[method]?.())
6439
6380
  .filter((transport) => transport !== undefined && transport.available);
6440
6381
  if (this.config.debug) {
6441
- console.log('[Zaplier] Anti-adblock initialized with transports:', this.transports.map(t => t.name));
6382
+ console.log("[Zaplier] Anti-adblock initialized with transports:", this.transports.map((t) => t.name));
6442
6383
  }
6443
6384
  }
6444
6385
  /**
6445
6386
  * Send data using the best available transport
6446
6387
  */
6447
- async send(data, endpoint = '/tracking/event') {
6388
+ async send(data, endpoint = "/tracking/event") {
6448
6389
  this.stats.totalRequests++;
6449
6390
  if (!this.config.enabled || this.transports.length === 0) {
6450
- return { success: false, method: 'none', error: 'No transports available' };
6391
+ return {
6392
+ success: false,
6393
+ method: "none",
6394
+ error: "No transports available",
6395
+ };
6451
6396
  }
6452
- let lastError = '';
6397
+ let lastError = "";
6453
6398
  // Try each transport in order (sequential, not parallel)
6454
6399
  for (const transport of this.transports) {
6455
6400
  if (this.config.debug) {
@@ -6468,7 +6413,7 @@ class AntiAdblockManager {
6468
6413
  }
6469
6414
  else {
6470
6415
  this.updateMethodStats(transport.name, false);
6471
- lastError = result.error || 'Unknown error';
6416
+ lastError = result.error || "Unknown error";
6472
6417
  if (this.config.debug) {
6473
6418
  console.warn(`[Zaplier] ❌ Transport ${transport.name} failed:`, result.error);
6474
6419
  }
@@ -6483,13 +6428,13 @@ class AntiAdblockManager {
6483
6428
  }
6484
6429
  // Wait before trying next transport
6485
6430
  if (this.config.fallbackDelay > 0) {
6486
- await new Promise(resolve => setTimeout(resolve, this.config.fallbackDelay));
6431
+ await new Promise((resolve) => setTimeout(resolve, this.config.fallbackDelay));
6487
6432
  }
6488
6433
  }
6489
6434
  return {
6490
6435
  success: false,
6491
- method: 'all_failed',
6492
- error: `All transports failed. Last error: ${lastError}`
6436
+ method: "all_failed",
6437
+ error: `All transports failed. Last error: ${lastError}`,
6493
6438
  };
6494
6439
  }
6495
6440
  updateMethodStats(method, success) {
@@ -6523,7 +6468,7 @@ class AntiAdblockManager {
6523
6468
  * Destroy all transports
6524
6469
  */
6525
6470
  destroy() {
6526
- this.transports.forEach(transport => {
6471
+ this.transports.forEach((transport) => {
6527
6472
  if (transport.destroy) {
6528
6473
  transport.destroy();
6529
6474
  }
@@ -6544,7 +6489,7 @@ class AntiAdblockManager {
6544
6489
  results[transport.name] = {
6545
6490
  success: false,
6546
6491
  method: transport.name,
6547
- error: error instanceof Error ? error.message : String(error)
6492
+ error: error instanceof Error ? error.message : String(error),
6548
6493
  };
6549
6494
  }
6550
6495
  }
@@ -7047,10 +6992,10 @@ class SessionReplayEngine {
7047
6992
  this.sessionId = sessionId;
7048
6993
  this.config = {
7049
6994
  enabled: true,
7050
- sampleRate: 0.1,
6995
+ sampleRate: 1.0, // 100% for development/testing
7051
6996
  maskSensitiveFields: true,
7052
6997
  maxEventBuffer: 1000,
7053
- batchInterval: 5000,
6998
+ batchInterval: 2000, // Send every 2 seconds for faster testing
7054
6999
  captureClicks: true,
7055
7000
  captureScrolls: true,
7056
7001
  captureInputs: true,
@@ -7335,19 +7280,35 @@ class SessionReplayEngine {
7335
7280
  try {
7336
7281
  // Try WebRTC first if anti-adblock manager is available
7337
7282
  if (this.antiAdblockManager) {
7338
- const result = await this.antiAdblockManager.send(payload, '/api/replay/record');
7283
+ const result = await this.antiAdblockManager.send(payload, '/replays/record');
7339
7284
  if (result.success) {
7340
7285
  return; // Success via WebRTC
7341
7286
  }
7342
7287
  }
7288
+ // Get API endpoint from global config
7289
+ const apiEndpoint = window.zaplier_config?.apiEndpoint || 'http://localhost:3001';
7290
+ const token = window.zaplier_config?.token || '';
7343
7291
  // Fallback to standard fetch
7344
- const response = await fetch('/api/replay/record', {
7292
+ const response = await fetch(`${apiEndpoint}/replays/record?token=${token}`, {
7345
7293
  method: 'POST',
7346
7294
  headers: { 'Content-Type': 'application/json' },
7347
- body: JSON.stringify(payload)
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
+ })
7348
7306
  });
7349
7307
  if (!response.ok) {
7350
- console.warn('[Zaplier] Session replay batch failed to send');
7308
+ console.warn('[Zaplier] Session replay batch failed to send:', response.status);
7309
+ }
7310
+ else {
7311
+ console.log('[Zaplier] Session replay batch sent successfully');
7351
7312
  }
7352
7313
  }
7353
7314
  catch (error) {
@@ -7680,7 +7641,7 @@ class ZaplierSDK {
7680
7641
  this.antiAdblockManager = new AntiAdblockManager(this.config.apiEndpoint, this.config.token, // Pass token to anti-adblock manager
7681
7642
  {
7682
7643
  enabled: true,
7683
- methods: ["elysia-websocket", "fetch", "resource"], // Elysia WebSocket as primary
7644
+ methods: ["elysia-websocket", "fetch"],
7684
7645
  fallbackDelay: 100,
7685
7646
  maxRetries: 2,
7686
7647
  debug: this.config.debug,