vibex-sh 0.9.6 → 0.9.8
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/index.js +128 -41
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -437,19 +437,89 @@ async function main() {
|
|
|
437
437
|
let reconnectAttempts = 0;
|
|
438
438
|
const maxReconnectDelay = 5000;
|
|
439
439
|
|
|
440
|
+
// Connection state management
|
|
441
|
+
let connectionState = 'disconnected'; // disconnected, connecting, connected, closing, closed
|
|
442
|
+
let connectionEstablished = false;
|
|
443
|
+
let connectionLock = false;
|
|
444
|
+
let connectionStartTime = null;
|
|
445
|
+
|
|
440
446
|
// Store auth code received from socket
|
|
441
447
|
let receivedAuthCode = authCode;
|
|
442
448
|
|
|
443
449
|
// Track if this is a new session (not reusing an existing one)
|
|
444
450
|
const isNewSession = !options.sessionId;
|
|
445
451
|
|
|
452
|
+
// Graceful shutdown function
|
|
453
|
+
const closeWebSocket = () => {
|
|
454
|
+
return new Promise((resolve) => {
|
|
455
|
+
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
|
456
|
+
if (connectionState === 'connected' || connectionState === 'closing') {
|
|
457
|
+
connectionState = 'closed';
|
|
458
|
+
}
|
|
459
|
+
resolve();
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
connectionState = 'closing';
|
|
464
|
+
const closeStartTime = Date.now();
|
|
465
|
+
console.log(' 🔄 Initiating graceful WebSocket close...');
|
|
466
|
+
|
|
467
|
+
const closeTimeout = setTimeout(() => {
|
|
468
|
+
const elapsed = Date.now() - closeStartTime;
|
|
469
|
+
console.log(` ⚠️ Close handshake timeout after ${elapsed}ms, forcing exit`);
|
|
470
|
+
connectionState = 'closed';
|
|
471
|
+
resolve();
|
|
472
|
+
}, 2000);
|
|
473
|
+
|
|
474
|
+
// Store original onclose handler
|
|
475
|
+
const originalOnClose = socket.onclose;
|
|
476
|
+
|
|
477
|
+
socket.onclose = (event) => {
|
|
478
|
+
clearTimeout(closeTimeout);
|
|
479
|
+
const elapsed = Date.now() - closeStartTime;
|
|
480
|
+
connectionState = 'closed';
|
|
481
|
+
connectionEstablished = false;
|
|
482
|
+
console.log(` ✓ WebSocket closed gracefully (code: ${event.code}, reason: ${event.reason || 'none'}, time: ${elapsed}ms)`);
|
|
483
|
+
|
|
484
|
+
// Call original handler if it exists
|
|
485
|
+
if (originalOnClose) {
|
|
486
|
+
originalOnClose(event);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
resolve();
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
socket.close(1000, 'Stream ended');
|
|
493
|
+
});
|
|
494
|
+
};
|
|
495
|
+
|
|
446
496
|
const connectWebSocket = () => {
|
|
497
|
+
// Prevent multiple simultaneous connections
|
|
498
|
+
if (connectionLock) {
|
|
499
|
+
console.log(' ⚠️ Connection already in progress, skipping...');
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (connectionState === 'connected' || connectionState === 'connecting') {
|
|
504
|
+
console.log(` ⚠️ Already ${connectionState}, skipping new connection...`);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
connectionLock = true;
|
|
509
|
+
connectionState = 'connecting';
|
|
510
|
+
connectionStartTime = Date.now();
|
|
511
|
+
console.log(' 🔄 Connecting to WebSocket...');
|
|
512
|
+
|
|
447
513
|
try {
|
|
448
514
|
socket = new WebSocket(`${socketUrl}?sessionId=${sessionId}`);
|
|
449
515
|
|
|
450
516
|
socket.onopen = () => {
|
|
517
|
+
const connectTime = Date.now() - connectionStartTime;
|
|
518
|
+
connectionLock = false;
|
|
519
|
+
connectionState = 'connected';
|
|
451
520
|
isConnected = true;
|
|
452
|
-
|
|
521
|
+
connectionEstablished = true;
|
|
522
|
+
console.log(` ✓ Connected to server (${connectTime}ms)\n`);
|
|
453
523
|
reconnectAttempts = 0;
|
|
454
524
|
|
|
455
525
|
// Join session
|
|
@@ -458,17 +528,15 @@ async function main() {
|
|
|
458
528
|
sessionId,
|
|
459
529
|
}));
|
|
460
530
|
|
|
461
|
-
//
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
}, 100);
|
|
531
|
+
// Set hasJoinedSession immediately - HTTP POST doesn't need WebSocket
|
|
532
|
+
hasJoinedSession = true;
|
|
533
|
+
// Process any queued logs immediately
|
|
534
|
+
while (logQueue.length > 0) {
|
|
535
|
+
const logData = logQueue.shift();
|
|
536
|
+
// Send logs via HTTP POST (non-blocking) instead of WebSocket
|
|
537
|
+
// WebSocket is only for receiving logs
|
|
538
|
+
sendLogViaHTTP(logData);
|
|
539
|
+
}
|
|
472
540
|
};
|
|
473
541
|
|
|
474
542
|
socket.onmessage = (event) => {
|
|
@@ -607,11 +675,17 @@ async function main() {
|
|
|
607
675
|
};
|
|
608
676
|
|
|
609
677
|
socket.onclose = (event) => {
|
|
678
|
+
connectionLock = false;
|
|
679
|
+
connectionState = 'closed';
|
|
680
|
+
connectionEstablished = false;
|
|
610
681
|
isConnected = false;
|
|
611
682
|
hasJoinedSession = false;
|
|
683
|
+
|
|
684
|
+
const connectionDuration = connectionStartTime ? Date.now() - connectionStartTime : 0;
|
|
685
|
+
console.log(` 📊 Connection closed (code: ${event.code}, reason: ${event.reason || 'none'}, duration: ${connectionDuration}ms)`);
|
|
612
686
|
|
|
613
|
-
// Reconnect logic
|
|
614
|
-
if (event.code !== 1000
|
|
687
|
+
// Reconnect logic - only if not a normal closure and not already closing
|
|
688
|
+
if (event.code !== 1000 && connectionState !== 'closing') {
|
|
615
689
|
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), maxReconnectDelay);
|
|
616
690
|
reconnectAttempts++;
|
|
617
691
|
console.log(` ↻ Reconnecting in ${delay}ms (attempt ${reconnectAttempts})...\n`);
|
|
@@ -619,9 +693,14 @@ async function main() {
|
|
|
619
693
|
reconnectTimeout = setTimeout(() => {
|
|
620
694
|
connectWebSocket();
|
|
621
695
|
}, delay);
|
|
696
|
+
} else if (event.code === 1000) {
|
|
697
|
+
console.log(' ✓ Normal closure, no reconnect needed\n');
|
|
622
698
|
}
|
|
623
699
|
};
|
|
624
700
|
} catch (error) {
|
|
701
|
+
connectionLock = false;
|
|
702
|
+
connectionState = 'disconnected';
|
|
703
|
+
connectionEstablished = false;
|
|
625
704
|
console.error(` ✗ Error creating WebSocket: ${error.message}`);
|
|
626
705
|
console.error(` ↻ URL: ${socketUrl}`);
|
|
627
706
|
// Retry connection
|
|
@@ -634,6 +713,7 @@ async function main() {
|
|
|
634
713
|
// Send logs via HTTP POST (non-blocking, same as SDKs)
|
|
635
714
|
// Use Cloudflare Worker endpoint (port 8787 for local, or Worker URL for production)
|
|
636
715
|
// Token is optional - anonymous sessions can send logs without authentication
|
|
716
|
+
// HTTP POST works independently of WebSocket - don't wait for WebSocket connection
|
|
637
717
|
const sendLogViaHTTP = async (logData) => {
|
|
638
718
|
try {
|
|
639
719
|
// Determine ingest URL
|
|
@@ -649,9 +729,7 @@ async function main() {
|
|
|
649
729
|
const defaultWorkerUrl = 'https://vibex-ingest.prop.workers.dev';
|
|
650
730
|
ingestUrl = `${defaultWorkerUrl}/api/v1/ingest`;
|
|
651
731
|
}
|
|
652
|
-
|
|
653
|
-
console.log(` 📤 Sending log to: ${ingestUrl}`);
|
|
654
|
-
|
|
732
|
+
|
|
655
733
|
// Build headers - only include Authorization if token exists
|
|
656
734
|
const headers = {
|
|
657
735
|
'Content-Type': 'application/json',
|
|
@@ -790,43 +868,52 @@ async function main() {
|
|
|
790
868
|
};
|
|
791
869
|
}
|
|
792
870
|
|
|
793
|
-
// Send logs via HTTP POST
|
|
794
|
-
// WebSocket is only for receiving logs and auth codes
|
|
795
|
-
|
|
796
|
-
sendLogViaHTTP(logData);
|
|
797
|
-
} else {
|
|
798
|
-
logQueue.push(logData);
|
|
799
|
-
}
|
|
871
|
+
// Send logs via HTTP POST immediately - don't wait for WebSocket
|
|
872
|
+
// WebSocket is only for receiving logs and auth codes, not required for sending
|
|
873
|
+
sendLogViaHTTP(logData);
|
|
800
874
|
});
|
|
801
875
|
|
|
802
|
-
rl.on('close', () => {
|
|
876
|
+
rl.on('close', async () => {
|
|
803
877
|
// Wait for queued logs to be sent
|
|
804
878
|
const waitForQueue = () => {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
if (reconnectTimeout) {
|
|
811
|
-
clearTimeout(reconnectTimeout);
|
|
879
|
+
return new Promise((resolve) => {
|
|
880
|
+
if (logQueue.length === 0) {
|
|
881
|
+
resolve();
|
|
882
|
+
} else {
|
|
883
|
+
setTimeout(() => waitForQueue().then(resolve), 100);
|
|
812
884
|
}
|
|
813
|
-
|
|
814
|
-
} else {
|
|
815
|
-
setTimeout(waitForQueue, 100);
|
|
816
|
-
}
|
|
885
|
+
});
|
|
817
886
|
};
|
|
818
887
|
|
|
819
|
-
waitForQueue();
|
|
888
|
+
await waitForQueue();
|
|
889
|
+
|
|
890
|
+
console.log('\n Stream ended. Closing connection...\n');
|
|
891
|
+
|
|
892
|
+
// Cancel any pending reconnection attempts
|
|
893
|
+
if (reconnectTimeout) {
|
|
894
|
+
clearTimeout(reconnectTimeout);
|
|
895
|
+
reconnectTimeout = null;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Graceful shutdown - wait for close handshake
|
|
899
|
+
await closeWebSocket();
|
|
900
|
+
|
|
901
|
+
// Give a moment for any final cleanup
|
|
902
|
+
setTimeout(() => process.exit(0), 100);
|
|
820
903
|
});
|
|
821
904
|
|
|
822
|
-
process.on('SIGINT', () => {
|
|
905
|
+
process.on('SIGINT', async () => {
|
|
823
906
|
console.log('\n Interrupted. Closing connection...\n');
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
}
|
|
907
|
+
|
|
908
|
+
// Cancel any pending reconnection attempts
|
|
827
909
|
if (reconnectTimeout) {
|
|
828
910
|
clearTimeout(reconnectTimeout);
|
|
911
|
+
reconnectTimeout = null;
|
|
829
912
|
}
|
|
913
|
+
|
|
914
|
+
// Graceful shutdown
|
|
915
|
+
await closeWebSocket();
|
|
916
|
+
|
|
830
917
|
process.exit(0);
|
|
831
918
|
});
|
|
832
919
|
}
|