playsocketjs 2.6.0 → 2.6.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.
package/README.md CHANGED
@@ -7,7 +7,7 @@ A reactive, optimistic WebSocket library that simplifies game & app development
7
7
  PlaySocket eliminates the traditional complexity of collaborative experiences:
8
8
 
9
9
  - **Streamlined architecture**: No additional backend code is required, but server-authoritative behavior supported
10
- - **State synchronization**: Built-in storage system keeps the full state synchronized across all clients, always conflict-free and in order
10
+ - **State synchronization**: Built-in storage system keeps the full state synchronized across all clients, always conflict-free and in order
11
11
  - **Resilient & secure connections**: Automatic reconnection handling & strict rate-limiting
12
12
  - **Lightweight**: Uses WebSockets for efficient, predictable, reliable communication and has little dependencies
13
13
 
@@ -19,7 +19,7 @@ npm install playsocketjs
19
19
 
20
20
  ## Usage examples
21
21
 
22
- Note that in production, you should **always try...catch** promises, such as socket.init() – they can reject!
22
+ Note that in production, you should **always try...catch** promises, such as socket.init() – they can reject!
23
23
 
24
24
  Initializing the client:
25
25
  ```javascript
@@ -77,7 +77,7 @@ Sending traditional requests to the server:
77
77
  socket.sendRequest('chosen-request-name', { fact: "You can build server-authoritative logic using this!" })
78
78
  ```
79
79
 
80
- ## API Reference
80
+ ## API reference
81
81
 
82
82
  ### Constructor
83
83
 
@@ -89,36 +89,45 @@ new PlaySocket(id?: string, options: PlaySocketOptions)
89
89
  ```
90
90
 
91
91
  #### Configuration options
92
- - `endpoint`: WebSocket server endpoint (e.g., 'wss://example.com/socket')
93
- - `customData`: You can pass arbitrary data to the "clientRegistered" server event (optional)
94
- - `debug`: Set this property to true to enable extra logging
95
92
 
96
- ### Methods
97
-
98
- - `init()`: Initialize the WebSocket connection – Returns Promise (async) which resolves with the client's ID
99
- - `createRoom(initialStorage?: object, size?: number)`: Create a new room and become host – Returns Promise (async) which resolves with the room ID. The room participant maximum is 100
100
- - `joinRoom(hostId: string)`: Join an existing room – Returns Promise (async)
101
- - `destroy()`: Use this to leave a room and close the connection
102
- - `updateStorage(key: string, type: 'set' | 'array-add' | 'array-add-unique' | 'array-remove-matching' | 'array-update-matching', value: any, updateValue?: any)`: Update a key in the shared storage (max. 100 keys). Array operation types allow for conflict-free simultaneous array updates. For '-matching' operations, value becomes the value to match, and updateValue the replacement.
103
- - `sendRequest(name: string, data?: any)`: Send requests to the server with optional custom data (handle these in the `requestReceived` server event)
104
- - `onEvent(event: string, callback: Function)`: Register an event callback
93
+ | Option | Type | Default | Description |
94
+ |--------|------|---------|-------------|
95
+ | `endpoint` | string | `none - set this!` | WebSocket server endpoint (e.g., 'wss://example.com/socket') |
96
+ | `customData` | object | `{}` | Arbitrary data to pass to the "clientRegistered" server event (optional) |
97
+ | `debug` | boolean | `false` | Set to true to enable extra logging |
105
98
 
106
- #### Event types
99
+ ### Methods
107
100
 
108
- - `status`: Connection status updates (returns status `string`)
109
- - `error`: Error events (returns error `string`)
110
- - `instanceDestroyed`: Destruction event - triggered by manual .destroy() method invocation or by fatal errors and disconnects
111
- - `storageUpdated`: Storage state changes (returns storage `object`)
112
- - `hostMigrated`: Host changes (returns the new host's ID `string`)
113
- - `clientConnected`: New client connected to the room (returns client's ID `string`)
114
- - `clientDisconnected`: Client disconnected from the room (returns client's ID `string`, room ID (if available) `string`)
101
+ | Method | Parameters | Return type | Description |
102
+ |--------|------------|-------------|-------------|
103
+ | `init()` | - | `Promise<string>` | Initialize the WebSocket connection – Returns Promise which resolves with the client's ID |
104
+ | `createRoom()` | `initialStorage?: object, size?: number` | `Promise<string>` | Create a new room and become host – Returns Promise which resolves with the room ID. The room participant maximum is 100 |
105
+ | `joinRoom()` | `roomId: string` | `Promise<void>` | Join an existing room |
106
+ | `destroy()` | - | `void` | Use this to leave a room and close the connection |
107
+ | `updateStorage()` | `key: string, type: 'set' \| 'array-add' \| 'array-add-unique' \| 'array-remove-matching' \| 'array-update-matching', value: any, updateValue?: any` | `void` | Update a key in the shared storage (max. 100 keys). Array operation types allow for conflict-free simultaneous array updates. For '-matching' operations, value becomes the value to match, and updateValue the replacement |
108
+ | `sendRequest()` | `name: string, data?: any` | `void` | Send requests to the server with optional custom data (handle these in the `requestReceived` server event) |
109
+ | `onEvent()` | `event: string, callback: Function` | `void` | Register an event callback |
110
+
111
+ ### Event types
112
+
113
+ | Event | Callback parameter | Description |
114
+ |-------|-------------------|-------------|
115
+ | `status` | `status: string` | Connection status updates |
116
+ | `error` | `error: string` | Error events |
117
+ | `instanceDestroyed` | - | Destruction event - triggered by manual .destroy() method invocation or by fatal errors and disconnects |
118
+ | `storageUpdated` | `storage: object` | Storage state changes |
119
+ | `hostMigrated` | `roomId: string` | Host changes |
120
+ | `clientConnected` | `clientId: string` | New client connected to the room |
121
+ | `clientDisconnected` | `clientId: string, roomId?: string` | Client disconnected from the room |
115
122
 
116
123
  ### Properties (Read-only)
117
124
 
118
- - `id`: Client's unique identifier on the WebSocket server
119
- - `isHost`: If this user is currently assigned the host role
120
- - `connectionCount`: Number of active client connections in room (without yourself)
121
- - `getStorage`: Retrieve storage object
125
+ | Property | Type | Description |
126
+ |----------|------|-------------|
127
+ | `id` | string | Client's unique identifier on the WebSocket server |
128
+ | `isHost` | boolean | If this user is currently assigned the host role |
129
+ | `connectionCount` | number | Number of active client connections in room (without yourself) |
130
+ | `getStorage` | object | Retrieve storage object |
122
131
 
123
132
  &nbsp;
124
133
 
@@ -188,51 +197,59 @@ process.on('SIGINT', shutdown);
188
197
  process.on('SIGTERM', shutdown);
189
198
  ```
190
199
 
191
- ## API Reference
200
+ ## API reference
192
201
 
193
202
  ### Constructor
194
203
 
204
+ Creates a new PlaySocket Server instance with configuration options.
205
+
195
206
  ```javascript
196
207
  new PlaySocket(options: PlaySocketServerOptions)
197
208
  ```
198
209
 
199
- Creates a new PlaySocket Server instance with configuration options.
200
-
201
210
  ### Configuration options
202
211
 
203
- - `port`: Port to listen on (default: 3000, used only if no server provided)
204
- - `path`: WebSocket endpoint path (default: '/')
205
- - `server`: Existing http server (optional)
206
- - `rateLimit`: Adjust the messages/second rate limit (defaults to 20)
207
- - `debug`: Set this property to true to enable extra logging
212
+ | Option | Type | Default | Description |
213
+ |--------|------|---------|-------------|
214
+ | `port` | number | 3000 | Port to listen on (used only if no server provided) |
215
+ | `path` | string | '/' | WebSocket endpoint path |
216
+ | `server` | http.Server | - | Existing http server (optional) |
217
+ | `rateLimit` | number | 20 | Adjust the messages/second rate limit |
218
+ | `debug` | boolean | false | Set to true to enable extra logging |
208
219
 
209
220
  ### Methods
210
221
 
211
- - `stop`: Closes all active client connections, the websocket server and the underlying http server if it's standalone
212
- - `kick(clientId: string, reason?: string)`: Kick a client by their clientID – this will close their connection and set an error message
213
- - `onEvent(event: string, callback: Function)`: Register a server-side event callback
214
- - `getRoomStorage(roomId: string)`: Get a snapshot of the current room storage (returns storage `object`)
215
- - `updateRoomStorage(roomId: string, key: string, type: 'set' | 'array-add' | 'array-add-unique' | 'array-remove-matching' | 'array-update-matching', value: any, updateValue?: any)`: Update a key in the shared room storage from the server.
216
- - `createRoom(initialStorage?: object, size?: number, host?: string)`: Create a room (returns `object` containing room ID and state) – Rooms created with a non-player host like "server" (default) will not be deleted when the last participant leaves
217
- - `destroyRoom(roomId: string)`: Destroy a room & kick all participants
218
-
219
- #### Event types
220
-
221
- - `clientRegistered`: Client registered with the server (returns the client's ID `string`, customData `object`)
222
- - `clientRegistrationRequested`: Client requests to register (returns the client's ID `string`, customData `object`) – return `false` or a rejection reason `string` to block the registration
223
- - `clientDisconnected`: Client disconnected from the server (returns the client's ID `string`)
224
- - `clientJoinedRoom`: Client joined a room – note that clients can only leave by disconnecting (returns the client's ID `string`, room ID `string`)
225
- - `clientJoinRequested`: Client requests to join a room (returns the client's ID `string`, room ID `string`) – return `false` or a rejection reason `string` to block them from joining
226
- - `roomCreated`: Client created a room (returns room ID `string`)
227
- - `roomDestroyed`: Room was destroyed, this happens when all participants leave (returns room ID `string`)
228
- - `roomCreationRequested`: Room creation requested by client (returns `object` containing the client's ID `string` and the initialStorage `object`) – if you return an `object` in the callback, it will take that as the initial storage instead. If you return `false`, the creation will be denied
229
- - `storageUpdated`: Room storage property updated (returns `object` containing the ID of the client who requested the update `string`, room ID `string`, the update `object`, and the storage `object`)
230
- - `storageUpdateRequested`: Room storage property update requested by client (returns `object` containing the client's ID `string`, room ID `string`, the update `object`, and the current storage `object` that hasn't been updated yet) – if you return `false` in the callback, the update will be blocked
231
- - `requestReceived`: Request from client was received by the server (returns `object` containing client's ID `string`, if in room – room ID `string`, request name `string` and optional passed data of type `any`)
222
+ | Method | Parameters | Return type | Description |
223
+ |--------|------------|-------------|-------------|
224
+ | `stop()` | - | `void` | Closes all active client connections, the websocket server and the underlying http server if it's standalone |
225
+ | `kick()` | `clientId: string, reason?: string` | `void` | Kick a client by their clientID – this will close their connection and set an error message |
226
+ | `onEvent()` | `event: string, callback: Function` | `void` | Register a server-side event callback |
227
+ | `getRoomStorage()` | `roomId: string` | `object` | Get a snapshot of the current room storage |
228
+ | `updateRoomStorage()` | `roomId: string, key: string, type: 'set' \| 'array-add' \| 'array-add-unique' \| 'array-remove-matching' \| 'array-update-matching', value: any, updateValue?: any` | `void` | Update a key in the shared room storage from the server |
229
+ | `createRoom()` | `initialStorage?: object, size?: number, host?: string` | `Promise<object>` | Create a room (returns object containing room ID and state) – Rooms created with a non-player host like "server" (default) will not be deleted when the last participant leaves |
230
+ | `destroyRoom()` | `roomId: string` | `void` | Destroy a room & kick all participants |
231
+
232
+ ### Event types
233
+
234
+ | Event | Callback parameters | Description | Return for action |
235
+ |-------|-------------------|-------------|--------------|
236
+ | `clientRegistered` | `clientId: string, customData: object` | Client registered with the server | - |
237
+ | `clientRegistrationRequested` | `clientId: string, customData: object` | Client requests to register | Return `false` or rejection reason `string` to block |
238
+ | `clientDisconnected` | `clientId: string` | Client disconnected from the server | - |
239
+ | `clientJoinedRoom` | `clientId: string, roomId: string` | Client joined a room (clients can only leave by disconnecting) | - |
240
+ | `clientJoinRequested` | `clientId: string, roomId: string` | Client requests to join a room | Return `false` or rejection reason `string` to block |
241
+ | `roomCreated` | `roomId: string` | Client created a room | - |
242
+ | `roomDestroyed` | `roomId: string` | Room was destroyed (happens when all participants leave) | - |
243
+ | `roomCreationRequested` | `{clientId: string, initialStorage: object}` | Room creation requested by client | Return `object` to override initial storage, `false` to deny |
244
+ | `storageUpdated` | `{clientId: string, roomId: string, update: object, storage: object}` | Room storage property updated | - |
245
+ | `storageUpdateRequested` | `{clientId: string, roomId: string, update: object, storage: object}` | Room storage property update requested by client | Return `false` to block the update |
246
+ | `requestReceived` | `{clientId: string, roomId?: string, requestName: string, data?: any}` | Request from client was received by the server | - |
232
247
 
233
248
  ### Properties (Read-only)
234
249
 
235
- - `getRooms`: Retrieve the rooms object
250
+ | Property | Type | Description |
251
+ |----------|------|-------------|
252
+ | `getRooms` | object | Retrieve the rooms object |
236
253
 
237
254
  # License
238
255
 
@@ -2083,6 +2083,8 @@ class CRDTManager {
2083
2083
 
2084
2084
  /* eslint-disable jsdoc/require-returns */
2085
2085
 
2086
+ const MAX_ROOM_SIZE = 500;
2087
+
2086
2088
  /**
2087
2089
  * @typedef {import('node:http').Server} HttpServer
2088
2090
  * @typedef {import('ws').WebSocket} WebSocket
@@ -2282,25 +2284,16 @@ class PlaySocketServer {
2282
2284
  return;
2283
2285
  }
2284
2286
 
2285
- // Event callback with potential initial storage modifications
2286
- const reviewedStorage = await this.#triggerEvent("roomCreationRequested", { clientId: ws.clientId, initialStorage: structuredClone({ ...data.initialStorage }) });
2287
- if (typeof reviewedStorage === 'object') data.initialStorage = reviewedStorage;
2288
- if (reviewedStorage === false) {
2289
- ws.send(encode({
2290
- type: 'room_creation_failed',
2291
- reason: 'Room creation denied.'
2292
- }), { binary: true });
2293
- return;
2294
- }
2287
+ try {
2288
+ const newRoom = await this.createRoom(data.initialStorage, data.size, ws.clientId);
2295
2289
 
2296
- // Check if client/creator is still connected - abort if not
2297
- if (!this.#clients.has(ws.clientId)) {
2298
- if (this.#debug) console.log(`Room creation cancelled - client ${ws.clientId} disconnected during event callback.`);
2299
- return;
2300
- }
2290
+ // Check if client/creator is still connected - abort if not
2291
+ if (!this.#clients.has(ws.clientId)) {
2292
+ this.destroyRoom(newRoom.id);
2293
+ if (this.#debug) console.log(`Room creation cancelled - client ${ws.clientId} disconnected during event callback.`);
2294
+ return;
2295
+ }
2301
2296
 
2302
- try {
2303
- const newRoom = this.createRoom(data.initialStorage, data.size, ws.clientId);
2304
2297
  this.#rooms[newRoom.id].participants.push(ws.clientId); // Add client to the room
2305
2298
  this.#clientRooms.set(ws.clientId, newRoom.id); // Add client to the client-room map
2306
2299
  ws.send(encode({ type: 'room_created', state: newRoom.state, roomId: newRoom.id }), { binary: true });
@@ -2638,12 +2631,18 @@ class PlaySocketServer {
2638
2631
 
2639
2632
  /**
2640
2633
  * Create a room from the server
2634
+ * @async
2641
2635
  * @param {object} [initialStorage] - Optional initial storage object
2642
- * @param {number} [size] - Max. room size, up to 100 (defaults to 100)
2636
+ * @param {number} [size] - Max. room size, up to 500
2643
2637
  * @param {string} [host] - Host ID, defaults to "server" (when set to "server", room will not be deleted if all clients leave)
2644
- * @returns {object} - Object containing room state and room ID
2638
+ * @returns {Promise<object>} Object containing room state and room ID
2645
2639
  */
2646
- createRoom(initialStorage, size, host = "server") {
2640
+ async createRoom(initialStorage, size, host = "server") {
2641
+ // Event callback with potential initial storage modifications
2642
+ const reviewedStorage = await this.#triggerEvent("roomCreationRequested", { clientId: host, initialStorage: structuredClone({ ...initialStorage }) });
2643
+ if (typeof reviewedStorage === 'object') initialStorage = reviewedStorage;
2644
+ if (reviewedStorage === false) throw new Error("Room creation denied.");
2645
+
2647
2646
  let newRoomId;
2648
2647
 
2649
2648
  for (let i = 0; i < 100; i++) {
@@ -2665,7 +2664,7 @@ class PlaySocketServer {
2665
2664
  this.#rooms[newRoomId] = {
2666
2665
  participants: [],
2667
2666
  host,
2668
- size: Math.min(Number(size), 100) || 100,
2667
+ size: Math.min(Number(size), MAX_ROOM_SIZE) || MAX_ROOM_SIZE,
2669
2668
  crdtManager: roomCrdtManager
2670
2669
  };
2671
2670
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playsocketjs",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "description": "WebSocket wrapper for creating simple multiplayer systems with ease.",
5
5
  "type": "module",
6
6
  "module": "dist/playsocket-client.js",