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 +20 -3
- package/dist/index.cjs +118 -164
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -16
- package/dist/index.d.ts +18 -16
- package/dist/index.js +118 -171
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
-
|
|
359
|
-
|
|
360
|
-
|
|
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
|
|
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
|
-
|
|
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 -
|
|
338
|
-
* @param auth - Authentication options (accessToken, apiKey, bearerToken)
|
|
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,
|
|
332
|
+
connect(url, auth, userId) {
|
|
342
333
|
let authToken;
|
|
343
|
-
let
|
|
334
|
+
let authUserId;
|
|
344
335
|
if (typeof auth === "string") {
|
|
345
336
|
authToken = auth;
|
|
346
|
-
|
|
347
|
-
} else if (auth
|
|
348
|
-
authToken = auth.accessToken;
|
|
349
|
-
|
|
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
|
-
|
|
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
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
this.
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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.
|
|
466
|
-
this.
|
|
467
|
-
this.
|
|
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
|
-
|
|
552
|
-
|
|
531
|
+
* Check if connected
|
|
532
|
+
*/
|
|
553
533
|
isConnected() {
|
|
554
|
-
|
|
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.
|
|
565
|
-
if (this.
|
|
566
|
-
|
|
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.
|
|
595
|
-
this.
|
|
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,
|
|
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 {
|