exguard-backend 1.0.22 → 1.0.24

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
@@ -38,6 +38,7 @@ __export(src_exports, {
38
38
  cache: () => cache,
39
39
  createExGuardExpress: () => createExGuardExpress,
40
40
  createExGuardFastify: () => createExGuardFastify,
41
+ createRealtime: () => createRealtime,
41
42
  realtime: () => realtime
42
43
  });
43
44
  module.exports = __toCommonJS(src_exports);
@@ -286,15 +287,58 @@ var ExGuardRealtime = class {
286
287
  handlers = /* @__PURE__ */ new Map();
287
288
  websocket = null;
288
289
  reconnectAttempts = 0;
289
- maxReconnectAttempts = 5;
290
- reconnectDelay = 1e3;
290
+ config;
291
+ currentUrl = null;
292
+ currentToken = null;
293
+ shouldReconnect = true;
294
+ constructor(config = {}) {
295
+ this.config = {
296
+ autoReconnect: config.autoReconnect ?? true,
297
+ maxReconnectAttempts: config.maxReconnectAttempts ?? 5,
298
+ reconnectDelay: config.reconnectDelay ?? 1e3,
299
+ reconnectMaxDelay: config.reconnectMaxDelay ?? 3e4,
300
+ onConnect: config.onConnect ?? (() => {
301
+ }),
302
+ onDisconnect: config.onDisconnect ?? (() => {
303
+ }),
304
+ onError: config.onError ?? ((error) => console.error("[ExGuardRealtime] Error:", error))
305
+ };
306
+ }
291
307
  /**
292
308
  * Connect to realtime server
309
+ * @param url - WebSocket URL (e.g., wss://api.example.com/realtime)
310
+ * @param auth - Authentication options (accessToken, apiKey, bearerToken) - optional
293
311
  */
294
- connect(url, token) {
312
+ connect(url, auth) {
313
+ let authToken;
314
+ let authType = null;
315
+ if (typeof auth === "string") {
316
+ authToken = auth;
317
+ authType = "access_token";
318
+ } else if (auth?.accessToken) {
319
+ authToken = auth.accessToken;
320
+ authType = "access_token";
321
+ } else if (auth?.apiKey) {
322
+ authToken = auth.apiKey;
323
+ authType = "api_key";
324
+ } else if (auth?.bearerToken) {
325
+ authToken = auth.bearerToken;
326
+ authType = "bearer";
327
+ }
295
328
  return new Promise((resolve, reject) => {
329
+ this.shouldReconnect = true;
330
+ this.currentUrl = url;
331
+ this.currentToken = authToken ?? null;
296
332
  try {
297
- this.websocket = new WebSocket(`${url}?token=${token}`);
333
+ let wsUrl = url;
334
+ if (authToken && authType) {
335
+ if (authType === "bearer") {
336
+ wsUrl = `${url}?bearer=${encodeURIComponent(authToken)}`;
337
+ } else {
338
+ wsUrl = `${url}?${authType}=${encodeURIComponent(authToken)}`;
339
+ }
340
+ }
341
+ this.websocket = new WebSocket(wsUrl);
298
342
  const timeout = setTimeout(() => {
299
343
  reject(new Error("Connection timeout"));
300
344
  }, 1e4);
@@ -302,6 +346,7 @@ var ExGuardRealtime = class {
302
346
  clearTimeout(timeout);
303
347
  this.reconnectAttempts = 0;
304
348
  console.log("[ExGuardRealtime] Connected to realtime server");
349
+ this.config.onConnect();
305
350
  resolve();
306
351
  };
307
352
  this.websocket.onmessage = (event) => {
@@ -315,12 +360,15 @@ var ExGuardRealtime = class {
315
360
  this.websocket.onclose = () => {
316
361
  clearTimeout(timeout);
317
362
  console.log("[ExGuardRealtime] Disconnected from realtime server");
318
- this.handleReconnect(url, token);
363
+ this.config.onDisconnect();
364
+ this.handleReconnect();
319
365
  };
320
366
  this.websocket.onerror = (error) => {
321
367
  clearTimeout(timeout);
322
368
  console.error("[ExGuardRealtime] WebSocket error:", error);
323
- reject(error);
369
+ const err = new Error("WebSocket connection error");
370
+ this.config.onError(err);
371
+ reject(err);
324
372
  };
325
373
  } catch (error) {
326
374
  reject(error);
@@ -331,6 +379,7 @@ var ExGuardRealtime = class {
331
379
  * Disconnect from realtime server
332
380
  */
333
381
  disconnect() {
382
+ this.shouldReconnect = false;
334
383
  if (this.websocket) {
335
384
  this.websocket.close();
336
385
  this.websocket = null;
@@ -354,6 +403,16 @@ var ExGuardRealtime = class {
354
403
  }
355
404
  };
356
405
  }
406
+ /**
407
+ * Subscribe to all realtime events
408
+ */
409
+ subscribeAll(handler) {
410
+ const eventTypes = ["rbac_update", "user_update", "role_update", "permission_update"];
411
+ const unsubscribes = eventTypes.map((type) => this.subscribe(type, handler));
412
+ return () => {
413
+ unsubscribes.forEach((unsub) => unsub());
414
+ };
415
+ }
357
416
  /**
358
417
  * Handle incoming realtime events
359
418
  */
@@ -373,13 +432,19 @@ var ExGuardRealtime = class {
373
432
  /**
374
433
  * Handle reconnection logic
375
434
  */
376
- handleReconnect(url, token) {
377
- if (this.reconnectAttempts < this.maxReconnectAttempts) {
435
+ handleReconnect() {
436
+ if (!this.shouldReconnect || !this.config.autoReconnect || !this.currentUrl) {
437
+ return;
438
+ }
439
+ if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
378
440
  this.reconnectAttempts++;
379
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
441
+ const delay = Math.min(
442
+ this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
443
+ this.config.reconnectMaxDelay
444
+ );
380
445
  console.log(`[ExGuardRealtime] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
381
446
  setTimeout(() => {
382
- this.connect(url, token).catch((error) => {
447
+ this.connect(this.currentUrl, this.currentToken).catch((error) => {
383
448
  console.error("[ExGuardRealtime] Reconnection failed:", error);
384
449
  });
385
450
  }, delay);
@@ -387,13 +452,63 @@ var ExGuardRealtime = class {
387
452
  console.error("[ExGuardRealtime] Max reconnection attempts reached");
388
453
  }
389
454
  }
455
+ /**
456
+ * Manually trigger reconnection with current auth
457
+ */
458
+ reconnect() {
459
+ if (this.currentUrl && this.currentToken) {
460
+ this.reconnectAttempts = 0;
461
+ return this.connect(this.currentUrl, this.currentToken);
462
+ } else if (this.currentUrl) {
463
+ this.reconnectAttempts = 0;
464
+ return this.connect(this.currentUrl);
465
+ }
466
+ return Promise.reject(new Error("No URL configured"));
467
+ }
390
468
  /**
391
469
  * Check if connected
392
470
  */
393
471
  isConnected() {
394
472
  return this.websocket?.readyState === WebSocket.OPEN;
395
473
  }
474
+ /**
475
+ * Get connection status
476
+ */
477
+ getStatus() {
478
+ if (!this.websocket) return "disconnected";
479
+ switch (this.websocket.readyState) {
480
+ case WebSocket.CONNECTING:
481
+ return "connecting";
482
+ case WebSocket.OPEN:
483
+ return "connected";
484
+ case WebSocket.CLOSING:
485
+ case WebSocket.CLOSED:
486
+ return "disconnected";
487
+ default:
488
+ return "disconnected";
489
+ }
490
+ }
491
+ /**
492
+ * Send a message to the server
493
+ */
494
+ send(event) {
495
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
496
+ this.websocket.send(JSON.stringify(event));
497
+ }
498
+ }
499
+ /**
500
+ * Update configuration
501
+ */
502
+ updateConfig(config) {
503
+ this.config = {
504
+ ...this.config,
505
+ ...config
506
+ };
507
+ }
396
508
  };
509
+ function createRealtime(config) {
510
+ return new ExGuardRealtime(config);
511
+ }
397
512
  var realtime = new ExGuardRealtime();
398
513
 
399
514
  // src/exguard-backend-enhanced.ts
@@ -403,21 +518,40 @@ var ExGuardBackendEnhanced = class {
403
518
  cache;
404
519
  realtime;
405
520
  userId = null;
521
+ _isRealtimeConnected = false;
406
522
  constructor(config) {
407
523
  this.config = {
408
524
  timeout: 1e4,
409
525
  cache: {
410
526
  enabled: true,
411
527
  ttl: 3e5
412
- // 5 minutes
413
528
  },
414
529
  realtime: {
415
- enabled: false
530
+ enabled: false,
531
+ autoReconnect: true,
532
+ maxReconnectAttempts: 5,
533
+ reconnectDelay: 1e3
416
534
  },
417
535
  ...config
418
536
  };
419
537
  this.cache = cache;
420
- this.realtime = realtime;
538
+ const realtimeConfig = {
539
+ autoReconnect: this.config.realtime?.autoReconnect ?? true,
540
+ maxReconnectAttempts: this.config.realtime?.maxReconnectAttempts ?? 5,
541
+ reconnectDelay: this.config.realtime?.reconnectDelay ?? 1e3,
542
+ onConnect: () => {
543
+ this._isRealtimeConnected = true;
544
+ this.config.realtime?.onConnect?.();
545
+ },
546
+ onDisconnect: () => {
547
+ this._isRealtimeConnected = false;
548
+ this.config.realtime?.onDisconnect?.();
549
+ },
550
+ onError: (error) => {
551
+ this.config.realtime?.onError?.(error);
552
+ }
553
+ };
554
+ this.realtime = createRealtime(realtimeConfig);
421
555
  this.client = import_axios2.default.create({
422
556
  baseURL: this.config.apiUrl,
423
557
  timeout: this.config.timeout,
@@ -425,21 +559,22 @@ var ExGuardBackendEnhanced = class {
425
559
  "Content-Type": "application/json"
426
560
  }
427
561
  });
428
- if (this.config.realtime?.enabled && this.config.realtime.url && this.config.realtime.token) {
562
+ if (this.config.realtime?.enabled && this.config.realtime.url) {
429
563
  this.setupRealtime();
430
564
  }
431
565
  setInterval(() => {
432
566
  this.cache.cleanup();
433
567
  }, 6e4);
434
568
  }
435
- /**
436
- * Setup realtime connection and event handlers
437
- */
438
569
  async setupRealtime() {
439
570
  try {
440
- await this.realtime.connect(this.config.realtime.url, this.config.realtime.token);
571
+ await this.realtime.connect(
572
+ this.config.realtime.url,
573
+ this.config.realtime.token
574
+ );
441
575
  this.realtime.subscribe("rbac_update", (event) => {
442
576
  console.log("[ExGuardBackend] RBAC update received:", event);
577
+ this.config.realtime?.onRBACUpdate?.(event);
443
578
  if (event.userId) {
444
579
  this.cache.clearUserCache(event.userId);
445
580
  } else {
@@ -448,17 +583,72 @@ var ExGuardBackendEnhanced = class {
448
583
  });
449
584
  this.realtime.subscribe("user_update", (event) => {
450
585
  console.log("[ExGuardBackend] User update received:", event);
586
+ this.config.realtime?.onUserUpdate?.(event);
451
587
  if (event.userId) {
452
588
  this.cache.clearUserCache(event.userId);
453
589
  }
454
590
  });
591
+ this.realtime.subscribe("role_update", (event) => {
592
+ console.log("[ExGuardBackend] Role update received:", event);
593
+ if (event.userId) {
594
+ this.cache.clearUserCache(event.userId);
595
+ } else {
596
+ this.cache.clear();
597
+ }
598
+ });
599
+ this.realtime.subscribe("permission_update", (event) => {
600
+ console.log("[ExGuardBackend] Permission update received:", event);
601
+ if (event.userId) {
602
+ this.cache.clearUserCache(event.userId);
603
+ } else {
604
+ this.cache.clear();
605
+ }
606
+ });
455
607
  } catch (error) {
456
608
  console.error("[ExGuardBackend] Failed to setup realtime:", error);
457
609
  }
458
610
  }
459
611
  /**
460
- * Extract user ID from JWT token
612
+ * Manually connect to realtime server
461
613
  */
614
+ async connectRealtime(url, token) {
615
+ const wsUrl = url || this.config.realtime?.url;
616
+ const wsToken = token || this.config.realtime?.token;
617
+ if (!wsUrl) {
618
+ throw new Error("WebSocket URL is required");
619
+ }
620
+ await this.setupRealtime();
621
+ }
622
+ /**
623
+ * Disconnect from realtime server
624
+ */
625
+ disconnectRealtime() {
626
+ this.realtime.disconnect();
627
+ }
628
+ /**
629
+ * Check if realtime is connected
630
+ */
631
+ isRealtimeConnected() {
632
+ return this._isRealtimeConnected || this.realtime.isConnected();
633
+ }
634
+ /**
635
+ * Get realtime connection status
636
+ */
637
+ getRealtimeStatus() {
638
+ return this.realtime.getStatus();
639
+ }
640
+ /**
641
+ * Subscribe to realtime events
642
+ */
643
+ subscribeToRealtime(eventType, handler) {
644
+ return this.realtime.subscribe(eventType, handler);
645
+ }
646
+ /**
647
+ * Subscribe to all realtime events
648
+ */
649
+ subscribeToAllRealtime(handler) {
650
+ return this.realtime.subscribeAll(handler);
651
+ }
462
652
  extractUserIdFromToken(token) {
463
653
  try {
464
654
  const payload = JSON.parse(atob(token.split(".")[1]));
@@ -467,16 +657,10 @@ var ExGuardBackendEnhanced = class {
467
657
  return null;
468
658
  }
469
659
  }
470
- /**
471
- * Get cache key for user data
472
- */
473
660
  getCacheKey(token, suffix = "") {
474
661
  const userId = this.extractUserIdFromToken(token) || "unknown";
475
662
  return `user:${userId}:${suffix}`;
476
663
  }
477
- /**
478
- * Get user roles and permissions from cache or API
479
- */
480
664
  async getUserAccess(token) {
481
665
  if (!this.config.cache?.enabled) {
482
666
  return this.fetchUserAccess(token);
@@ -492,9 +676,6 @@ var ExGuardBackendEnhanced = class {
492
676
  this.cache.set(cacheKey, data, this.config.cache.ttl);
493
677
  return data;
494
678
  }
495
- /**
496
- * Fetch user access from API (always hits the server)
497
- */
498
679
  async fetchUserAccess(token) {
499
680
  try {
500
681
  const response = await this.client.get("/guard/me", {
@@ -516,9 +697,6 @@ var ExGuardBackendEnhanced = class {
516
697
  throw error;
517
698
  }
518
699
  }
519
- /**
520
- * Check if user has specific permission (cached)
521
- */
522
700
  async hasPermission(token, permission) {
523
701
  if (!this.config.cache?.enabled) {
524
702
  const userAccess2 = await this.getUserAccess(token);
@@ -540,9 +718,6 @@ var ExGuardBackendEnhanced = class {
540
718
  this.cache.set(cacheKey, hasPermission, this.config.cache.ttl / 2);
541
719
  return hasPermission;
542
720
  }
543
- /**
544
- * Check if user has specific role (cached)
545
- */
546
721
  async hasRole(token, role) {
547
722
  if (!this.config.cache?.enabled) {
548
723
  const userAccess2 = await this.getUserAccess(token);
@@ -560,9 +735,6 @@ var ExGuardBackendEnhanced = class {
560
735
  this.cache.set(cacheKey, hasRole, this.config.cache.ttl / 2);
561
736
  return hasRole;
562
737
  }
563
- /**
564
- * Get all permissions for a specific module (cached)
565
- */
566
738
  async getModulePermissions(token, moduleKey) {
567
739
  if (!this.config.cache?.enabled) {
568
740
  const userAccess2 = await this.getUserAccess(token);
@@ -582,9 +754,6 @@ var ExGuardBackendEnhanced = class {
582
754
  this.cache.set(cacheKey, permissions, this.config.cache.ttl);
583
755
  return permissions;
584
756
  }
585
- /**
586
- * Get user roles (cached)
587
- */
588
757
  async getUserRoles(token) {
589
758
  if (!this.config.cache?.enabled) {
590
759
  const userAccess2 = await this.getUserAccess(token);
@@ -602,9 +771,6 @@ var ExGuardBackendEnhanced = class {
602
771
  this.cache.set(cacheKey, roles, this.config.cache.ttl);
603
772
  return roles;
604
773
  }
605
- /**
606
- * Get user field offices (cached)
607
- */
608
774
  async getUserFieldOffices(token) {
609
775
  if (!this.config.cache?.enabled) {
610
776
  const userAccess2 = await this.getUserAccess(token);
@@ -622,9 +788,6 @@ var ExGuardBackendEnhanced = class {
622
788
  this.cache.set(cacheKey, fieldOffices, this.config.cache.ttl);
623
789
  return fieldOffices;
624
790
  }
625
- /**
626
- * Batch check multiple permissions (optimized)
627
- */
628
791
  async hasPermissions(token, permissions) {
629
792
  const results = {};
630
793
  const uncachedPermissions = [];
@@ -656,9 +819,6 @@ var ExGuardBackendEnhanced = class {
656
819
  }
657
820
  return results;
658
821
  }
659
- /**
660
- * Batch check multiple roles (optimized)
661
- */
662
822
  async hasRoles(token, roles) {
663
823
  const results = {};
664
824
  const uncachedRoles = [];
@@ -688,9 +848,6 @@ var ExGuardBackendEnhanced = class {
688
848
  }
689
849
  return results;
690
850
  }
691
- /**
692
- * Clear cache for a specific user
693
- */
694
851
  clearUserCache(token) {
695
852
  const userId = this.extractUserIdFromToken(token);
696
853
  if (userId) {
@@ -698,15 +855,13 @@ var ExGuardBackendEnhanced = class {
698
855
  console.log(`[ExGuardBackend] Cache cleared for user: ${userId}`);
699
856
  }
700
857
  }
701
- /**
702
- * Get cache statistics
703
- */
858
+ clearAllCache() {
859
+ this.cache.clear();
860
+ console.log("[ExGuardBackend] All cache cleared");
861
+ }
704
862
  getCacheStats() {
705
863
  return this.cache.getStats();
706
864
  }
707
- /**
708
- * Disconnect from realtime server
709
- */
710
865
  disconnect() {
711
866
  this.realtime.disconnect();
712
867
  }
@@ -1403,6 +1558,7 @@ function createGuardContext2(req) {
1403
1558
  cache,
1404
1559
  createExGuardExpress,
1405
1560
  createExGuardFastify,
1561
+ createRealtime,
1406
1562
  realtime
1407
1563
  });
1408
1564
  //# sourceMappingURL=index.cjs.map