verani 0.1.6 → 0.1.7

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.
@@ -0,0 +1 @@
1
+ const require_types=require(`./types-083oWz55.cjs`);function encodeClientMessage$1(t){return require_types.n(t)}function decodeServerMessage$1(t){return require_types.s(t)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,t){this.config=e,this.onStateChange=t,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,t={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:t.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:t.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:t.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:t.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:t.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:t.maxQueueSize??100,connectionTimeout:t.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,t=>{clearTimeout(e),this.handleClose(t)}),this.ws.addEventListener(`error`,t=>{clearTimeout(e),this.handleError(t)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.emitLifecycleEvent(`open`),this.emitLifecycleEvent(`connected`),this.onOpenCallback?.()}handleMessage(e){let t=decodeServerMessage$1(e.data);if(!t)return;let r=t.type,i=t.data;t.type===`event`&&t.data&&typeof t.data==`object`&&`type`in t.data&&(r=t.data.type,i=t.data);let a=this.listeners.get(r);if(a)for(let e of a)try{e(i)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`close`,e),this.emitLifecycleEvent(`disconnected`,e),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}handleError(e){this.emitLifecycleEvent(`error`,e),this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`error`,e),this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}emitLifecycleEvent(e,t){let n=this.listeners.get(e);if(n)for(let e of n)try{e(t)}catch{}}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,t)=>{this.connectionResolve=e,this.connectionReject=t}),this.connectionPromise)}on(e,t){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t)}off(e,t){let n=this.listeners.get(e);n&&(n.delete(t),n.size===0&&this.listeners.delete(e))}once(e,t){let n=r=>{this.off(e,n),t(r)};this.on(e,n)}emit(e,n){let r={type:e,data:n};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(r))}catch{this.queueMessage(r)}else this.queueMessage(r)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};exports.ConnectionManager=ConnectionManager,exports.DEFAULT_RECONNECTION_CONFIG=DEFAULT_RECONNECTION_CONFIG,exports.PROTOCOL_VERSION=require_types.t,exports.VeraniClient=VeraniClient,exports.decodeClientMessage=require_types.a,exports.decodeFrame=require_types.o,exports.decodeServerMessage=require_types.s,exports.encodeClientMessage=require_types.n,exports.encodeFrame=require_types.r,exports.encodeServerMessage=require_types.i;
@@ -0,0 +1,208 @@
1
+ import { a as encodeFrame, d as ServerMessage, f as VeraniMessage, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, s as ClientMessage, t as decodeClientMessage, u as PROTOCOL_VERSION } from "./decode-DMA_rBWB.cjs";
2
+
3
+ //#region src/client/connection.d.ts
4
+ /**
5
+ * Connection state management for Verani client
6
+ */
7
+ type ConnectionState = "connecting" | "connected" | "disconnected" | "reconnecting" | "error";
8
+ interface ReconnectionConfig {
9
+ /** Enable automatic reconnection */
10
+ enabled: boolean;
11
+ /** Maximum number of reconnection attempts (0 = infinite) */
12
+ maxAttempts: number;
13
+ /** Initial delay in milliseconds */
14
+ initialDelay: number;
15
+ /** Maximum delay in milliseconds */
16
+ maxDelay: number;
17
+ /** Backoff multiplier for exponential backoff */
18
+ backoffMultiplier: number;
19
+ }
20
+ declare const DEFAULT_RECONNECTION_CONFIG: ReconnectionConfig;
21
+ /**
22
+ * Manages WebSocket connection lifecycle and reconnection logic
23
+ */
24
+ declare class ConnectionManager {
25
+ private config;
26
+ private onStateChange?;
27
+ private state;
28
+ private reconnectAttempts;
29
+ private reconnectTimer?;
30
+ private currentDelay;
31
+ constructor(config?: ReconnectionConfig, onStateChange?: ((state: ConnectionState) => void) | undefined);
32
+ /**
33
+ * Gets the current connection state
34
+ */
35
+ getState(): ConnectionState;
36
+ /**
37
+ * Updates the connection state and notifies listeners
38
+ */
39
+ setState(newState: ConnectionState): void;
40
+ /**
41
+ * Resets reconnection state (called on successful connection)
42
+ */
43
+ resetReconnection(): void;
44
+ /**
45
+ * Schedules a reconnection attempt
46
+ */
47
+ scheduleReconnect(connectFn: () => void): boolean;
48
+ /**
49
+ * Cancels any pending reconnection
50
+ */
51
+ cancelReconnect(): void;
52
+ /**
53
+ * Clears the reconnect timer
54
+ */
55
+ private clearReconnectTimer;
56
+ /**
57
+ * Gets the current reconnection attempt count
58
+ */
59
+ getReconnectAttempts(): number;
60
+ /**
61
+ * Gets the next reconnection delay
62
+ */
63
+ getNextDelay(): number;
64
+ /**
65
+ * Cleanup method
66
+ */
67
+ destroy(): void;
68
+ }
69
+ //#endregion
70
+ //#region src/client/client.d.ts
71
+ /**
72
+ * Client options for configuring the Verani client
73
+ */
74
+ interface VeraniClientOptions {
75
+ /** Reconnection configuration */
76
+ reconnection?: Partial<ReconnectionConfig>;
77
+ /** Maximum number of messages to queue when disconnected */
78
+ maxQueueSize?: number;
79
+ /** Connection timeout in milliseconds */
80
+ connectionTimeout?: number;
81
+ }
82
+ /**
83
+ * Verani WebSocket client with automatic reconnection and lifecycle management
84
+ */
85
+ declare class VeraniClient {
86
+ private url;
87
+ private ws?;
88
+ private listeners;
89
+ private connectionManager;
90
+ private messageQueue;
91
+ private options;
92
+ private onOpenCallback?;
93
+ private onCloseCallback?;
94
+ private onErrorCallback?;
95
+ private onStateChangeCallback?;
96
+ private connectionPromise?;
97
+ private connectionResolve?;
98
+ private connectionReject?;
99
+ /**
100
+ * Creates a new Verani client
101
+ * @param url - WebSocket URL to connect to
102
+ * @param options - Client configuration options
103
+ */
104
+ constructor(url: string, options?: VeraniClientOptions);
105
+ /**
106
+ * Establishes WebSocket connection
107
+ */
108
+ private connect;
109
+ /**
110
+ * Handles successful WebSocket connection
111
+ */
112
+ private handleOpen;
113
+ /**
114
+ * Handles incoming WebSocket messages
115
+ */
116
+ private handleMessage;
117
+ /**
118
+ * Handles WebSocket closure
119
+ */
120
+ private handleClose;
121
+ /**
122
+ * Handles WebSocket errors
123
+ */
124
+ private handleError;
125
+ /**
126
+ * Handles connection errors
127
+ */
128
+ private handleConnectionError;
129
+ /**
130
+ * Emits a lifecycle event to registered listeners
131
+ */
132
+ private emitLifecycleEvent;
133
+ /**
134
+ * Flushes queued messages when connection is established
135
+ */
136
+ private flushMessageQueue;
137
+ /**
138
+ * Gets the current connection state
139
+ */
140
+ getState(): ConnectionState;
141
+ /**
142
+ * Checks if the client is currently connected
143
+ */
144
+ isConnected(): boolean;
145
+ /**
146
+ * Waits for the connection to be established
147
+ * @returns Promise that resolves when connected
148
+ */
149
+ waitForConnection(): Promise<void>;
150
+ /**
151
+ * Registers an event listener
152
+ * @param event - Event type to listen for
153
+ * @param callback - Callback function to invoke when event is received
154
+ */
155
+ on(event: string, callback: (data: any) => void): void;
156
+ /**
157
+ * Removes an event listener
158
+ * @param event - Event type to remove listener from
159
+ * @param callback - Callback function to remove
160
+ */
161
+ off(event: string, callback: (data: any) => void): void;
162
+ /**
163
+ * Registers a one-time event listener
164
+ * @param event - Event type to listen for
165
+ * @param callback - Callback function to invoke once
166
+ */
167
+ once(event: string, callback: (data: any) => void): void;
168
+ /**
169
+ * Sends a message to the server
170
+ * @param type - Message type
171
+ * @param data - Optional message data
172
+ */
173
+ emit(type: string, data?: any): void;
174
+ /**
175
+ * Queues a message for sending when connected
176
+ */
177
+ private queueMessage;
178
+ /**
179
+ * Registers lifecycle callback for connection open
180
+ */
181
+ onOpen(callback: () => void): void;
182
+ /**
183
+ * Registers lifecycle callback for connection close
184
+ */
185
+ onClose(callback: (event: CloseEvent) => void): void;
186
+ /**
187
+ * Registers lifecycle callback for connection error
188
+ */
189
+ onError(callback: (error: Event) => void): void;
190
+ /**
191
+ * Registers lifecycle callback for state changes
192
+ */
193
+ onStateChange(callback: (state: ConnectionState) => void): void;
194
+ /**
195
+ * Manually triggers a reconnection
196
+ */
197
+ reconnect(): void;
198
+ /**
199
+ * Closes the connection without reconnecting
200
+ */
201
+ disconnect(): void;
202
+ /**
203
+ * Closes the connection and cleans up resources
204
+ */
205
+ close(): void;
206
+ }
207
+ //#endregion
208
+ export { type ClientMessage, ConnectionManager, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, PROTOCOL_VERSION, type ReconnectionConfig, type ServerMessage, VeraniClient, type VeraniClientOptions, type VeraniMessage, decodeClientMessage, decodeFrame, decodeServerMessage, encodeClientMessage, encodeFrame, encodeServerMessage };
@@ -0,0 +1,208 @@
1
+ import { a as encodeFrame, d as ServerMessage, f as VeraniMessage, i as encodeClientMessage, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, s as ClientMessage, t as decodeClientMessage, u as PROTOCOL_VERSION } from "./decode-9DerwlQ1.mjs";
2
+
3
+ //#region src/client/connection.d.ts
4
+ /**
5
+ * Connection state management for Verani client
6
+ */
7
+ type ConnectionState = "connecting" | "connected" | "disconnected" | "reconnecting" | "error";
8
+ interface ReconnectionConfig {
9
+ /** Enable automatic reconnection */
10
+ enabled: boolean;
11
+ /** Maximum number of reconnection attempts (0 = infinite) */
12
+ maxAttempts: number;
13
+ /** Initial delay in milliseconds */
14
+ initialDelay: number;
15
+ /** Maximum delay in milliseconds */
16
+ maxDelay: number;
17
+ /** Backoff multiplier for exponential backoff */
18
+ backoffMultiplier: number;
19
+ }
20
+ declare const DEFAULT_RECONNECTION_CONFIG: ReconnectionConfig;
21
+ /**
22
+ * Manages WebSocket connection lifecycle and reconnection logic
23
+ */
24
+ declare class ConnectionManager {
25
+ private config;
26
+ private onStateChange?;
27
+ private state;
28
+ private reconnectAttempts;
29
+ private reconnectTimer?;
30
+ private currentDelay;
31
+ constructor(config?: ReconnectionConfig, onStateChange?: ((state: ConnectionState) => void) | undefined);
32
+ /**
33
+ * Gets the current connection state
34
+ */
35
+ getState(): ConnectionState;
36
+ /**
37
+ * Updates the connection state and notifies listeners
38
+ */
39
+ setState(newState: ConnectionState): void;
40
+ /**
41
+ * Resets reconnection state (called on successful connection)
42
+ */
43
+ resetReconnection(): void;
44
+ /**
45
+ * Schedules a reconnection attempt
46
+ */
47
+ scheduleReconnect(connectFn: () => void): boolean;
48
+ /**
49
+ * Cancels any pending reconnection
50
+ */
51
+ cancelReconnect(): void;
52
+ /**
53
+ * Clears the reconnect timer
54
+ */
55
+ private clearReconnectTimer;
56
+ /**
57
+ * Gets the current reconnection attempt count
58
+ */
59
+ getReconnectAttempts(): number;
60
+ /**
61
+ * Gets the next reconnection delay
62
+ */
63
+ getNextDelay(): number;
64
+ /**
65
+ * Cleanup method
66
+ */
67
+ destroy(): void;
68
+ }
69
+ //#endregion
70
+ //#region src/client/client.d.ts
71
+ /**
72
+ * Client options for configuring the Verani client
73
+ */
74
+ interface VeraniClientOptions {
75
+ /** Reconnection configuration */
76
+ reconnection?: Partial<ReconnectionConfig>;
77
+ /** Maximum number of messages to queue when disconnected */
78
+ maxQueueSize?: number;
79
+ /** Connection timeout in milliseconds */
80
+ connectionTimeout?: number;
81
+ }
82
+ /**
83
+ * Verani WebSocket client with automatic reconnection and lifecycle management
84
+ */
85
+ declare class VeraniClient {
86
+ private url;
87
+ private ws?;
88
+ private listeners;
89
+ private connectionManager;
90
+ private messageQueue;
91
+ private options;
92
+ private onOpenCallback?;
93
+ private onCloseCallback?;
94
+ private onErrorCallback?;
95
+ private onStateChangeCallback?;
96
+ private connectionPromise?;
97
+ private connectionResolve?;
98
+ private connectionReject?;
99
+ /**
100
+ * Creates a new Verani client
101
+ * @param url - WebSocket URL to connect to
102
+ * @param options - Client configuration options
103
+ */
104
+ constructor(url: string, options?: VeraniClientOptions);
105
+ /**
106
+ * Establishes WebSocket connection
107
+ */
108
+ private connect;
109
+ /**
110
+ * Handles successful WebSocket connection
111
+ */
112
+ private handleOpen;
113
+ /**
114
+ * Handles incoming WebSocket messages
115
+ */
116
+ private handleMessage;
117
+ /**
118
+ * Handles WebSocket closure
119
+ */
120
+ private handleClose;
121
+ /**
122
+ * Handles WebSocket errors
123
+ */
124
+ private handleError;
125
+ /**
126
+ * Handles connection errors
127
+ */
128
+ private handleConnectionError;
129
+ /**
130
+ * Emits a lifecycle event to registered listeners
131
+ */
132
+ private emitLifecycleEvent;
133
+ /**
134
+ * Flushes queued messages when connection is established
135
+ */
136
+ private flushMessageQueue;
137
+ /**
138
+ * Gets the current connection state
139
+ */
140
+ getState(): ConnectionState;
141
+ /**
142
+ * Checks if the client is currently connected
143
+ */
144
+ isConnected(): boolean;
145
+ /**
146
+ * Waits for the connection to be established
147
+ * @returns Promise that resolves when connected
148
+ */
149
+ waitForConnection(): Promise<void>;
150
+ /**
151
+ * Registers an event listener
152
+ * @param event - Event type to listen for
153
+ * @param callback - Callback function to invoke when event is received
154
+ */
155
+ on(event: string, callback: (data: any) => void): void;
156
+ /**
157
+ * Removes an event listener
158
+ * @param event - Event type to remove listener from
159
+ * @param callback - Callback function to remove
160
+ */
161
+ off(event: string, callback: (data: any) => void): void;
162
+ /**
163
+ * Registers a one-time event listener
164
+ * @param event - Event type to listen for
165
+ * @param callback - Callback function to invoke once
166
+ */
167
+ once(event: string, callback: (data: any) => void): void;
168
+ /**
169
+ * Sends a message to the server
170
+ * @param type - Message type
171
+ * @param data - Optional message data
172
+ */
173
+ emit(type: string, data?: any): void;
174
+ /**
175
+ * Queues a message for sending when connected
176
+ */
177
+ private queueMessage;
178
+ /**
179
+ * Registers lifecycle callback for connection open
180
+ */
181
+ onOpen(callback: () => void): void;
182
+ /**
183
+ * Registers lifecycle callback for connection close
184
+ */
185
+ onClose(callback: (event: CloseEvent) => void): void;
186
+ /**
187
+ * Registers lifecycle callback for connection error
188
+ */
189
+ onError(callback: (error: Event) => void): void;
190
+ /**
191
+ * Registers lifecycle callback for state changes
192
+ */
193
+ onStateChange(callback: (state: ConnectionState) => void): void;
194
+ /**
195
+ * Manually triggers a reconnection
196
+ */
197
+ reconnect(): void;
198
+ /**
199
+ * Closes the connection without reconnecting
200
+ */
201
+ disconnect(): void;
202
+ /**
203
+ * Closes the connection and cleans up resources
204
+ */
205
+ close(): void;
206
+ }
207
+ //#endregion
208
+ export { type ClientMessage, ConnectionManager, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, PROTOCOL_VERSION, type ReconnectionConfig, type ServerMessage, VeraniClient, type VeraniClientOptions, type VeraniMessage, decodeClientMessage, decodeFrame, decodeServerMessage, encodeClientMessage, encodeFrame, encodeServerMessage };
@@ -0,0 +1 @@
1
+ import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,c){this.config=e,this.onStateChange=c,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,c={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:c.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:c.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:c.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:c.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:c.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:c.maxQueueSize??100,connectionTimeout:c.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,c=>{clearTimeout(e),this.handleClose(c)}),this.ws.addEventListener(`error`,c=>{clearTimeout(e),this.handleError(c)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.emitLifecycleEvent(`open`),this.emitLifecycleEvent(`connected`),this.onOpenCallback?.()}handleMessage(e){let c=decodeServerMessage$1(e.data);if(!c)return;let l=c.type,u=c.data;c.type===`event`&&c.data&&typeof c.data==`object`&&`type`in c.data&&(l=c.data.type,u=c.data);let d=this.listeners.get(l);if(d)for(let e of d)try{e(u)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`close`,e),this.emitLifecycleEvent(`disconnected`,e),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}handleError(e){this.emitLifecycleEvent(`error`,e),this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`error`,e),this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}emitLifecycleEvent(e,c){let l=this.listeners.get(e);if(l)for(let e of l)try{e(c)}catch{}}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,c)=>{this.connectionResolve=e,this.connectionReject=c}),this.connectionPromise)}on(e,c){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(c)}off(e,c){let l=this.listeners.get(e);l&&(l.delete(c),l.size===0&&this.listeners.delete(e))}once(e,c){let l=u=>{this.off(e,l),c(u)};this.on(e,l)}emit(e,c){let l={type:e,data:c};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(l))}catch{this.queueMessage(l)}else this.queueMessage(l)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,decodeClientMessage,decodeFrame,decodeServerMessage,encodeClientMessage,encodeFrame,encodeServerMessage};
@@ -0,0 +1,87 @@
1
+ //#region src/shared/types.d.ts
2
+ /**
3
+ * Core message types shared between client and server
4
+ */
5
+ /**
6
+ * Base message frame structure used for all WebSocket communication
7
+ */
8
+ interface MessageFrame {
9
+ type: string;
10
+ channel?: string;
11
+ data?: any;
12
+ }
13
+ /**
14
+ * Message sent from client to server
15
+ */
16
+ interface ClientMessage extends MessageFrame {
17
+ type: string;
18
+ channel?: string;
19
+ data?: any;
20
+ }
21
+ /**
22
+ * Message sent from server to client
23
+ */
24
+ interface ServerMessage extends MessageFrame {
25
+ type: string;
26
+ channel?: string;
27
+ data?: any;
28
+ }
29
+ /**
30
+ * Connection metadata attached to each WebSocket
31
+ */
32
+ interface ConnectionMeta {
33
+ userId: string;
34
+ clientId: string;
35
+ channels: string[];
36
+ }
37
+ /**
38
+ * Unified message type for both directions
39
+ */
40
+ type VeraniMessage = ClientMessage | ServerMessage;
41
+ /**
42
+ * Protocol version for future compatibility
43
+ */
44
+ declare const PROTOCOL_VERSION = "1.0.0";
45
+ //#endregion
46
+ //#region src/shared/encode.d.ts
47
+ /**
48
+ * Encodes a message frame to JSON string for transmission
49
+ * @param frame - The message frame to encode
50
+ * @returns JSON string representation of the frame
51
+ * @throws Error if encoding fails
52
+ */
53
+ declare function encodeFrame(frame: MessageFrame): string;
54
+ /**
55
+ * Encodes a client message to JSON string
56
+ * @param message - The client message to encode
57
+ * @returns JSON string representation
58
+ */
59
+ declare function encodeClientMessage(message: MessageFrame): string;
60
+ /**
61
+ * Encodes a server message to JSON string
62
+ * @param message - The server message to encode
63
+ * @returns JSON string representation
64
+ */
65
+ declare function encodeServerMessage(message: MessageFrame): string;
66
+ //#endregion
67
+ //#region src/shared/decode.d.ts
68
+ /**
69
+ * Decodes a raw message into a MessageFrame
70
+ * @param raw - Raw data from WebSocket (string, ArrayBuffer, etc)
71
+ * @returns Decoded MessageFrame or null if invalid
72
+ */
73
+ declare function decodeFrame(raw: any): MessageFrame | null;
74
+ /**
75
+ * Decodes a client message
76
+ * @param raw - Raw data from client WebSocket
77
+ * @returns Decoded message or null if invalid
78
+ */
79
+ declare function decodeClientMessage(raw: any): MessageFrame | null;
80
+ /**
81
+ * Decodes a server message
82
+ * @param raw - Raw data from server WebSocket
83
+ * @returns Decoded message or null if invalid
84
+ */
85
+ declare function decodeServerMessage(raw: any): MessageFrame | null;
86
+ //#endregion
87
+ export { encodeFrame as a, ConnectionMeta as c, ServerMessage as d, VeraniMessage as f, encodeClientMessage as i, MessageFrame as l, decodeFrame as n, encodeServerMessage as o, decodeServerMessage as r, ClientMessage as s, decodeClientMessage as t, PROTOCOL_VERSION as u };
@@ -0,0 +1,87 @@
1
+ //#region src/shared/types.d.ts
2
+ /**
3
+ * Core message types shared between client and server
4
+ */
5
+ /**
6
+ * Base message frame structure used for all WebSocket communication
7
+ */
8
+ interface MessageFrame {
9
+ type: string;
10
+ channel?: string;
11
+ data?: any;
12
+ }
13
+ /**
14
+ * Message sent from client to server
15
+ */
16
+ interface ClientMessage extends MessageFrame {
17
+ type: string;
18
+ channel?: string;
19
+ data?: any;
20
+ }
21
+ /**
22
+ * Message sent from server to client
23
+ */
24
+ interface ServerMessage extends MessageFrame {
25
+ type: string;
26
+ channel?: string;
27
+ data?: any;
28
+ }
29
+ /**
30
+ * Connection metadata attached to each WebSocket
31
+ */
32
+ interface ConnectionMeta {
33
+ userId: string;
34
+ clientId: string;
35
+ channels: string[];
36
+ }
37
+ /**
38
+ * Unified message type for both directions
39
+ */
40
+ type VeraniMessage = ClientMessage | ServerMessage;
41
+ /**
42
+ * Protocol version for future compatibility
43
+ */
44
+ declare const PROTOCOL_VERSION = "1.0.0";
45
+ //#endregion
46
+ //#region src/shared/encode.d.ts
47
+ /**
48
+ * Encodes a message frame to JSON string for transmission
49
+ * @param frame - The message frame to encode
50
+ * @returns JSON string representation of the frame
51
+ * @throws Error if encoding fails
52
+ */
53
+ declare function encodeFrame(frame: MessageFrame): string;
54
+ /**
55
+ * Encodes a client message to JSON string
56
+ * @param message - The client message to encode
57
+ * @returns JSON string representation
58
+ */
59
+ declare function encodeClientMessage(message: MessageFrame): string;
60
+ /**
61
+ * Encodes a server message to JSON string
62
+ * @param message - The server message to encode
63
+ * @returns JSON string representation
64
+ */
65
+ declare function encodeServerMessage(message: MessageFrame): string;
66
+ //#endregion
67
+ //#region src/shared/decode.d.ts
68
+ /**
69
+ * Decodes a raw message into a MessageFrame
70
+ * @param raw - Raw data from WebSocket (string, ArrayBuffer, etc)
71
+ * @returns Decoded MessageFrame or null if invalid
72
+ */
73
+ declare function decodeFrame(raw: any): MessageFrame | null;
74
+ /**
75
+ * Decodes a client message
76
+ * @param raw - Raw data from client WebSocket
77
+ * @returns Decoded message or null if invalid
78
+ */
79
+ declare function decodeClientMessage(raw: any): MessageFrame | null;
80
+ /**
81
+ * Decodes a server message
82
+ * @param raw - Raw data from server WebSocket
83
+ * @returns Decoded message or null if invalid
84
+ */
85
+ declare function decodeServerMessage(raw: any): MessageFrame | null;
86
+ //#endregion
87
+ export { encodeFrame as a, ConnectionMeta as c, ServerMessage as d, VeraniMessage as f, encodeClientMessage as i, MessageFrame as l, decodeFrame as n, encodeServerMessage as o, decodeServerMessage as r, ClientMessage as s, decodeClientMessage as t, PROTOCOL_VERSION as u };
@@ -0,0 +1 @@
1
+ function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(a){try{let o=typeof a==`string`?a:a.toString(),s=JSON.parse(o);return isValidFrame(s)?s:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}const PROTOCOL_VERSION=`1.0.0`;Object.defineProperty(exports,`a`,{enumerable:!0,get:function(){return decodeClientMessage}}),Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return encodeServerMessage}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return encodeClientMessage}}),Object.defineProperty(exports,`o`,{enumerable:!0,get:function(){return decodeFrame}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return encodeFrame}}),Object.defineProperty(exports,`s`,{enumerable:!0,get:function(){return decodeServerMessage}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return`1.0.0`}});
@@ -0,0 +1 @@
1
+ function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(a){try{let o=typeof a==`string`?a:a.toString(),s=JSON.parse(o);return isValidFrame(s)?s:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}const PROTOCOL_VERSION=`1.0.0`;export{decodeClientMessage as a,encodeServerMessage as i,encodeClientMessage as n,decodeFrame as o,encodeFrame as r,decodeServerMessage as s,PROTOCOL_VERSION as t};
package/dist/verani.cjs CHANGED
@@ -1 +1 @@
1
- let __cloudflare_actors=require(`@cloudflare/actors`);function defaultExtractMeta(e){let _=crypto.randomUUID(),v=crypto.randomUUID(),y=new URL(e.url).searchParams.get(`channels`);return{userId:_,clientId:v,channels:y?y.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function storeAttachment(e,_){e.serializeAttachment(_)}function restoreSessions(e){let _=0;for(let v of e.ctx.getWebSockets()){let y=v.deserializeAttachment();y&&(e.sessions.set(v,{ws:v,meta:y}),_++)}}function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(e){try{let _=typeof e==`string`?e:e.toString(),v=JSON.parse(_);return isValidFrame(v)?v:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}function decodeFrame$1(e){return decodeFrame(e)??{type:`invalid`}}function encodeFrame$1(e){return encodeFrame(e)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(_){let v=sanitizeToClassName(_.name||_.websocketPath||`VeraniActor`);class x extends __cloudflare_actors.Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(e){return{locationHint:`me`,sockets:{upgradePath:_.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(e){let v=new URL(e.url),y=e.headers.get(`Upgrade`);return v.pathname===_.websocketPath&&y===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async onInit(){try{restoreSessions(this),_.onHibernationRestore&&this.sessions.size>0&&await _.onHibernationRestore(this)}catch{}}onWebSocketConnect(e,v){try{let b;if(b=_.extractMeta?_.extractMeta(v):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(e,b),this.sessions.set(e,{ws:e,meta:b}),_.onConnect){let v={actor:this,ws:e,meta:b};_.onConnect(v)}}catch(v){if(_.onError)try{_.onError(v,{actor:this,ws:e,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}e.close(1011,`Internal server error`)}}onWebSocketMessage(e,v){let y;try{let b=decodeFrame$1(v);if(y=this.sessions.get(e),!y)return;if(_.onMessage){let v={actor:this,ws:e,meta:y.meta,frame:b};_.onMessage(v,b)}}catch(v){if(_.onError&&y)try{_.onError(v,{actor:this,ws:e,meta:y.meta})}catch{}}}onWebSocketDisconnect(e){try{let v=this.sessions.get(e);if(this.sessions.delete(e),v&&_.onDisconnect){let y={actor:this,ws:e,meta:v.meta};_.onDisconnect(y)}}catch{}}broadcast(e,_,v){let y=0,b=encodeFrame$1({type:`event`,channel:e,data:_});for(let{ws:_,meta:x}of this.sessions.values())if(x.channels.includes(e)&&!(v?.except&&_===v.except)&&!(v?.userIds&&!v.userIds.includes(x.userId))&&!(v?.clientIds&&!v.clientIds.includes(x.clientId)))try{_.send(b),y++}catch{}return y}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:_}of this.sessions.values())e.add(_.userId);return Array.from(e)}getUserSessions(e){let _=[];for(let{ws:v,meta:y}of this.sessions.values())y.userId===e&&_.push(v);return _}sendToUser(e,_,v){let y=0,b=encodeFrame$1({type:`event`,channel:_,data:v});for(let{ws:v,meta:x}of this.sessions.values())if(x.userId===e&&x.channels.includes(_))try{v.send(b),y++}catch{}return y}getStorage(){return this.ctx.storage}}return Object.defineProperty(x,`name`,{value:v,writable:!1,configurable:!0}),x}function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,_){this.config=e,this.onStateChange=_,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,_={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:_.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:_.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:_.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:_.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:_.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:_.maxQueueSize??100,connectionTimeout:_.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,_=>{clearTimeout(e),this.handleClose(_)}),this.ws.addEventListener(`error`,_=>{clearTimeout(e),this.handleError(_)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.emitLifecycleEvent(`open`),this.emitLifecycleEvent(`connected`),this.onOpenCallback?.()}handleMessage(e){let _=decodeServerMessage$1(e.data);if(!_)return;let v=_.type,y=_.data;_.type===`event`&&_.data&&typeof _.data==`object`&&`type`in _.data&&(v=_.data.type,y=_.data);let b=this.listeners.get(v);if(b)for(let e of b)try{e(y)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`close`,e),this.emitLifecycleEvent(`disconnected`,e),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}handleError(e){this.emitLifecycleEvent(`error`,e),this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`error`,e),this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}emitLifecycleEvent(e,_){let v=this.listeners.get(e);if(v)for(let e of v)try{e(_)}catch{}}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,_)=>{this.connectionResolve=e,this.connectionReject=_}),this.connectionPromise)}on(e,_){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(_)}off(e,_){let v=this.listeners.get(e);v&&(v.delete(_),v.size===0&&this.listeners.delete(e))}once(e,_){let v=y=>{this.off(e,v),_(y)};this.on(e,v)}emit(e,_){let v={type:e,data:_};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(v))}catch{this.queueMessage(v)}else this.queueMessage(v)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};const PROTOCOL_VERSION=`1.0.0`;exports.ConnectionManager=ConnectionManager,exports.DEFAULT_RECONNECTION_CONFIG=DEFAULT_RECONNECTION_CONFIG,exports.PROTOCOL_VERSION=`1.0.0`,exports.VeraniClient=VeraniClient,exports.createActorHandler=createActorHandler,exports.decodeClientMessage=decodeClientMessage,exports.decodeFrame=decodeFrame,exports.decodeServerMessage=decodeServerMessage,exports.defineRoom=defineRoom,exports.encodeClientMessage=encodeClientMessage,exports.encodeFrame=encodeFrame,exports.encodeServerMessage=encodeServerMessage,exports.restoreSessions=restoreSessions,exports.storeAttachment=storeAttachment;
1
+ const require_types=require(`./types-083oWz55.cjs`);let __cloudflare_actors=require(`@cloudflare/actors`);function defaultExtractMeta(e){let a=crypto.randomUUID(),o=crypto.randomUUID(),s=new URL(e.url).searchParams.get(`channels`);return{userId:a,clientId:o,channels:s?s.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function storeAttachment(e,a){e.serializeAttachment(a)}function restoreSessions(e){let a=0;for(let o of e.ctx.getWebSockets()){let s=o.deserializeAttachment();s&&(e.sessions.set(o,{ws:o,meta:s}),a++)}}function decodeFrame$1(a){return require_types.o(a)??{type:`invalid`}}function encodeFrame$1(a){return require_types.r(a)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(e){let o=sanitizeToClassName(e.name||e.websocketPath||`VeraniActor`);class s extends __cloudflare_actors.Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(a){return{locationHint:`me`,sockets:{upgradePath:e.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(a){let o=new URL(a.url),s=a.headers.get(`Upgrade`);return o.pathname===e.websocketPath&&s===`websocket`&&await this.shouldUpgradeWebSocket(a)?this.onWebSocketUpgrade(a):this.onRequest(a)}async onInit(){try{restoreSessions(this),e.onHibernationRestore&&this.sessions.size>0&&await e.onHibernationRestore(this)}catch{}}onWebSocketConnect(a,o){try{let s;if(s=e.extractMeta?e.extractMeta(o):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(a,s),this.sessions.set(a,{ws:a,meta:s}),e.onConnect){let o={actor:this,ws:a,meta:s};e.onConnect(o)}}catch(o){if(e.onError)try{e.onError(o,{actor:this,ws:a,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}a.close(1011,`Internal server error`)}}onWebSocketMessage(a,o){let s;try{let c=decodeFrame$1(o);if(s=this.sessions.get(a),!s)return;if(e.onMessage){let o={actor:this,ws:a,meta:s.meta,frame:c};e.onMessage(o,c)}}catch(o){if(e.onError&&s)try{e.onError(o,{actor:this,ws:a,meta:s.meta})}catch{}}}onWebSocketDisconnect(a){try{let o=this.sessions.get(a);if(this.sessions.delete(a),o&&e.onDisconnect){let s={actor:this,ws:a,meta:o.meta};e.onDisconnect(s)}}catch{}}broadcast(e,a,o){let s=0,c=encodeFrame$1({type:`event`,channel:e,data:a});for(let{ws:a,meta:l}of this.sessions.values())if(l.channels.includes(e)&&!(o?.except&&a===o.except)&&!(o?.userIds&&!o.userIds.includes(l.userId))&&!(o?.clientIds&&!o.clientIds.includes(l.clientId)))try{a.send(c),s++}catch{}return s}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:a}of this.sessions.values())e.add(a.userId);return Array.from(e)}getUserSessions(e){let a=[];for(let{ws:o,meta:s}of this.sessions.values())s.userId===e&&a.push(o);return a}sendToUser(e,a,o){let s=0,c=encodeFrame$1({type:`event`,channel:a,data:o});for(let{ws:o,meta:l}of this.sessions.values())if(l.userId===e&&l.channels.includes(a))try{o.send(c),s++}catch{}return s}getStorage(){return this.ctx.storage}}return Object.defineProperty(s,`name`,{value:o,writable:!1,configurable:!0}),s}exports.PROTOCOL_VERSION=require_types.t,exports.createActorHandler=createActorHandler,exports.decodeClientMessage=require_types.a,exports.decodeFrame=require_types.o,exports.decodeServerMessage=require_types.s,exports.defineRoom=defineRoom,exports.encodeClientMessage=require_types.n,exports.encodeFrame=require_types.r,exports.encodeServerMessage=require_types.i,exports.restoreSessions=restoreSessions,exports.storeAttachment=storeAttachment;
package/dist/verani.d.cts CHANGED
@@ -1,52 +1,8 @@
1
+ import { a as encodeFrame, c as ConnectionMeta, d as ServerMessage, f as VeraniMessage, i as encodeClientMessage, l as MessageFrame, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, s as ClientMessage, t as decodeClientMessage, u as PROTOCOL_VERSION } from "./decode-DMA_rBWB.cjs";
1
2
  import { Actor, ActorConfiguration } from "@cloudflare/actors";
2
3
 
3
- //#region src/shared/types.d.ts
4
-
5
- /**
6
- * Core message types shared between client and server
7
- */
8
- /**
9
- * Base message frame structure used for all WebSocket communication
10
- */
11
- interface MessageFrame {
12
- type: string;
13
- channel?: string;
14
- data?: any;
15
- }
16
- /**
17
- * Message sent from client to server
18
- */
19
- interface ClientMessage extends MessageFrame {
20
- type: string;
21
- channel?: string;
22
- data?: any;
23
- }
24
- /**
25
- * Message sent from server to client
26
- */
27
- interface ServerMessage extends MessageFrame {
28
- type: string;
29
- channel?: string;
30
- data?: any;
31
- }
32
- /**
33
- * Connection metadata attached to each WebSocket
34
- */
35
- interface ConnectionMeta {
36
- userId: string;
37
- clientId: string;
38
- channels: string[];
39
- }
40
- /**
41
- * Unified message type for both directions
42
- */
43
- type VeraniMessage = ClientMessage | ServerMessage;
44
- /**
45
- * Protocol version for future compatibility
46
- */
47
- declare const PROTOCOL_VERSION = "1.0.0";
48
- //#endregion
49
4
  //#region src/actor/types.d.ts
5
+
50
6
  /**
51
7
  * Options for broadcasting messages to connections
52
8
  */
@@ -181,250 +137,4 @@ declare function createActorHandler<TMeta extends ConnectionMeta = ConnectionMet
181
137
  declare function storeAttachment(ws: WebSocket, meta: ConnectionMeta): void;
182
138
  declare function restoreSessions(actor: any): void;
183
139
  //#endregion
184
- //#region src/client/connection.d.ts
185
- /**
186
- * Connection state management for Verani client
187
- */
188
- type ConnectionState = "connecting" | "connected" | "disconnected" | "reconnecting" | "error";
189
- interface ReconnectionConfig {
190
- /** Enable automatic reconnection */
191
- enabled: boolean;
192
- /** Maximum number of reconnection attempts (0 = infinite) */
193
- maxAttempts: number;
194
- /** Initial delay in milliseconds */
195
- initialDelay: number;
196
- /** Maximum delay in milliseconds */
197
- maxDelay: number;
198
- /** Backoff multiplier for exponential backoff */
199
- backoffMultiplier: number;
200
- }
201
- declare const DEFAULT_RECONNECTION_CONFIG: ReconnectionConfig;
202
- /**
203
- * Manages WebSocket connection lifecycle and reconnection logic
204
- */
205
- declare class ConnectionManager {
206
- private config;
207
- private onStateChange?;
208
- private state;
209
- private reconnectAttempts;
210
- private reconnectTimer?;
211
- private currentDelay;
212
- constructor(config?: ReconnectionConfig, onStateChange?: ((state: ConnectionState) => void) | undefined);
213
- /**
214
- * Gets the current connection state
215
- */
216
- getState(): ConnectionState;
217
- /**
218
- * Updates the connection state and notifies listeners
219
- */
220
- setState(newState: ConnectionState): void;
221
- /**
222
- * Resets reconnection state (called on successful connection)
223
- */
224
- resetReconnection(): void;
225
- /**
226
- * Schedules a reconnection attempt
227
- */
228
- scheduleReconnect(connectFn: () => void): boolean;
229
- /**
230
- * Cancels any pending reconnection
231
- */
232
- cancelReconnect(): void;
233
- /**
234
- * Clears the reconnect timer
235
- */
236
- private clearReconnectTimer;
237
- /**
238
- * Gets the current reconnection attempt count
239
- */
240
- getReconnectAttempts(): number;
241
- /**
242
- * Gets the next reconnection delay
243
- */
244
- getNextDelay(): number;
245
- /**
246
- * Cleanup method
247
- */
248
- destroy(): void;
249
- }
250
- //#endregion
251
- //#region src/client/client.d.ts
252
- /**
253
- * Client options for configuring the Verani client
254
- */
255
- interface VeraniClientOptions {
256
- /** Reconnection configuration */
257
- reconnection?: Partial<ReconnectionConfig>;
258
- /** Maximum number of messages to queue when disconnected */
259
- maxQueueSize?: number;
260
- /** Connection timeout in milliseconds */
261
- connectionTimeout?: number;
262
- }
263
- /**
264
- * Verani WebSocket client with automatic reconnection and lifecycle management
265
- */
266
- declare class VeraniClient {
267
- private url;
268
- private ws?;
269
- private listeners;
270
- private connectionManager;
271
- private messageQueue;
272
- private options;
273
- private onOpenCallback?;
274
- private onCloseCallback?;
275
- private onErrorCallback?;
276
- private onStateChangeCallback?;
277
- private connectionPromise?;
278
- private connectionResolve?;
279
- private connectionReject?;
280
- /**
281
- * Creates a new Verani client
282
- * @param url - WebSocket URL to connect to
283
- * @param options - Client configuration options
284
- */
285
- constructor(url: string, options?: VeraniClientOptions);
286
- /**
287
- * Establishes WebSocket connection
288
- */
289
- private connect;
290
- /**
291
- * Handles successful WebSocket connection
292
- */
293
- private handleOpen;
294
- /**
295
- * Handles incoming WebSocket messages
296
- */
297
- private handleMessage;
298
- /**
299
- * Handles WebSocket closure
300
- */
301
- private handleClose;
302
- /**
303
- * Handles WebSocket errors
304
- */
305
- private handleError;
306
- /**
307
- * Handles connection errors
308
- */
309
- private handleConnectionError;
310
- /**
311
- * Emits a lifecycle event to registered listeners
312
- */
313
- private emitLifecycleEvent;
314
- /**
315
- * Flushes queued messages when connection is established
316
- */
317
- private flushMessageQueue;
318
- /**
319
- * Gets the current connection state
320
- */
321
- getState(): ConnectionState;
322
- /**
323
- * Checks if the client is currently connected
324
- */
325
- isConnected(): boolean;
326
- /**
327
- * Waits for the connection to be established
328
- * @returns Promise that resolves when connected
329
- */
330
- waitForConnection(): Promise<void>;
331
- /**
332
- * Registers an event listener
333
- * @param event - Event type to listen for
334
- * @param callback - Callback function to invoke when event is received
335
- */
336
- on(event: string, callback: (data: any) => void): void;
337
- /**
338
- * Removes an event listener
339
- * @param event - Event type to remove listener from
340
- * @param callback - Callback function to remove
341
- */
342
- off(event: string, callback: (data: any) => void): void;
343
- /**
344
- * Registers a one-time event listener
345
- * @param event - Event type to listen for
346
- * @param callback - Callback function to invoke once
347
- */
348
- once(event: string, callback: (data: any) => void): void;
349
- /**
350
- * Sends a message to the server
351
- * @param type - Message type
352
- * @param data - Optional message data
353
- */
354
- emit(type: string, data?: any): void;
355
- /**
356
- * Queues a message for sending when connected
357
- */
358
- private queueMessage;
359
- /**
360
- * Registers lifecycle callback for connection open
361
- */
362
- onOpen(callback: () => void): void;
363
- /**
364
- * Registers lifecycle callback for connection close
365
- */
366
- onClose(callback: (event: CloseEvent) => void): void;
367
- /**
368
- * Registers lifecycle callback for connection error
369
- */
370
- onError(callback: (error: Event) => void): void;
371
- /**
372
- * Registers lifecycle callback for state changes
373
- */
374
- onStateChange(callback: (state: ConnectionState) => void): void;
375
- /**
376
- * Manually triggers a reconnection
377
- */
378
- reconnect(): void;
379
- /**
380
- * Closes the connection without reconnecting
381
- */
382
- disconnect(): void;
383
- /**
384
- * Closes the connection and cleans up resources
385
- */
386
- close(): void;
387
- }
388
- //#endregion
389
- //#region src/shared/encode.d.ts
390
- /**
391
- * Encodes a message frame to JSON string for transmission
392
- * @param frame - The message frame to encode
393
- * @returns JSON string representation of the frame
394
- * @throws Error if encoding fails
395
- */
396
- declare function encodeFrame(frame: MessageFrame): string;
397
- /**
398
- * Encodes a client message to JSON string
399
- * @param message - The client message to encode
400
- * @returns JSON string representation
401
- */
402
- declare function encodeClientMessage(message: MessageFrame): string;
403
- /**
404
- * Encodes a server message to JSON string
405
- * @param message - The server message to encode
406
- * @returns JSON string representation
407
- */
408
- declare function encodeServerMessage(message: MessageFrame): string;
409
- //#endregion
410
- //#region src/shared/decode.d.ts
411
- /**
412
- * Decodes a raw message into a MessageFrame
413
- * @param raw - Raw data from WebSocket (string, ArrayBuffer, etc)
414
- * @returns Decoded MessageFrame or null if invalid
415
- */
416
- declare function decodeFrame(raw: any): MessageFrame | null;
417
- /**
418
- * Decodes a client message
419
- * @param raw - Raw data from client WebSocket
420
- * @returns Decoded message or null if invalid
421
- */
422
- declare function decodeClientMessage(raw: any): MessageFrame | null;
423
- /**
424
- * Decodes a server message
425
- * @param raw - Raw data from server WebSocket
426
- * @returns Decoded message or null if invalid
427
- */
428
- declare function decodeServerMessage(raw: any): MessageFrame | null;
429
- //#endregion
430
- export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, ConnectionManager, type ConnectionMeta, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type ReconnectionConfig, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, VeraniClient, type VeraniClientOptions, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
140
+ export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, type ConnectionMeta, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
package/dist/verani.d.mts CHANGED
@@ -1,52 +1,8 @@
1
+ import { a as encodeFrame, c as ConnectionMeta, d as ServerMessage, f as VeraniMessage, i as encodeClientMessage, l as MessageFrame, n as decodeFrame, o as encodeServerMessage, r as decodeServerMessage, s as ClientMessage, t as decodeClientMessage, u as PROTOCOL_VERSION } from "./decode-9DerwlQ1.mjs";
1
2
  import { Actor, ActorConfiguration } from "@cloudflare/actors";
2
3
 
3
- //#region src/shared/types.d.ts
4
-
5
- /**
6
- * Core message types shared between client and server
7
- */
8
- /**
9
- * Base message frame structure used for all WebSocket communication
10
- */
11
- interface MessageFrame {
12
- type: string;
13
- channel?: string;
14
- data?: any;
15
- }
16
- /**
17
- * Message sent from client to server
18
- */
19
- interface ClientMessage extends MessageFrame {
20
- type: string;
21
- channel?: string;
22
- data?: any;
23
- }
24
- /**
25
- * Message sent from server to client
26
- */
27
- interface ServerMessage extends MessageFrame {
28
- type: string;
29
- channel?: string;
30
- data?: any;
31
- }
32
- /**
33
- * Connection metadata attached to each WebSocket
34
- */
35
- interface ConnectionMeta {
36
- userId: string;
37
- clientId: string;
38
- channels: string[];
39
- }
40
- /**
41
- * Unified message type for both directions
42
- */
43
- type VeraniMessage = ClientMessage | ServerMessage;
44
- /**
45
- * Protocol version for future compatibility
46
- */
47
- declare const PROTOCOL_VERSION = "1.0.0";
48
- //#endregion
49
4
  //#region src/actor/types.d.ts
5
+
50
6
  /**
51
7
  * Options for broadcasting messages to connections
52
8
  */
@@ -181,250 +137,4 @@ declare function createActorHandler<TMeta extends ConnectionMeta = ConnectionMet
181
137
  declare function storeAttachment(ws: WebSocket, meta: ConnectionMeta): void;
182
138
  declare function restoreSessions(actor: any): void;
183
139
  //#endregion
184
- //#region src/client/connection.d.ts
185
- /**
186
- * Connection state management for Verani client
187
- */
188
- type ConnectionState = "connecting" | "connected" | "disconnected" | "reconnecting" | "error";
189
- interface ReconnectionConfig {
190
- /** Enable automatic reconnection */
191
- enabled: boolean;
192
- /** Maximum number of reconnection attempts (0 = infinite) */
193
- maxAttempts: number;
194
- /** Initial delay in milliseconds */
195
- initialDelay: number;
196
- /** Maximum delay in milliseconds */
197
- maxDelay: number;
198
- /** Backoff multiplier for exponential backoff */
199
- backoffMultiplier: number;
200
- }
201
- declare const DEFAULT_RECONNECTION_CONFIG: ReconnectionConfig;
202
- /**
203
- * Manages WebSocket connection lifecycle and reconnection logic
204
- */
205
- declare class ConnectionManager {
206
- private config;
207
- private onStateChange?;
208
- private state;
209
- private reconnectAttempts;
210
- private reconnectTimer?;
211
- private currentDelay;
212
- constructor(config?: ReconnectionConfig, onStateChange?: ((state: ConnectionState) => void) | undefined);
213
- /**
214
- * Gets the current connection state
215
- */
216
- getState(): ConnectionState;
217
- /**
218
- * Updates the connection state and notifies listeners
219
- */
220
- setState(newState: ConnectionState): void;
221
- /**
222
- * Resets reconnection state (called on successful connection)
223
- */
224
- resetReconnection(): void;
225
- /**
226
- * Schedules a reconnection attempt
227
- */
228
- scheduleReconnect(connectFn: () => void): boolean;
229
- /**
230
- * Cancels any pending reconnection
231
- */
232
- cancelReconnect(): void;
233
- /**
234
- * Clears the reconnect timer
235
- */
236
- private clearReconnectTimer;
237
- /**
238
- * Gets the current reconnection attempt count
239
- */
240
- getReconnectAttempts(): number;
241
- /**
242
- * Gets the next reconnection delay
243
- */
244
- getNextDelay(): number;
245
- /**
246
- * Cleanup method
247
- */
248
- destroy(): void;
249
- }
250
- //#endregion
251
- //#region src/client/client.d.ts
252
- /**
253
- * Client options for configuring the Verani client
254
- */
255
- interface VeraniClientOptions {
256
- /** Reconnection configuration */
257
- reconnection?: Partial<ReconnectionConfig>;
258
- /** Maximum number of messages to queue when disconnected */
259
- maxQueueSize?: number;
260
- /** Connection timeout in milliseconds */
261
- connectionTimeout?: number;
262
- }
263
- /**
264
- * Verani WebSocket client with automatic reconnection and lifecycle management
265
- */
266
- declare class VeraniClient {
267
- private url;
268
- private ws?;
269
- private listeners;
270
- private connectionManager;
271
- private messageQueue;
272
- private options;
273
- private onOpenCallback?;
274
- private onCloseCallback?;
275
- private onErrorCallback?;
276
- private onStateChangeCallback?;
277
- private connectionPromise?;
278
- private connectionResolve?;
279
- private connectionReject?;
280
- /**
281
- * Creates a new Verani client
282
- * @param url - WebSocket URL to connect to
283
- * @param options - Client configuration options
284
- */
285
- constructor(url: string, options?: VeraniClientOptions);
286
- /**
287
- * Establishes WebSocket connection
288
- */
289
- private connect;
290
- /**
291
- * Handles successful WebSocket connection
292
- */
293
- private handleOpen;
294
- /**
295
- * Handles incoming WebSocket messages
296
- */
297
- private handleMessage;
298
- /**
299
- * Handles WebSocket closure
300
- */
301
- private handleClose;
302
- /**
303
- * Handles WebSocket errors
304
- */
305
- private handleError;
306
- /**
307
- * Handles connection errors
308
- */
309
- private handleConnectionError;
310
- /**
311
- * Emits a lifecycle event to registered listeners
312
- */
313
- private emitLifecycleEvent;
314
- /**
315
- * Flushes queued messages when connection is established
316
- */
317
- private flushMessageQueue;
318
- /**
319
- * Gets the current connection state
320
- */
321
- getState(): ConnectionState;
322
- /**
323
- * Checks if the client is currently connected
324
- */
325
- isConnected(): boolean;
326
- /**
327
- * Waits for the connection to be established
328
- * @returns Promise that resolves when connected
329
- */
330
- waitForConnection(): Promise<void>;
331
- /**
332
- * Registers an event listener
333
- * @param event - Event type to listen for
334
- * @param callback - Callback function to invoke when event is received
335
- */
336
- on(event: string, callback: (data: any) => void): void;
337
- /**
338
- * Removes an event listener
339
- * @param event - Event type to remove listener from
340
- * @param callback - Callback function to remove
341
- */
342
- off(event: string, callback: (data: any) => void): void;
343
- /**
344
- * Registers a one-time event listener
345
- * @param event - Event type to listen for
346
- * @param callback - Callback function to invoke once
347
- */
348
- once(event: string, callback: (data: any) => void): void;
349
- /**
350
- * Sends a message to the server
351
- * @param type - Message type
352
- * @param data - Optional message data
353
- */
354
- emit(type: string, data?: any): void;
355
- /**
356
- * Queues a message for sending when connected
357
- */
358
- private queueMessage;
359
- /**
360
- * Registers lifecycle callback for connection open
361
- */
362
- onOpen(callback: () => void): void;
363
- /**
364
- * Registers lifecycle callback for connection close
365
- */
366
- onClose(callback: (event: CloseEvent) => void): void;
367
- /**
368
- * Registers lifecycle callback for connection error
369
- */
370
- onError(callback: (error: Event) => void): void;
371
- /**
372
- * Registers lifecycle callback for state changes
373
- */
374
- onStateChange(callback: (state: ConnectionState) => void): void;
375
- /**
376
- * Manually triggers a reconnection
377
- */
378
- reconnect(): void;
379
- /**
380
- * Closes the connection without reconnecting
381
- */
382
- disconnect(): void;
383
- /**
384
- * Closes the connection and cleans up resources
385
- */
386
- close(): void;
387
- }
388
- //#endregion
389
- //#region src/shared/encode.d.ts
390
- /**
391
- * Encodes a message frame to JSON string for transmission
392
- * @param frame - The message frame to encode
393
- * @returns JSON string representation of the frame
394
- * @throws Error if encoding fails
395
- */
396
- declare function encodeFrame(frame: MessageFrame): string;
397
- /**
398
- * Encodes a client message to JSON string
399
- * @param message - The client message to encode
400
- * @returns JSON string representation
401
- */
402
- declare function encodeClientMessage(message: MessageFrame): string;
403
- /**
404
- * Encodes a server message to JSON string
405
- * @param message - The server message to encode
406
- * @returns JSON string representation
407
- */
408
- declare function encodeServerMessage(message: MessageFrame): string;
409
- //#endregion
410
- //#region src/shared/decode.d.ts
411
- /**
412
- * Decodes a raw message into a MessageFrame
413
- * @param raw - Raw data from WebSocket (string, ArrayBuffer, etc)
414
- * @returns Decoded MessageFrame or null if invalid
415
- */
416
- declare function decodeFrame(raw: any): MessageFrame | null;
417
- /**
418
- * Decodes a client message
419
- * @param raw - Raw data from client WebSocket
420
- * @returns Decoded message or null if invalid
421
- */
422
- declare function decodeClientMessage(raw: any): MessageFrame | null;
423
- /**
424
- * Decodes a server message
425
- * @param raw - Raw data from server WebSocket
426
- * @returns Decoded message or null if invalid
427
- */
428
- declare function decodeServerMessage(raw: any): MessageFrame | null;
429
- //#endregion
430
- export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, ConnectionManager, type ConnectionMeta, type ConnectionState, DEFAULT_RECONNECTION_CONFIG, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type ReconnectionConfig, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, VeraniClient, type VeraniClientOptions, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
140
+ export { type ActorHandlerClass, type ActorStub, type BroadcastOptions, type ClientMessage, type ConnectionMeta, type MessageContext, type MessageFrame, PROTOCOL_VERSION, type RoomContext, type RoomDefinition, type ServerMessage, type VeraniActor, type VeraniMessage, createActorHandler, decodeClientMessage, decodeFrame, decodeServerMessage, defineRoom, encodeClientMessage, encodeFrame, encodeServerMessage, restoreSessions, storeAttachment };
package/dist/verani.mjs CHANGED
@@ -1 +1 @@
1
- import{Actor}from"@cloudflare/actors";function defaultExtractMeta(e){let _=crypto.randomUUID(),v=crypto.randomUUID(),y=new URL(e.url).searchParams.get(`channels`);return{userId:_,clientId:v,channels:y?y.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function storeAttachment(e,_){e.serializeAttachment(_)}function restoreSessions(e){let _=0;for(let v of e.ctx.getWebSockets()){let y=v.deserializeAttachment();y&&(e.sessions.set(v,{ws:v,meta:y}),_++)}}function isValidFrame(e){return e&&typeof e==`object`&&typeof e.type==`string`&&(e.channel===void 0||typeof e.channel==`string`)}function decodeFrame(e){try{let _=typeof e==`string`?e:e.toString(),v=JSON.parse(_);return isValidFrame(v)?v:null}catch{return null}}function decodeClientMessage(e){return decodeFrame(e)}function decodeServerMessage(e){return decodeFrame(e)}function encodeFrame(e){try{return JSON.stringify(e)}catch(e){throw Error(`Failed to encode frame: ${e instanceof Error?e.message:`unknown error`}`)}}function encodeClientMessage(e){return encodeFrame(e)}function encodeServerMessage(e){return encodeFrame(e)}function decodeFrame$1(e){return decodeFrame(e)??{type:`invalid`}}function encodeFrame$1(e){return encodeFrame(e)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(_){let v=sanitizeToClassName(_.name||_.websocketPath||`VeraniActor`);class x extends Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(e){return{locationHint:`me`,sockets:{upgradePath:_.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(e){let v=new URL(e.url),y=e.headers.get(`Upgrade`);return v.pathname===_.websocketPath&&y===`websocket`&&await this.shouldUpgradeWebSocket(e)?this.onWebSocketUpgrade(e):this.onRequest(e)}async onInit(){try{restoreSessions(this),_.onHibernationRestore&&this.sessions.size>0&&await _.onHibernationRestore(this)}catch{}}onWebSocketConnect(e,v){try{let b;if(b=_.extractMeta?_.extractMeta(v):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(e,b),this.sessions.set(e,{ws:e,meta:b}),_.onConnect){let v={actor:this,ws:e,meta:b};_.onConnect(v)}}catch(v){if(_.onError)try{_.onError(v,{actor:this,ws:e,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}e.close(1011,`Internal server error`)}}onWebSocketMessage(e,v){let y;try{let b=decodeFrame$1(v);if(y=this.sessions.get(e),!y)return;if(_.onMessage){let v={actor:this,ws:e,meta:y.meta,frame:b};_.onMessage(v,b)}}catch(v){if(_.onError&&y)try{_.onError(v,{actor:this,ws:e,meta:y.meta})}catch{}}}onWebSocketDisconnect(e){try{let v=this.sessions.get(e);if(this.sessions.delete(e),v&&_.onDisconnect){let y={actor:this,ws:e,meta:v.meta};_.onDisconnect(y)}}catch{}}broadcast(e,_,v){let y=0,b=encodeFrame$1({type:`event`,channel:e,data:_});for(let{ws:_,meta:x}of this.sessions.values())if(x.channels.includes(e)&&!(v?.except&&_===v.except)&&!(v?.userIds&&!v.userIds.includes(x.userId))&&!(v?.clientIds&&!v.clientIds.includes(x.clientId)))try{_.send(b),y++}catch{}return y}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:_}of this.sessions.values())e.add(_.userId);return Array.from(e)}getUserSessions(e){let _=[];for(let{ws:v,meta:y}of this.sessions.values())y.userId===e&&_.push(v);return _}sendToUser(e,_,v){let y=0,b=encodeFrame$1({type:`event`,channel:_,data:v});for(let{ws:v,meta:x}of this.sessions.values())if(x.userId===e&&x.channels.includes(_))try{v.send(b),y++}catch{}return y}getStorage(){return this.ctx.storage}}return Object.defineProperty(x,`name`,{value:v,writable:!1,configurable:!0}),x}function encodeClientMessage$1(e){return encodeClientMessage(e)}function decodeServerMessage$1(e){return decodeServerMessage(e)}const DEFAULT_RECONNECTION_CONFIG={enabled:!0,maxAttempts:10,initialDelay:1e3,maxDelay:3e4,backoffMultiplier:1.5};var ConnectionManager=class{constructor(e=DEFAULT_RECONNECTION_CONFIG,_){this.config=e,this.onStateChange=_,this.state=`disconnected`,this.reconnectAttempts=0,this.currentDelay=e.initialDelay}getState(){return this.state}setState(e){this.state!==e&&(this.state=e,this.onStateChange?.(e))}resetReconnection(){this.reconnectAttempts=0,this.currentDelay=this.config.initialDelay,this.clearReconnectTimer()}scheduleReconnect(e){return this.config.enabled?this.config.maxAttempts>0&&this.reconnectAttempts>=this.config.maxAttempts?(this.setState(`error`),!1):(this.clearReconnectTimer(),this.setState(`reconnecting`),this.reconnectAttempts++,this.reconnectTimer=setTimeout(()=>{e(),this.currentDelay=Math.min(this.currentDelay*this.config.backoffMultiplier,this.config.maxDelay)},this.currentDelay),!0):!1}cancelReconnect(){this.clearReconnectTimer(),this.state===`reconnecting`&&this.setState(`disconnected`)}clearReconnectTimer(){this.reconnectTimer!==void 0&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=void 0)}getReconnectAttempts(){return this.reconnectAttempts}getNextDelay(){return this.currentDelay}destroy(){this.clearReconnectTimer()}},VeraniClient=class{constructor(e,_={}){this.url=e,this.listeners=new Map,this.messageQueue=[],this.options={reconnection:{enabled:_.reconnection?.enabled??DEFAULT_RECONNECTION_CONFIG.enabled,maxAttempts:_.reconnection?.maxAttempts??DEFAULT_RECONNECTION_CONFIG.maxAttempts,initialDelay:_.reconnection?.initialDelay??DEFAULT_RECONNECTION_CONFIG.initialDelay,maxDelay:_.reconnection?.maxDelay??DEFAULT_RECONNECTION_CONFIG.maxDelay,backoffMultiplier:_.reconnection?.backoffMultiplier??DEFAULT_RECONNECTION_CONFIG.backoffMultiplier},maxQueueSize:_.maxQueueSize??100,connectionTimeout:_.connectionTimeout??1e4},this.connectionManager=new ConnectionManager(this.options.reconnection,e=>{this.onStateChangeCallback?.(e)}),this.connect()}connect(){try{this.connectionManager.setState(`connecting`),this.emitLifecycleEvent(`connecting`),this.ws=new WebSocket(this.url);let e=setTimeout(()=>{this.connectionManager.getState()===`connecting`&&(this.ws?.close(),this.handleConnectionError(Error(`Connection timeout`)))},this.options.connectionTimeout);this.ws.addEventListener(`open`,()=>{clearTimeout(e),this.handleOpen()}),this.ws.addEventListener(`message`,e=>{this.handleMessage(e)}),this.ws.addEventListener(`close`,_=>{clearTimeout(e),this.handleClose(_)}),this.ws.addEventListener(`error`,_=>{clearTimeout(e),this.handleError(_)})}catch(e){this.handleConnectionError(e)}}handleOpen(){this.connectionManager.setState(`connected`),this.connectionManager.resetReconnection(),this.flushMessageQueue(),this.connectionResolve&&(this.connectionResolve(),this.connectionPromise=void 0,this.connectionResolve=void 0,this.connectionReject=void 0),this.emitLifecycleEvent(`open`),this.emitLifecycleEvent(`connected`),this.onOpenCallback?.()}handleMessage(e){let _=decodeServerMessage$1(e.data);if(!_)return;let v=_.type,y=_.data;_.type===`event`&&_.data&&typeof _.data==`object`&&`type`in _.data&&(v=_.data.type,y=_.data);let b=this.listeners.get(v);if(b)for(let e of b)try{e(y)}catch{}}handleClose(e){this.connectionManager.setState(`disconnected`),this.connectionReject&&=(this.connectionReject(Error(`Connection closed: ${e.reason||`Unknown reason`}`)),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`close`,e),this.emitLifecycleEvent(`disconnected`,e),this.onCloseCallback?.(e),e.code!==1e3&&e.code!==1001&&this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}handleError(e){this.emitLifecycleEvent(`error`,e),this.onErrorCallback?.(e)}handleConnectionError(e){this.connectionReject&&=(this.connectionReject(e),this.connectionPromise=void 0,this.connectionResolve=void 0,void 0),this.emitLifecycleEvent(`error`,e),this.connectionManager.scheduleReconnect(()=>this.connect())&&this.emitLifecycleEvent(`reconnecting`)}emitLifecycleEvent(e,_){let v=this.listeners.get(e);if(v)for(let e of v)try{e(_)}catch{}}flushMessageQueue(){if(!(!this.ws||this.ws.readyState!==WebSocket.OPEN))for(;this.messageQueue.length>0;){let e=this.messageQueue.shift();try{this.ws.send(encodeClientMessage$1(e))}catch{}}}getState(){return this.connectionManager.getState()}isConnected(){return this.ws?.readyState===WebSocket.OPEN}waitForConnection(){return this.isConnected()?Promise.resolve():(this.connectionPromise||=new Promise((e,_)=>{this.connectionResolve=e,this.connectionReject=_}),this.connectionPromise)}on(e,_){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(_)}off(e,_){let v=this.listeners.get(e);v&&(v.delete(_),v.size===0&&this.listeners.delete(e))}once(e,_){let v=y=>{this.off(e,v),_(y)};this.on(e,v)}emit(e,_){let v={type:e,data:_};if(this.isConnected())try{this.ws.send(encodeClientMessage$1(v))}catch{this.queueMessage(v)}else this.queueMessage(v)}queueMessage(e){this.messageQueue.length>=this.options.maxQueueSize&&this.messageQueue.shift(),this.messageQueue.push(e)}onOpen(e){this.onOpenCallback=e}onClose(e){this.onCloseCallback=e}onError(e){this.onErrorCallback=e}onStateChange(e){this.onStateChangeCallback=e}reconnect(){this.disconnect(),this.connect()}disconnect(){this.connectionManager.cancelReconnect(),this.ws&&=(this.ws.close(1e3,`Client disconnect`),void 0)}close(){this.disconnect(),this.listeners.clear(),this.messageQueue=[],this.connectionManager.destroy()}};const PROTOCOL_VERSION=`1.0.0`;export{ConnectionManager,DEFAULT_RECONNECTION_CONFIG,PROTOCOL_VERSION,VeraniClient,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,encodeClientMessage,encodeFrame,encodeServerMessage,restoreSessions,storeAttachment};
1
+ import{a as decodeClientMessage,i as encodeServerMessage,n as encodeClientMessage,o as decodeFrame,r as encodeFrame,s as decodeServerMessage,t as PROTOCOL_VERSION}from"./types-CJLnZrA8.mjs";import{Actor}from"@cloudflare/actors";function defaultExtractMeta(e){let d=crypto.randomUUID(),f=crypto.randomUUID(),p=new URL(e.url).searchParams.get(`channels`);return{userId:d,clientId:f,channels:p?p.split(`,`).map(e=>e.trim()).filter(Boolean):[`default`]}}function defineRoom(e){return{name:e.name,websocketPath:e.websocketPath,extractMeta:e.extractMeta||defaultExtractMeta,onConnect:e.onConnect,onDisconnect:e.onDisconnect,onMessage:e.onMessage,onError:e.onError}}function storeAttachment(e,d){e.serializeAttachment(d)}function restoreSessions(e){let d=0;for(let f of e.ctx.getWebSockets()){let p=f.deserializeAttachment();p&&(e.sessions.set(f,{ws:f,meta:p}),d++)}}function decodeFrame$1(e){return decodeFrame(e)??{type:`invalid`}}function encodeFrame$1(e){return encodeFrame(e)}function sanitizeToClassName(e){return e.replace(/^\/+/,``).split(/[-_\/\s]+/).map(e=>e.replace(/[^a-zA-Z0-9]/g,``)).filter(e=>e.length>0).map(e=>e.charAt(0).toUpperCase()+e.slice(1).toLowerCase()).join(``)||`VeraniActor`}function createActorHandler(e){let d=sanitizeToClassName(e.name||e.websocketPath||`VeraniActor`);class f extends Actor{constructor(...e){super(...e),this.sessions=new Map}static configuration(d){return{locationHint:`me`,sockets:{upgradePath:e.websocketPath,autoResponse:{ping:`ping`,pong:`pong`}}}}async shouldUpgradeWebSocket(e){return!0}async fetch(d){let f=new URL(d.url),p=d.headers.get(`Upgrade`);return f.pathname===e.websocketPath&&p===`websocket`&&await this.shouldUpgradeWebSocket(d)?this.onWebSocketUpgrade(d):this.onRequest(d)}async onInit(){try{restoreSessions(this),e.onHibernationRestore&&this.sessions.size>0&&await e.onHibernationRestore(this)}catch{}}onWebSocketConnect(d,f){try{let p;if(p=e.extractMeta?e.extractMeta(f):{userId:`anonymous`,clientId:crypto.randomUUID(),channels:[`default`]},storeAttachment(d,p),this.sessions.set(d,{ws:d,meta:p}),e.onConnect){let f={actor:this,ws:d,meta:p};e.onConnect(f)}}catch(f){if(e.onError)try{e.onError(f,{actor:this,ws:d,meta:{userId:`unknown`,clientId:`unknown`,channels:[]}})}catch{}d.close(1011,`Internal server error`)}}onWebSocketMessage(d,f){let p;try{let m=decodeFrame$1(f);if(p=this.sessions.get(d),!p)return;if(e.onMessage){let f={actor:this,ws:d,meta:p.meta,frame:m};e.onMessage(f,m)}}catch(f){if(e.onError&&p)try{e.onError(f,{actor:this,ws:d,meta:p.meta})}catch{}}}onWebSocketDisconnect(d){try{let f=this.sessions.get(d);if(this.sessions.delete(d),f&&e.onDisconnect){let p={actor:this,ws:d,meta:f.meta};e.onDisconnect(p)}}catch{}}broadcast(e,d,f){let p=0,m=encodeFrame$1({type:`event`,channel:e,data:d});for(let{ws:d,meta:h}of this.sessions.values())if(h.channels.includes(e)&&!(f?.except&&d===f.except)&&!(f?.userIds&&!f.userIds.includes(h.userId))&&!(f?.clientIds&&!f.clientIds.includes(h.clientId)))try{d.send(m),p++}catch{}return p}getSessionCount(){return this.sessions.size}getConnectedUserIds(){let e=new Set;for(let{meta:d}of this.sessions.values())e.add(d.userId);return Array.from(e)}getUserSessions(e){let d=[];for(let{ws:f,meta:p}of this.sessions.values())p.userId===e&&d.push(f);return d}sendToUser(e,d,f){let p=0,m=encodeFrame$1({type:`event`,channel:d,data:f});for(let{ws:f,meta:h}of this.sessions.values())if(h.userId===e&&h.channels.includes(d))try{f.send(m),p++}catch{}return p}getStorage(){return this.ctx.storage}}return Object.defineProperty(f,`name`,{value:d,writable:!1,configurable:!0}),f}export{PROTOCOL_VERSION,createActorHandler,decodeClientMessage,decodeFrame,decodeServerMessage,defineRoom,encodeClientMessage,encodeFrame,encodeServerMessage,restoreSessions,storeAttachment};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "verani",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "A simple, focused realtime SDK for Cloudflare Actors with Socket.io-like semantics",
5
5
  "license": "ISC",
6
6
  "keywords": [
@@ -24,6 +24,16 @@
24
24
  "types": "./dist/verani.d.cts",
25
25
  "default": "./dist/verani.cjs"
26
26
  }
27
+ },
28
+ "./client": {
29
+ "import": {
30
+ "types": "./dist/client.d.mts",
31
+ "default": "./dist/client.mjs"
32
+ },
33
+ "require": {
34
+ "types": "./dist/client.d.cts",
35
+ "default": "./dist/client.cjs"
36
+ }
27
37
  }
28
38
  },
29
39
  "files": [