@roj-ai/transport 0.0.2

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.
Files changed (64) hide show
  1. package/dist/client/client-connection.d.ts +48 -0
  2. package/dist/client/client-connection.d.ts.map +1 -0
  3. package/dist/client/client-connection.js +160 -0
  4. package/dist/client/index.d.ts +10 -0
  5. package/dist/client/index.d.ts.map +1 -0
  6. package/dist/client/index.js +7 -0
  7. package/dist/core/connection.d.ts +59 -0
  8. package/dist/core/connection.d.ts.map +1 -0
  9. package/dist/core/connection.js +165 -0
  10. package/dist/core/index.d.ts +16 -0
  11. package/dist/core/index.d.ts.map +1 -0
  12. package/dist/core/index.js +13 -0
  13. package/dist/core/protocol.d.ts +206 -0
  14. package/dist/core/protocol.d.ts.map +1 -0
  15. package/dist/core/protocol.js +93 -0
  16. package/dist/core/result.d.ts +26 -0
  17. package/dist/core/result.d.ts.map +1 -0
  18. package/dist/core/result.js +20 -0
  19. package/dist/core/router.d.ts +79 -0
  20. package/dist/core/router.d.ts.map +1 -0
  21. package/dist/core/router.js +119 -0
  22. package/dist/core/types.d.ts +44 -0
  23. package/dist/core/types.d.ts.map +1 -0
  24. package/dist/core/types.js +14 -0
  25. package/dist/index.d.ts +24 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +26 -0
  28. package/dist/platform/browser.d.ts +14 -0
  29. package/dist/platform/browser.d.ts.map +1 -0
  30. package/dist/platform/browser.js +65 -0
  31. package/dist/platform/bun.d.ts +69 -0
  32. package/dist/platform/bun.d.ts.map +1 -0
  33. package/dist/platform/bun.js +152 -0
  34. package/dist/platform/types.d.ts +83 -0
  35. package/dist/platform/types.d.ts.map +1 -0
  36. package/dist/platform/types.js +14 -0
  37. package/dist/server/connection-manager.d.ts +106 -0
  38. package/dist/server/connection-manager.d.ts.map +1 -0
  39. package/dist/server/connection-manager.js +234 -0
  40. package/dist/server/index.d.ts +12 -0
  41. package/dist/server/index.d.ts.map +1 -0
  42. package/dist/server/index.js +8 -0
  43. package/dist/server/server-connection.d.ts +50 -0
  44. package/dist/server/server-connection.d.ts.map +1 -0
  45. package/dist/server/server-connection.js +84 -0
  46. package/package.json +52 -0
  47. package/src/__tests__/integration.test.ts +443 -0
  48. package/src/client/client-connection.ts +207 -0
  49. package/src/client/index.ts +24 -0
  50. package/src/core/connection.ts +203 -0
  51. package/src/core/index.ts +52 -0
  52. package/src/core/protocol.test.ts +235 -0
  53. package/src/core/protocol.ts +348 -0
  54. package/src/core/result.ts +35 -0
  55. package/src/core/router.test.ts +248 -0
  56. package/src/core/router.ts +186 -0
  57. package/src/core/types.ts +85 -0
  58. package/src/index.ts +80 -0
  59. package/src/platform/browser.ts +79 -0
  60. package/src/platform/bun.ts +220 -0
  61. package/src/platform/types.ts +102 -0
  62. package/src/server/connection-manager.ts +298 -0
  63. package/src/server/index.ts +26 -0
  64. package/src/server/server-connection.ts +122 -0
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Message Router
3
+ *
4
+ * Handles notification dispatching - no request-response, no correlation.
5
+ * Simplified for notification-only protocols.
6
+ */
7
+
8
+ import type { HandlerContext, InferInput, Protocol, ProtocolDef, ProtocolHandlers } from './protocol.js'
9
+ import type { WireMessage } from './types.js'
10
+
11
+ // ============================================================================
12
+ // Router Types
13
+ // ============================================================================
14
+
15
+ /**
16
+ * Message send function type.
17
+ */
18
+ export type MessageSender = (message: string) => boolean
19
+
20
+ /**
21
+ * Router error types for debugging protocol issues.
22
+ */
23
+ export type RouterErrorType = 'parse' | 'unknown_type' | 'validation' | 'handler'
24
+
25
+ /**
26
+ * Router error handler callback.
27
+ */
28
+ export type RouterErrorHandler = (error: {
29
+ type: RouterErrorType
30
+ message: string
31
+ raw?: string
32
+ details?: unknown
33
+ }) => void
34
+
35
+ // ============================================================================
36
+ // Message Router
37
+ // ============================================================================
38
+
39
+ /**
40
+ * Message router for handling incoming notifications and sending outbound notifications.
41
+ *
42
+ * Simplified notification-only router:
43
+ * - No request-response correlation
44
+ * - No pending requests tracking
45
+ * - Fire-and-forget notifications only
46
+ */
47
+ /**
48
+ * Raw message listener — receives every parsed message before handler dispatch.
49
+ */
50
+ export type RawMessageListener = (type: string, payload: unknown) => void
51
+
52
+ export class MessageRouter<TReceive extends ProtocolDef, TSend extends ProtocolDef> {
53
+ private readonly receiveProtocol: Protocol<TReceive> | null
54
+ private readonly sendProtocol: Protocol<TSend> | null
55
+
56
+ private handlers: Partial<ProtocolHandlers<TReceive>> = {}
57
+ private sender: MessageSender | null = null
58
+ private errorHandler: RouterErrorHandler | null = null
59
+ private rawMessageListener: RawMessageListener | null = null
60
+
61
+ constructor(
62
+ receiveProtocol: Protocol<TReceive> | null,
63
+ sendProtocol: Protocol<TSend> | null,
64
+ ) {
65
+ this.receiveProtocol = receiveProtocol
66
+ this.sendProtocol = sendProtocol
67
+ }
68
+
69
+ /**
70
+ * Set the message sender function.
71
+ */
72
+ setSender(sender: MessageSender): void {
73
+ this.sender = sender
74
+ }
75
+
76
+ /**
77
+ * Set handlers for incoming notifications.
78
+ */
79
+ setHandlers(handlers: Partial<ProtocolHandlers<TReceive>>): void {
80
+ this.handlers = handlers
81
+ }
82
+
83
+ /**
84
+ * Set error handler for debugging protocol issues.
85
+ */
86
+ setErrorHandler(handler: RouterErrorHandler): void {
87
+ this.errorHandler = handler
88
+ }
89
+
90
+ /**
91
+ * Set a raw message listener that receives every incoming message.
92
+ * When set, bypasses per-type handler dispatch — the listener receives (type, payload) directly.
93
+ */
94
+ setRawMessageListener(listener: RawMessageListener): void {
95
+ this.rawMessageListener = listener
96
+ }
97
+
98
+ /**
99
+ * Handle an incoming raw message (notification).
100
+ */
101
+ async handleMessage(raw: string, ctx: HandlerContext): Promise<void> {
102
+ let parsed: WireMessage
103
+ try {
104
+ parsed = JSON.parse(raw)
105
+ } catch (e) {
106
+ this.errorHandler?.({ type: 'parse', message: 'Invalid JSON', raw, details: e })
107
+ return
108
+ }
109
+
110
+ const { type, payload } = parsed
111
+
112
+ // Raw listener mode — forward everything without dispatch or validation
113
+ if (this.rawMessageListener) {
114
+ this.rawMessageListener(type, payload)
115
+ return
116
+ }
117
+
118
+ if (this.receiveProtocol) {
119
+ // Validated path — protocol provided, validate input before dispatching
120
+ const endpoint = this.receiveProtocol._def[type]
121
+ if (!endpoint) {
122
+ this.errorHandler?.({ type: 'unknown_type', message: `Unknown message type: ${type}`, raw })
123
+ return
124
+ }
125
+
126
+ const inputResult = this.receiveProtocol.validateInput(type, payload)
127
+ if (!inputResult.success) {
128
+ this.errorHandler?.({ type: 'validation', message: `Validation failed for ${type}`, raw, details: inputResult.error })
129
+ return
130
+ }
131
+
132
+ const handler = this.handlers[type]
133
+ if (!handler) return
134
+
135
+ try {
136
+ await (handler as (input: unknown, ctx: HandlerContext) => Promise<void>)(inputResult.data, ctx)
137
+ } catch (e) {
138
+ this.errorHandler?.({ type: 'handler', message: `Handler error for ${type}`, raw, details: e })
139
+ }
140
+ } else {
141
+ // Unvalidated path — no protocol, dispatch directly (trusted source)
142
+ const handler = this.handlers[type]
143
+ if (!handler) return
144
+
145
+ try {
146
+ await (handler as (input: unknown, ctx: HandlerContext) => Promise<void>)(payload, ctx)
147
+ } catch (e) {
148
+ this.errorHandler?.({ type: 'handler', message: `Handler error for ${type}`, raw, details: e })
149
+ }
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Send a notification (fire-and-forget).
155
+ */
156
+ notify<K extends keyof TSend>(
157
+ name: K,
158
+ input: TSend[K] extends { input: infer I } ? (I extends { _type: string } ? never : InferInput<TSend[K]>) : never,
159
+ ): boolean {
160
+ if (!this.sender) {
161
+ return false
162
+ }
163
+
164
+ const message: WireMessage = {
165
+ type: String(name),
166
+ payload: input,
167
+ ts: Date.now(),
168
+ }
169
+
170
+ return this.sender(JSON.stringify(message))
171
+ }
172
+ }
173
+
174
+ // ============================================================================
175
+ // Router Builder
176
+ // ============================================================================
177
+
178
+ /**
179
+ * Create a message router for the given protocols.
180
+ */
181
+ export function createRouter<TReceive extends ProtocolDef, TSend extends ProtocolDef>(
182
+ receiveProtocol: Protocol<TReceive> | null,
183
+ sendProtocol: Protocol<TSend> | null,
184
+ ): MessageRouter<TReceive, TSend> {
185
+ return new MessageRouter(receiveProtocol, sendProtocol)
186
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Core Transport Types
3
+ *
4
+ * Platform-agnostic types for WebSocket transport infrastructure.
5
+ */
6
+
7
+ // ============================================================================
8
+ // Connection States
9
+ // ============================================================================
10
+
11
+ /**
12
+ * Connection state machine states.
13
+ */
14
+ export type ConnectionState =
15
+ | 'disconnected'
16
+ | 'connecting'
17
+ | 'connected'
18
+ | 'reconnecting'
19
+
20
+ // ============================================================================
21
+ // Wire Message Format
22
+ // ============================================================================
23
+
24
+ /**
25
+ * Wire message envelope for all protocol messages.
26
+ * Simplified: notification-only, no correlation ID.
27
+ */
28
+ export interface WireMessage<T = unknown> {
29
+ /** Notification type name */
30
+ type: string
31
+ /** Message payload */
32
+ payload: T
33
+ /** Timestamp */
34
+ ts: number
35
+ }
36
+
37
+ // ============================================================================
38
+ // Transport Events
39
+ // ============================================================================
40
+
41
+ /**
42
+ * Transport event types.
43
+ */
44
+ export type TransportEvent =
45
+ | 'connected'
46
+ | 'disconnected'
47
+ | 'error'
48
+ | 'reconnecting'
49
+ | 'reconnected'
50
+ | 'state_change'
51
+ | 'router_error'
52
+
53
+ /**
54
+ * Transport event listener.
55
+ */
56
+ export type TransportEventListener = (event: TransportEvent, data?: unknown) => void
57
+
58
+ // ============================================================================
59
+ // Configuration
60
+ // ============================================================================
61
+
62
+ /**
63
+ * Reconnection strategy options.
64
+ */
65
+ export interface ReconnectOptions {
66
+ /** Base delay in ms (default: 1000) */
67
+ baseDelayMs: number
68
+ /** Maximum delay in ms (default: 30000) */
69
+ maxDelayMs: number
70
+ /** Maximum number of attempts before giving up (default: Infinity) */
71
+ maxAttempts: number
72
+ /** Jitter factor (0-1, default: 0.3) */
73
+ jitterFactor: number
74
+ }
75
+
76
+ // ============================================================================
77
+ // Default Values
78
+ // ============================================================================
79
+
80
+ export const DEFAULT_RECONNECT_OPTIONS: ReconnectOptions = {
81
+ baseDelayMs: 1000,
82
+ maxDelayMs: 30000,
83
+ maxAttempts: Infinity,
84
+ jitterFactor: 0.3,
85
+ }
package/src/index.ts ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * @roj-ai/transport
3
+ *
4
+ * Platform-agnostic WebSocket transport infrastructure with RPC-like protocol API.
5
+ */
6
+
7
+ // ============================================================================
8
+ // Core Exports
9
+ // ============================================================================
10
+
11
+ // Protocol Definition API
12
+ export { defineProtocol, method, notification } from './core/protocol.js'
13
+ export type {
14
+ CallerFor,
15
+ EndpointDef,
16
+ EndpointHandler,
17
+ ExtractProtocolDef,
18
+ HandlerContext,
19
+ HandlersFor,
20
+ InferError,
21
+ InferInput,
22
+ InferOutput,
23
+ IsMethod,
24
+ IsNotification,
25
+ MethodCaller,
26
+ MethodDef,
27
+ MethodHandler,
28
+ MethodOptions,
29
+ NotificationDef,
30
+ NotificationHandler,
31
+ NotificationNotifier,
32
+ NotificationOptions,
33
+ NotifierFor,
34
+ Protocol,
35
+ ProtocolCaller,
36
+ ProtocolDef,
37
+ ProtocolHandlers,
38
+ ProtocolNotifier,
39
+ } from './core/protocol.js'
40
+
41
+ // Result Type
42
+ export { Err, flatMapResult, isErr, isOk, mapResult, Ok, unwrapOr, unwrapOrThrow } from './core/result.js'
43
+ export type { Result } from './core/result.js'
44
+
45
+ // Types
46
+ export type { ConnectionState, ReconnectOptions, TransportEvent, TransportEventListener, WireMessage } from './core/types.js'
47
+
48
+ export { DEFAULT_RECONNECT_OPTIONS } from './core/types.js'
49
+
50
+ // Core Components
51
+ export { createRouter, MessageRouter } from './core/router.js'
52
+ export type { MessageSender, RawMessageListener, RouterErrorHandler, RouterErrorType } from './core/router.js'
53
+
54
+ export { Connection } from './core/connection.js'
55
+ export type { ConnectionConfig } from './core/connection.js'
56
+
57
+ // ============================================================================
58
+ // Platform Exports
59
+ // ============================================================================
60
+
61
+ export type { ICloseEvent, IServerWebSocket, IWebSocket, IWebSocketFactory } from './platform/types.js'
62
+
63
+ export { WebSocketReadyState } from './platform/types.js'
64
+
65
+ // ============================================================================
66
+ // Client Exports
67
+ // ============================================================================
68
+
69
+ export { ClientConnection, createClientConnection } from './client/client-connection.js'
70
+ export type { ClientConnectionConfig } from './client/client-connection.js'
71
+
72
+ // ============================================================================
73
+ // Server Exports
74
+ // ============================================================================
75
+
76
+ export { createServerConnection, ServerConnection } from './server/server-connection.js'
77
+ export type { ServerConnectionConfig, ServerHandlerContext } from './server/server-connection.js'
78
+
79
+ export { ConnectionManager } from './server/connection-manager.js'
80
+ export type { ConnectionStats } from './server/connection-manager.js'
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Browser WebSocket Adapter
3
+ *
4
+ * Wraps the native browser WebSocket to conform to IWebSocket interface.
5
+ */
6
+
7
+ import type { ICloseEvent, IWebSocket, IWebSocketFactory } from './types.js'
8
+ import { WebSocketReadyState } from './types.js'
9
+
10
+ /**
11
+ * Browser WebSocket adapter that wraps native WebSocket.
12
+ */
13
+ class BrowserWebSocketAdapter implements IWebSocket {
14
+ private readonly ws: WebSocket
15
+
16
+ onopen: (() => void) | null = null
17
+ onclose: ((event: ICloseEvent) => void) | null = null
18
+ onerror: ((error: Error) => void) | null = null
19
+ onmessage: ((data: string) => void) | null = null
20
+
21
+ constructor(url: string) {
22
+ this.ws = new WebSocket(url)
23
+
24
+ this.ws.onopen = () => {
25
+ this.onopen?.()
26
+ }
27
+
28
+ this.ws.onclose = (event) => {
29
+ this.onclose?.({
30
+ code: event.code,
31
+ reason: event.reason,
32
+ })
33
+ }
34
+
35
+ this.ws.onerror = () => {
36
+ // Browser WebSocket doesn't provide error details
37
+ this.onerror?.(new Error('WebSocket error'))
38
+ }
39
+
40
+ this.ws.onmessage = (event) => {
41
+ // Handle text data only
42
+ if (typeof event.data === 'string') {
43
+ this.onmessage?.(event.data)
44
+ } else if (event.data instanceof Blob) {
45
+ // Convert Blob to string for text-only protocol
46
+ event.data.text().then((text) => {
47
+ this.onmessage?.(text)
48
+ })
49
+ }
50
+ }
51
+ }
52
+
53
+ get readyState(): WebSocketReadyState {
54
+ return this.ws.readyState as WebSocketReadyState
55
+ }
56
+
57
+ send(data: string): void {
58
+ if (this.ws.readyState === WebSocketReadyState.OPEN) {
59
+ this.ws.send(data)
60
+ }
61
+ }
62
+
63
+ close(code?: number, reason?: string): void {
64
+ this.ws.close(code, reason)
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Browser WebSocket factory.
70
+ */
71
+ export const browserWebSocketFactory: IWebSocketFactory = {
72
+ create(url: string): IWebSocket {
73
+ return new BrowserWebSocketAdapter(url)
74
+ },
75
+ }
76
+
77
+ // Re-export types for convenience
78
+ export { WebSocketReadyState }
79
+ export type { ICloseEvent, IWebSocket, IWebSocketFactory }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Bun WebSocket Adapters
3
+ *
4
+ * Provides both client and server WebSocket adapters for Bun runtime.
5
+ */
6
+
7
+ import type { ICloseEvent, IServerWebSocket, IWebSocket, IWebSocketFactory } from './types.js'
8
+ import { WebSocketReadyState } from './types.js'
9
+
10
+ // Type for Bun's native WebSocket
11
+ type BunWebSocket = WebSocket
12
+
13
+ /**
14
+ * Bun WebSocket client adapter.
15
+ * Wraps Bun's WebSocket client to conform to IWebSocket interface.
16
+ */
17
+ class BunWebSocketClientAdapter implements IWebSocket {
18
+ private readonly ws: BunWebSocket
19
+
20
+ onopen: (() => void) | null = null
21
+ onclose: ((event: ICloseEvent) => void) | null = null
22
+ onerror: ((error: Error) => void) | null = null
23
+ onmessage: ((data: string) => void) | null = null
24
+
25
+ constructor(url: string) {
26
+ this.ws = new WebSocket(url)
27
+
28
+ this.ws.onopen = () => {
29
+ this.onopen?.()
30
+ }
31
+
32
+ this.ws.onclose = (event) => {
33
+ this.onclose?.({
34
+ code: event.code,
35
+ reason: event.reason,
36
+ })
37
+ }
38
+
39
+ this.ws.onerror = () => {
40
+ this.onerror?.(new Error('WebSocket error'))
41
+ }
42
+
43
+ this.ws.onmessage = (event) => {
44
+ if (typeof event.data === 'string') {
45
+ this.onmessage?.(event.data)
46
+ }
47
+ }
48
+ }
49
+
50
+ get readyState(): WebSocketReadyState {
51
+ return this.ws.readyState as WebSocketReadyState
52
+ }
53
+
54
+ send(data: string): void {
55
+ if (this.ws.readyState === WebSocketReadyState.OPEN) {
56
+ this.ws.send(data)
57
+ }
58
+ }
59
+
60
+ close(code?: number, reason?: string): void {
61
+ this.ws.close(code, reason)
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Bun WebSocket client factory.
67
+ */
68
+ export const bunWebSocketFactory: IWebSocketFactory = {
69
+ create(url: string): IWebSocket {
70
+ return new BunWebSocketClientAdapter(url)
71
+ },
72
+ }
73
+
74
+ /**
75
+ * Server WebSocket data interface.
76
+ */
77
+ export interface ServerWebSocketData<T = unknown> {
78
+ /** User-attached data */
79
+ userData: T
80
+ /** Topics this connection is subscribed to */
81
+ topics: Set<string>
82
+ }
83
+
84
+ // Using 'any' here because Bun's ServerWebSocket has complex generics
85
+ // that would require importing Bun types directly
86
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
+ type BunServerWebSocket = any
88
+
89
+ /**
90
+ * Bun server WebSocket adapter.
91
+ * Wraps Bun's ServerWebSocket to conform to IServerWebSocket interface.
92
+ */
93
+ export class BunServerWebSocketAdapter<T = unknown> implements IServerWebSocket<T> {
94
+ private readonly ws: BunServerWebSocket
95
+ private wsData: ServerWebSocketData<T>
96
+
97
+ onopen: (() => void) | null = null
98
+ onclose: ((event: ICloseEvent) => void) | null = null
99
+ onerror: ((error: Error) => void) | null = null
100
+ onmessage: ((data: string) => void) | null = null
101
+
102
+ constructor(ws: BunServerWebSocket, data: T) {
103
+ this.ws = ws
104
+ this.wsData = {
105
+ userData: data,
106
+ topics: new Set(),
107
+ }
108
+ }
109
+
110
+ get data(): T {
111
+ return this.wsData.userData
112
+ }
113
+
114
+ set data(value: T) {
115
+ this.wsData.userData = value
116
+ }
117
+
118
+ get readyState(): WebSocketReadyState {
119
+ return this.ws.readyState as WebSocketReadyState
120
+ }
121
+
122
+ send(data: string): void {
123
+ if (this.ws.readyState === WebSocketReadyState.OPEN) {
124
+ this.ws.send(data)
125
+ }
126
+ }
127
+
128
+ close(code?: number, reason?: string): void {
129
+ this.ws.close(code, reason)
130
+ }
131
+
132
+ subscribe(topic: string): void {
133
+ this.ws.subscribe(topic)
134
+ this.wsData.topics.add(topic)
135
+ }
136
+
137
+ unsubscribe(topic: string): void {
138
+ this.ws.unsubscribe(topic)
139
+ this.wsData.topics.delete(topic)
140
+ }
141
+
142
+ publish(topic: string, message: string): void {
143
+ this.ws.publish(topic, message)
144
+ }
145
+
146
+ isSubscribed(topic: string): boolean {
147
+ return this.wsData.topics.has(topic)
148
+ }
149
+
150
+ /**
151
+ * Get the underlying Bun WebSocket (for advanced use cases).
152
+ */
153
+ getRawWebSocket(): BunServerWebSocket {
154
+ return this.ws
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Helper to create Bun WebSocket handlers that integrate with the transport layer.
160
+ */
161
+ export interface BunWebSocketHandlers<T> {
162
+ open: (ws: BunServerWebSocket) => void
163
+ close: (ws: BunServerWebSocket, code: number, reason: string) => void
164
+ message: (ws: BunServerWebSocket, message: string | Buffer) => void
165
+ error: (ws: BunServerWebSocket, error: Error) => void
166
+ }
167
+
168
+ /**
169
+ * Create Bun WebSocket handlers with adapter integration.
170
+ */
171
+ export function createBunWebSocketHandlers<T>(callbacks: {
172
+ onOpen: (ws: IServerWebSocket<T>) => void
173
+ onClose: (ws: IServerWebSocket<T>, code: number, reason: string) => void
174
+ onMessage: (ws: IServerWebSocket<T>, message: string) => void
175
+ onError: (ws: IServerWebSocket<T>, error: Error) => void
176
+ getData: (ws: BunServerWebSocket) => T
177
+ }): BunWebSocketHandlers<T> {
178
+ // Map to store adapter instances
179
+ const adapters = new WeakMap<BunServerWebSocket, BunServerWebSocketAdapter<T>>()
180
+
181
+ const getOrCreateAdapter = (ws: BunServerWebSocket): BunServerWebSocketAdapter<T> => {
182
+ let adapter = adapters.get(ws)
183
+ if (!adapter) {
184
+ adapter = new BunServerWebSocketAdapter(ws, callbacks.getData(ws))
185
+ adapters.set(ws, adapter)
186
+ }
187
+ return adapter
188
+ }
189
+
190
+ return {
191
+ open(ws: BunServerWebSocket) {
192
+ const adapter = getOrCreateAdapter(ws)
193
+ callbacks.onOpen(adapter)
194
+ },
195
+
196
+ close(ws: BunServerWebSocket, code: number, reason: string) {
197
+ const adapter = adapters.get(ws)
198
+ if (adapter) {
199
+ callbacks.onClose(adapter, code, reason)
200
+ adapters.delete(ws)
201
+ }
202
+ },
203
+
204
+ message(ws: BunServerWebSocket, message: string | Buffer) {
205
+ const adapter = getOrCreateAdapter(ws)
206
+ // Convert Buffer to string if needed
207
+ const data = typeof message === 'string' ? message : message.toString('utf-8')
208
+ callbacks.onMessage(adapter, data)
209
+ },
210
+
211
+ error(ws: BunServerWebSocket, error: Error) {
212
+ const adapter = getOrCreateAdapter(ws)
213
+ callbacks.onError(adapter, error)
214
+ },
215
+ }
216
+ }
217
+
218
+ // Re-export types for convenience
219
+ export { WebSocketReadyState }
220
+ export type { ICloseEvent, IServerWebSocket, IWebSocket, IWebSocketFactory }