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