summaryrefslogtreecommitdiff
path: root/frontend/src/services
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2025-11-15 18:00:09 +0000
committersoryu <soryu@soryu.co>2025-11-15 18:00:09 +0000
commit3e7b2beca1136a42700a7e1aebfe4c0fb2861a00 (patch)
tree6c896c31820681e360e50a73839fc2284c043dea /frontend/src/services
downloadsoryu-3e7b2beca1136a42700a7e1aebfe4c0fb2861a00.tar.gz
soryu-3e7b2beca1136a42700a7e1aebfe4c0fb2861a00.zip
Initial commit
Diffstat (limited to 'frontend/src/services')
-rw-r--r--frontend/src/services/ws.ts69
1 files changed, 69 insertions, 0 deletions
diff --git a/frontend/src/services/ws.ts b/frontend/src/services/ws.ts
new file mode 100644
index 0000000..7d10512
--- /dev/null
+++ b/frontend/src/services/ws.ts
@@ -0,0 +1,69 @@
+// Minimal WS client with auto-reconnect for Rust backend interoperability.
+// Point URL to your Rust server: ws://localhost:8080/ws (example)
+
+type Listener = (data: any) => void
+
+export class VNWebSocket {
+ private url: string
+ private ws: WebSocket | null = null
+ private reconnectDelay = 1000
+ private maxReconnectDelay = 8000
+ private shouldReconnect = true
+ private listeners: Record<string, Listener[]> = {}
+
+ constructor(url: string) {
+ this.url = url
+ }
+
+ on(event: 'open' | 'close' | 'error' | 'message', cb: Listener) {
+ if (!this.listeners[event]) this.listeners[event] = []
+ this.listeners[event].push(cb)
+ }
+
+ private emit(event: string, data?: any) {
+ ;(this.listeners[event] || []).forEach(cb => cb(data))
+ }
+
+ connect() {
+ this.ws = new WebSocket(this.url)
+
+ this.ws.addEventListener('open', () => {
+ this.emit('open')
+ this.reconnectDelay = 1000
+ })
+
+ this.ws.addEventListener('message', (evt) => {
+ try {
+ const parsed = JSON.parse(evt.data)
+ this.emit('message', parsed)
+ } catch {
+ this.emit('message', evt.data)
+ }
+ })
+
+ this.ws.addEventListener('close', () => {
+ this.emit('close')
+ if (this.shouldReconnect) {
+ setTimeout(() => this.connect(), this.reconnectDelay)
+ this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay)
+ }
+ })
+
+ this.ws.addEventListener('error', (e) => {
+ this.emit('error', e)
+ this.ws?.close()
+ })
+ }
+
+ send(data: any) {
+ const payload = typeof data === 'string' ? data : JSON.stringify(data)
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
+ this.ws.send(payload)
+ }
+ }
+
+ close() {
+ this.shouldReconnect = false
+ this.ws?.close()
+ }
+}