exguard-backend 1.0.32 → 1.0.34

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/README.md CHANGED
@@ -355,11 +355,28 @@ EXGUARD_REALTIME_ENABLED=false
355
355
  EXGUARD_API_URL=https://api.your-domain.com
356
356
  EXGUARD_CACHE_ENABLED=true
357
357
  EXGUARD_CACHE_TTL=300000
358
- EXGUARD_REALTIME_ENABLED=true
359
- EXGUARD_REALTIME_URL=wss://api.your-domain.com/realtime
360
- EXGUARD_SERVICE_TOKEN=${SERVICE_JWT_TOKEN}
358
+ # Optional: Enable realtime for automatic cache invalidation
359
+ # EXGUARD_REALTIME_ENABLED=true
360
+ # EXGUARD_REALTIME_URL=wss://api.your-domain.com/realtime
361
+ # EXGUARD_SERVICE_TOKEN=your-service-token
361
362
  ```
362
363
 
364
+ ### ⚠️ Troubleshooting
365
+
366
+ **502 WebSocket Error:**
367
+ - This means the realtime server is not running or the URL is incorrect
368
+ - **Solution:** Set `EXGUARD_REALTIME_ENABLED=false` in your .env file
369
+ - The SDK will work without realtime (cache invalidation will be manual)
370
+
371
+ ```env
372
+ EXGUARD_REALTIME_ENABLED=false
373
+ ```
374
+
375
+ **To enable realtime later:**
376
+ 1. Make sure your WebSocket server is running
377
+ 2. Set correct URL: `EXGUARD_REALTIME_URL=ws://your-server:3000/realtime`
378
+ 3. Enable: `EXGUARD_REALTIME_ENABLED=true`
379
+
363
380
  ## 📊 Performance Benefits
364
381
 
365
382
  | Operation | Without Cache | With Cache | Improvement |
package/dist/index.cjs CHANGED
@@ -283,25 +283,16 @@ var ExGuardCache = class {
283
283
  var cache = new ExGuardCache();
284
284
 
285
285
  // src/realtime.ts
286
- var WebSocketImpl;
287
- if (typeof window === "undefined") {
288
- try {
289
- WebSocketImpl = require("ws");
290
- } catch {
291
- WebSocketImpl = null;
292
- }
293
- } else {
294
- WebSocketImpl = WebSocket;
295
- }
286
+ var import_socket = require("socket.io-client");
296
287
  var ExGuardRealtime = class {
297
288
  handlers = /* @__PURE__ */ new Map();
298
- websocket = null;
289
+ socket = null;
299
290
  reconnectAttempts = 0;
300
291
  config;
301
292
  currentUrl = null;
302
293
  currentToken = null;
294
+ currentUserId = null;
303
295
  shouldReconnect = true;
304
- isNode = typeof window === "undefined";
305
296
  constructor(config = {}) {
306
297
  this.config = {
307
298
  autoReconnect: config.autoReconnect ?? true,
@@ -319,13 +310,14 @@ var ExGuardRealtime = class {
319
310
  /**
320
311
  * Initialize and optionally connect to realtime server
321
312
  */
322
- async init(url, accessToken) {
313
+ async init(url, accessToken, userId) {
323
314
  if (url) {
324
315
  this.currentUrl = url;
325
316
  this.currentToken = accessToken ?? null;
317
+ this.currentUserId = userId ?? null;
326
318
  if (this.config.autoConnect) {
327
319
  try {
328
- await this.connect(url, accessToken);
320
+ await this.connect(url, accessToken, userId);
329
321
  } catch (error) {
330
322
  console.warn("[ExGuardRealtime] Auto-connect failed, will retry on demand:", error);
331
323
  }
@@ -333,138 +325,126 @@ var ExGuardRealtime = class {
333
325
  }
334
326
  }
335
327
  /**
336
- * Connect to realtime server
337
- * @param url - WebSocket URL (e.g., wss://api.example.com/realtime)
338
- * @param auth - Authentication options (accessToken, apiKey, bearerToken) - optional
339
- * @param silent - If true, won't throw error on connection failure
328
+ * Connect to realtime server using Socket.IO
329
+ * @param url - Server URL (e.g., https://api.example.com)
330
+ * @param auth - Authentication options (accessToken, apiKey, bearerToken, userId)
340
331
  */
341
- connect(url, auth, silent = false) {
332
+ connect(url, auth, userId) {
342
333
  let authToken;
343
- let authType = null;
334
+ let authUserId;
344
335
  if (typeof auth === "string") {
345
336
  authToken = auth;
346
- authType = "access_token";
347
- } else if (auth?.accessToken) {
348
- authToken = auth.accessToken;
349
- authType = "access_token";
350
- } else if (auth?.apiKey) {
351
- authToken = auth.apiKey;
352
- authType = "api_key";
353
- } else if (auth?.bearerToken) {
354
- authToken = auth.bearerToken;
355
- authType = "bearer";
337
+ authUserId = userId || "unknown";
338
+ } else if (typeof auth === "object") {
339
+ authToken = auth.accessToken || auth.bearerToken || auth.apiKey;
340
+ authUserId = auth.userId || userId || "unknown";
356
341
  }
357
342
  return new Promise((resolve, reject) => {
358
343
  this.shouldReconnect = true;
359
344
  this.currentUrl = url;
360
345
  this.currentToken = authToken ?? null;
361
- if (!WebSocketImpl) {
362
- const err = new Error('WebSocket is not available. Install "ws" package for Node.js: npm install ws');
363
- this.config.onError(err);
364
- if (!silent) reject(err);
365
- return;
366
- }
346
+ this.currentUserId = authUserId ?? null;
367
347
  try {
368
- let wsUrl = url;
369
- if (authToken && authType) {
370
- if (authType === "bearer") {
371
- wsUrl = `${url}?bearer=${encodeURIComponent(authToken)}`;
372
- } else {
373
- wsUrl = `${url}?${authType}=${encodeURIComponent(authToken)}`;
374
- }
375
- }
376
- if (this.isNode && WebSocketImpl === require("ws")) {
377
- this.websocket = new WebSocketImpl(wsUrl);
378
- this.setupNodeWebSocket(resolve, reject);
379
- } else {
380
- this.websocket = new WebSocketImpl(wsUrl);
381
- this.setupBrowserWebSocket(resolve, reject);
348
+ if (this.socket) {
349
+ this.socket.disconnect();
350
+ this.socket = null;
382
351
  }
352
+ const socketUrl = `${url}/realtime`;
353
+ console.log("[ExGuardRealtime] Connecting to:", socketUrl);
354
+ this.socket = (0, import_socket.io)(socketUrl, {
355
+ auth: {
356
+ token: authToken,
357
+ userId: authUserId
358
+ },
359
+ transports: ["websocket", "polling"],
360
+ reconnection: this.config.autoReconnect,
361
+ reconnectionDelay: this.config.reconnectDelay,
362
+ reconnectionDelayMax: this.config.reconnectMaxDelay,
363
+ reconnectionAttempts: this.config.maxReconnectAttempts,
364
+ timeout: 2e4
365
+ });
366
+ this.socket.on("connect", () => {
367
+ this.reconnectAttempts = 0;
368
+ console.log("[ExGuardRealtime] Connected! Socket ID:", this.socket?.id);
369
+ this.config.onConnect();
370
+ resolve();
371
+ });
372
+ this.socket.on("disconnect", (reason) => {
373
+ console.log("[ExGuardRealtime] Disconnected:", reason);
374
+ this.config.onDisconnect();
375
+ if (this.shouldReconnect && this.config.autoReconnect) {
376
+ this.handleReconnect();
377
+ }
378
+ });
379
+ this.socket.on("connect_error", (error) => {
380
+ console.error("[ExGuardRealtime] Connection error:", error.message);
381
+ const err = new Error(`Connection error: ${error.message}`);
382
+ this.config.onError(err);
383
+ if (!this.shouldReconnect || this.reconnectAttempts >= this.config.maxReconnectAttempts) {
384
+ reject(err);
385
+ }
386
+ });
387
+ this.socket.on("error", (error) => {
388
+ console.error("[ExGuardRealtime] Socket error:", error);
389
+ const err = new Error(`Socket error: ${error}`);
390
+ this.config.onError(err);
391
+ });
392
+ this.socket.on("reconnect_attempt", (attempt) => {
393
+ this.reconnectAttempts = attempt;
394
+ console.log(`[ExGuardRealtime] Reconnecting... (attempt ${attempt})`);
395
+ });
396
+ this.socket.on("reconnect", () => {
397
+ console.log("[ExGuardRealtime] Reconnected!");
398
+ });
399
+ this.socket.on("reconnect_failed", () => {
400
+ console.error("[ExGuardRealtime] Reconnection failed after max attempts");
401
+ const err = new Error("Reconnection failed after maximum attempts");
402
+ this.config.onError(err);
403
+ reject(err);
404
+ });
405
+ this.socket.onAny((eventName, payload) => {
406
+ try {
407
+ const realtimeEvent = {
408
+ type: eventName,
409
+ timestamp: payload?.timestamp || Date.now(),
410
+ data: payload?.data || payload,
411
+ userId: payload?.userId
412
+ };
413
+ this.handleEvent(realtimeEvent);
414
+ } catch (error) {
415
+ console.error("[ExGuardRealtime] Failed to handle event:", error);
416
+ }
417
+ });
418
+ setTimeout(() => {
419
+ if (!this.socket?.connected) {
420
+ const err = new Error("Connection timeout");
421
+ this.config.onError(err);
422
+ this.socket?.disconnect();
423
+ reject(err);
424
+ }
425
+ }, 2e4);
383
426
  } catch (error) {
384
427
  reject(error);
385
428
  }
386
429
  });
387
430
  }
388
- setupBrowserWebSocket(resolve, reject) {
389
- const timeout = setTimeout(() => {
390
- reject(new Error("Connection timeout"));
391
- }, 1e4);
392
- this.websocket.onopen = () => {
393
- clearTimeout(timeout);
394
- this.reconnectAttempts = 0;
395
- console.log("[ExGuardRealtime] Connected to realtime server");
396
- this.config.onConnect();
397
- resolve();
398
- };
399
- this.websocket.onmessage = (event) => {
400
- try {
401
- const realtimeEvent = JSON.parse(event.data);
402
- this.handleEvent(realtimeEvent);
403
- } catch (error) {
404
- console.error("[ExGuardRealtime] Failed to parse event:", error);
405
- }
406
- };
407
- this.websocket.onclose = () => {
408
- clearTimeout(timeout);
409
- console.log("[ExGuardRealtime] Disconnected from realtime server");
410
- this.config.onDisconnect();
411
- this.handleReconnect();
412
- };
413
- this.websocket.onerror = (error) => {
414
- clearTimeout(timeout);
415
- console.error("[ExGuardRealtime] WebSocket error:", error);
416
- const err = new Error("WebSocket connection error");
417
- this.config.onError(err);
418
- if (!this.shouldReconnect) {
419
- reject(err);
420
- }
421
- };
422
- }
423
- setupNodeWebSocket(resolve, reject) {
424
- const timeout = setTimeout(() => {
425
- reject(new Error("Connection timeout"));
426
- }, 1e4);
427
- this.websocket.on("open", () => {
428
- clearTimeout(timeout);
429
- this.reconnectAttempts = 0;
430
- console.log("[ExGuardRealtime] Connected to realtime server (Node.js)");
431
- this.config.onConnect();
432
- resolve();
433
- });
434
- this.websocket.on("message", (data) => {
435
- try {
436
- const realtimeEvent = JSON.parse(data.toString());
437
- this.handleEvent(realtimeEvent);
438
- } catch (error) {
439
- console.error("[ExGuardRealtime] Failed to parse event:", error);
440
- }
441
- });
442
- this.websocket.on("close", () => {
443
- clearTimeout(timeout);
444
- console.log("[ExGuardRealtime] Disconnected from realtime server");
445
- this.config.onDisconnect();
446
- this.handleReconnect();
447
- });
448
- this.websocket.on("error", (error) => {
449
- clearTimeout(timeout);
450
- console.error("[ExGuardRealtime] WebSocket error:", error);
451
- const err = new Error("WebSocket connection error: " + (error?.message || error));
452
- this.config.onError(err);
453
- if (this.shouldReconnect) {
454
- this.handleReconnect();
455
- } else {
456
- reject(err);
457
- }
458
- });
431
+ /**
432
+ * Subscribe to channels
433
+ */
434
+ subscribeToChannel(channel) {
435
+ if (this.socket?.connected) {
436
+ this.socket.emit(`subscribe:${channel}`);
437
+ console.log(`[ExGuardRealtime] Subscribed to channel: ${channel}`);
438
+ }
459
439
  }
460
440
  /**
461
441
  * Disconnect from realtime server
462
442
  */
463
443
  disconnect() {
464
444
  this.shouldReconnect = false;
465
- if (this.websocket) {
466
- this.websocket.close();
467
- this.websocket = null;
445
+ if (this.socket) {
446
+ this.socket.disconnect();
447
+ this.socket = null;
468
448
  }
469
449
  }
470
450
  /**
@@ -526,7 +506,7 @@ var ExGuardRealtime = class {
526
506
  );
527
507
  console.log(`[ExGuardRealtime] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
528
508
  setTimeout(() => {
529
- this.connect(this.currentUrl, this.currentToken).catch((error) => {
509
+ this.connect(this.currentUrl, this.currentToken, this.currentUserId).catch((error) => {
530
510
  console.error("[ExGuardRealtime] Reconnection failed:", error);
531
511
  });
532
512
  }, delay);
@@ -540,7 +520,7 @@ var ExGuardRealtime = class {
540
520
  reconnect() {
541
521
  if (this.currentUrl && this.currentToken) {
542
522
  this.reconnectAttempts = 0;
543
- return this.connect(this.currentUrl, this.currentToken);
523
+ return this.connect(this.currentUrl, this.currentToken, this.currentUserId || void 0);
544
524
  } else if (this.currentUrl) {
545
525
  this.reconnectAttempts = 0;
546
526
  return this.connect(this.currentUrl);
@@ -548,51 +528,25 @@ var ExGuardRealtime = class {
548
528
  return Promise.reject(new Error("No URL configured"));
549
529
  }
550
530
  /**
551
- * Check if connected
552
- */
531
+ * Check if connected
532
+ */
553
533
  isConnected() {
554
- if (!this.websocket) return false;
555
- if (this.isNode) {
556
- return this.websocket.readyState === 1;
557
- }
558
- return this.websocket.readyState === WebSocket.OPEN;
534
+ return this.socket?.connected ?? false;
559
535
  }
560
536
  /**
561
537
  * Get connection status
562
538
  */
563
539
  getStatus() {
564
- if (!this.websocket) return "disconnected";
565
- if (this.isNode) {
566
- switch (this.websocket.readyState) {
567
- case 0:
568
- return "connecting";
569
- case 1:
570
- return "connected";
571
- case 2:
572
- case 3:
573
- return "disconnected";
574
- default:
575
- return "disconnected";
576
- }
577
- }
578
- switch (this.websocket.readyState) {
579
- case WebSocket.CONNECTING:
580
- return "connecting";
581
- case WebSocket.OPEN:
582
- return "connected";
583
- case WebSocket.CLOSING:
584
- case WebSocket.CLOSED:
585
- return "disconnected";
586
- default:
587
- return "disconnected";
588
- }
540
+ if (!this.socket) return "disconnected";
541
+ if (this.socket.connected) return "connected";
542
+ return "disconnected";
589
543
  }
590
544
  /**
591
545
  * Send a message to the server
592
546
  */
593
- send(event) {
594
- if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
595
- this.websocket.send(JSON.stringify(event));
547
+ send(event, data) {
548
+ if (this.socket?.connected) {
549
+ this.socket.emit(event, data);
596
550
  }
597
551
  }
598
552
  /**
@@ -608,9 +562,9 @@ var ExGuardRealtime = class {
608
562
  * Connect on-demand with user's access token from request
609
563
  * Call this in your guard or middleware with the user's token
610
564
  */
611
- async connectWithUserToken(url, accessToken) {
565
+ async connectWithUserToken(url, accessToken, userId) {
612
566
  try {
613
- await this.connect(url, accessToken, true);
567
+ await this.connect(url, accessToken, userId);
614
568
  } catch (error) {
615
569
  console.warn("[ExGuardRealtime] Failed to connect with user token:", error);
616
570
  }
@@ -677,7 +631,7 @@ var ExGuardBackendEnhanced = class {
677
631
  }, 6e4);
678
632
  }
679
633
  async setupRealtime() {
680
- if (!this.config.realtime?.url) {
634
+ if (!this.config.realtime?.enabled || !this.config.realtime?.url) {
681
635
  return;
682
636
  }
683
637
  try {