exguard-backend 1.0.33 → 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/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
  }
@@ -683,9 +637,7 @@ var ExGuardBackendEnhanced = class {
683
637
  try {
684
638
  await this.realtime.connect(
685
639
  this.config.realtime.url,
686
- this.config.realtime.accessToken,
687
- true
688
- // silent mode - won't throw on connection failure
640
+ this.config.realtime.accessToken
689
641
  );
690
642
  this.realtime.subscribe("rbac_update", (event) => {
691
643
  console.log("[ExGuardBackend] RBAC update received:", event);