diff --git a/package.json b/package.json index cc26a59..f9dbef2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@appwrite.io/synapse", - "version": "0.4.0", + "version": "0.4.1", "description": "Operating system gateway for remote serverless environments", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/synapse.ts b/src/synapse.ts index 9fa1729..16b7c4d 100644 --- a/src/synapse.ts +++ b/src/synapse.ts @@ -22,7 +22,16 @@ export type MessageHandler = ( message: MessagePayload, connectionId: string, ) => void; + export type ConnectionCallback = (connectionId: string) => void; + +export type ConnectionCloseCallback = ( + connectionId: string, + code?: number, + reason?: string, + wasClean?: boolean, +) => void; + export type ErrorCallback = (error: Error, connectionId: string) => void; export type ServerConnectionCallback = (connectionId: string) => void; export type Logger = (message: string) => void; @@ -33,7 +42,7 @@ class Synapse { private messageHandlers: Record = {}; private connectionListeners = { onOpen: (() => {}) as ConnectionCallback, - onClose: (() => {}) as ConnectionCallback, + onClose: (() => {}) as ConnectionCloseCallback, onError: (() => {}) as ErrorCallback, }; @@ -112,8 +121,13 @@ class Synapse { ws.onmessage = (event) => this.handleMessage(event, connectionId); - ws.onclose = () => { - this.connectionListeners.onClose(connectionId); + ws.onclose = (event) => { + this.connectionListeners.onClose( + connectionId, + event.code, + event.reason, + event.wasClean, + ); this.attemptReconnect(connectionId); }; @@ -440,10 +454,10 @@ class Synapse { /** * Registers a callback for when a WebSocket connection is closed - * @param callback - Function to be called when connection closes + * @param callback - Function to be called when connection closes. Receives (connectionId, code, reason) * @returns The Synapse instance for method chaining */ - onClose(callback: ConnectionCallback): Synapse { + onClose(callback: ConnectionCloseCallback): Synapse { this.connectionListeners.onClose = callback; return this; } diff --git a/tests/synapse.test.ts b/tests/synapse.test.ts index cb6f0ca..33c9056 100644 --- a/tests/synapse.test.ts +++ b/tests/synapse.test.ts @@ -62,6 +62,28 @@ describe("Synapse", () => { await expect(connectPromise).rejects.toThrow("WebSocket error"); }); + + it("should call onClose with code, reason, and wasClean", () => { + const mockWs = createMockWebSocket(); + (WebSocket as unknown as jest.Mock).mockImplementation(() => mockWs); + + const onCloseMock = jest.fn(); + synapse.onClose(onCloseMock); + + // Use the real setup method so event handlers are set + (synapse as any).setupWebSocket(mockWs, { url: "/" }, "conn1"); + + // Simulate close event + const closeEvent = { code: 4001, reason: "Test reason", wasClean: true }; + mockWs.onclose && mockWs.onclose(closeEvent as any); + + expect(onCloseMock).toHaveBeenCalledWith( + "conn1", + 4001, + "Test reason", + true, + ); + }); }); describe("message handling", () => {