room-kit 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -61,7 +61,10 @@ export const chatRoom = defineRoomType<{
61
61
  rpc: {
62
62
  sendMessage: (input: { text: string }): Promise<{ id: string }>;
63
63
  };
64
- }>({ name: "chat", presence: "list" });
64
+ }>({
65
+ name: "chat", // namespace
66
+ presence: "list" // allow clients to list room members
67
+ });
65
68
  ```
66
69
 
67
70
  ```ts
@@ -155,13 +158,23 @@ const joined = await chatClient.join({
155
158
  });
156
159
 
157
160
  // Event payload and metadata are both inferred from the room schema.
158
- joined.on.message((payload, meta) => {
159
- // meta.source.kind is "server" or "member".
160
- console.log(payload.text, meta.source.kind);
161
+ const cleanup = joined.listen({
162
+ events: {
163
+ message: (payload, meta) => {
164
+ // meta.source.kind is "server" or "member".
165
+ console.log(payload.text, meta.source.kind);
166
+ },
167
+ },
168
+ presence: {
169
+ onChange: (presence) => {
170
+ console.log(`${presence.count} members online`);
171
+ },
172
+ },
161
173
  });
162
174
 
163
175
  // Fully typed request/response based on your room definition.
164
176
  await joined.rpc.sendMessage({ text: "hello" });
177
+ cleanup();
165
178
  // Cleanly leave the room when you're done.
166
179
  await joined.leave();
167
180
  ```
@@ -188,7 +201,7 @@ Runtime presence mode is configured in the `defineRoomType` options:
188
201
 
189
202
  `serveRoomType(socket, roomType, handlers, adapter?)` accepts:
190
203
 
191
- - `onAuth(socket)`: optional unless you type a non-`unknown` auth context.
204
+ - `onAuth(socket)`: optional unless you type a non-`unknown` auth context. Return `false` to reject the socket before room initialization.
192
205
  - `onConnect(socket, auth)`: optional transport-connect hook attempted once when the socket handler is attached (after auth resolution).
193
206
  - `revalidateAuth(socket, auth)`: optional per-request auth validation hook; return `{ kind: "ok", auth? }` to continue or `{ kind: "reject" }` to deny.
194
207
  - `initState(joinRequest)`: initializes room server state on first join for a given room instance.
@@ -212,11 +225,11 @@ Server context (`ctx`) includes:
212
225
 
213
226
  `serveRoomType` returns a handle:
214
227
 
215
- - `stop()` unregisters listeners for that socket
216
- - `stop.rooms()` returns snapshots for all rooms on the namespace
217
- - `stop.room(roomId)` returns one room snapshot or `undefined`
218
- - `stop.count(roomId)` returns the current member count for a room (`0` when the room does not exist; throws when room presence mode is `"none"`)
219
- - `stop.members(roomId, query)` returns a paginated presence listing (`{ count: 0, offset: 0, limit: 0, members: [] }` when the room does not exist; throws when room presence mode is not `"list"`)
228
+ - `handle.cleanup()` unregisters listeners for that socket
229
+ - `handle.rooms()` returns snapshots for all rooms on the namespace
230
+ - `handle.room(roomId)` returns one room snapshot or `undefined`
231
+ - `handle.count(roomId)` returns the current member count for a room (`0` when the room does not exist; throws when room presence mode is `"none"`)
232
+ - `handle.members(roomId, query)` returns a paginated presence listing (`{ count: 0, offset: 0, limit: 0, members: [] }` when the room does not exist; throws when room presence mode is not `"list"`)
220
233
 
221
234
  ## Client API
222
235
 
@@ -233,6 +246,7 @@ Server context (`ctx`) includes:
233
246
  - `joinedRoom.rpc.<name>(...args)` for typed RPC calls
234
247
  - `joinedRoom.emit.<event>(payload)` for client-emitted room events
235
248
  - `joinedRoom.on.<event>((payload, meta) => {})` for room event subscriptions
249
+ - `joinedRoom.listen({ events, presence })` batches event and presence subscriptions and returns one cleanup function
236
250
  - `joinedRoom.leave()` to leave the room and unregister the joined-room handle
237
251
  - `joinedRoom.presence` is part of the typed API when room presence mode is `"count"` or `"list"`; it exposes `current`, `onChange(handler)`, `count()`, and `list({ offset, limit })` when presence mode is `"list"`.
238
252
 
@@ -240,6 +254,7 @@ Server context (`ctx`) includes:
240
254
 
241
255
  - Throw `ClientSafeError` for messages you want sent to clients.
242
256
  - Non-`ClientSafeError` exceptions are sanitized to: `"An internal server error occurred."`
257
+ - `onAuth` may return `false` to reject the socket before any room state is initialized.
243
258
  - RPC and event dispatch only allow own properties (`Object.hasOwn`) to prevent prototype-based handler access.
244
259
  - Client event names are default-deny unless explicitly declared in `handlers.events`.
245
260
  - Do not trust client payloads for authorization; derive identity in `onAuth`.
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAER,gBAAgB,EAUhB,UAAU,EACV,cAAc,EAKjB,MAAM,SAAS,CAAC;AAsCjB;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,cAAc,CAAC,GAAG,CAAC,EAC9D,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,KAAK,GACZ,UAAU,CAAC,KAAK,CAAC,CA6CnB"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAER,gBAAgB,EAUhB,UAAU,EACV,cAAc,EAMjB,MAAM,SAAS,CAAC;AAwCjB;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,SAAS,cAAc,CAAC,GAAG,CAAC,EAC9D,MAAM,EAAE,gBAAgB,EACxB,IAAI,EAAE,KAAK,GACZ,UAAU,CAAC,KAAK,CAAC,CA6CnB"}
package/dist/client.js CHANGED
@@ -5,6 +5,7 @@ const CLIENT_EVENT = "room-kit:client-event";
5
5
  const SERVER_EVENT = "room-kit:server-event";
6
6
  const PRESENCE_EVENT = "room-kit:presence";
7
7
  const PRESENCE_QUERY_EVENT = "room-kit:presence-query";
8
+ const DEFAULT_ACK_TIMEOUT_MS = 30_000;
8
9
  const clientRegistries = new WeakMap();
9
10
  /**
10
11
  * Binds a client socket to a room type and returns a typed room client.
@@ -95,18 +96,34 @@ function createJoinedRoom(socket, state) {
95
96
  return undefined;
96
97
  }
97
98
  return (handler) => {
98
- const handlers = state.eventListeners.get(key) ?? new Set();
99
- handlers.add(handler);
100
- state.eventListeners.set(key, handlers);
101
- return () => {
102
- handlers.delete(handler);
103
- if (handlers.size === 0) {
104
- state.eventListeners.delete(key);
105
- }
106
- };
99
+ return registerEventListener(state, key, handler);
107
100
  };
108
101
  },
109
102
  });
103
+ const listen = (options) => {
104
+ const cleanups = [];
105
+ if (options.events) {
106
+ for (const [key, handler] of Object.entries(options.events)) {
107
+ if (typeof handler !== "function") {
108
+ continue;
109
+ }
110
+ cleanups.push(registerEventListener(state, key, handler));
111
+ }
112
+ }
113
+ if ("presence" in options && options.presence?.onChange) {
114
+ cleanups.push(registerPresenceListener(state, options.presence.onChange));
115
+ }
116
+ let cleaned = false;
117
+ return () => {
118
+ if (cleaned) {
119
+ return;
120
+ }
121
+ cleaned = true;
122
+ for (const cleanup of cleanups) {
123
+ cleanup();
124
+ }
125
+ };
126
+ };
110
127
  const base = {
111
128
  name: state.name,
112
129
  roomId: state.roomId,
@@ -115,6 +132,7 @@ function createJoinedRoom(socket, state) {
115
132
  rpc,
116
133
  emit,
117
134
  on,
135
+ listen,
118
136
  async leave() {
119
137
  await emitAck(socket, LEAVE_EVENT, {
120
138
  roomType: state.name,
@@ -132,10 +150,7 @@ function createJoinedRoom(socket, state) {
132
150
  return state.presenceCurrent;
133
151
  },
134
152
  onChange(handler) {
135
- state.presenceListeners.add(handler);
136
- return () => {
137
- state.presenceListeners.delete(handler);
138
- };
153
+ return registerPresenceListener(state, handler);
139
154
  },
140
155
  count() {
141
156
  return emitAck(socket, PRESENCE_QUERY_EVENT, {
@@ -158,6 +173,23 @@ function createJoinedRoom(socket, state) {
158
173
  },
159
174
  });
160
175
  }
176
+ function registerEventListener(state, key, handler) {
177
+ const handlers = state.eventListeners.get(key) ?? new Set();
178
+ handlers.add(handler);
179
+ state.eventListeners.set(key, handlers);
180
+ return () => {
181
+ handlers.delete(handler);
182
+ if (handlers.size === 0) {
183
+ state.eventListeners.delete(key);
184
+ }
185
+ };
186
+ }
187
+ function registerPresenceListener(state, handler) {
188
+ state.presenceListeners.add(handler);
189
+ return () => {
190
+ state.presenceListeners.delete(handler);
191
+ };
192
+ }
161
193
  function getClientRegistry(socket) {
162
194
  const existing = clientRegistries.get(socket);
163
195
  if (existing) {
@@ -184,6 +216,16 @@ function getClientRegistry(socket) {
184
216
  function installClientHandlers(socket, registry) {
185
217
  if (!registry.serverHandlerInstalled) {
186
218
  const onServerEvent = (frame) => {
219
+ if (!frame ||
220
+ typeof frame !== "object" ||
221
+ typeof frame.roomType !== "string" ||
222
+ typeof frame.roomId !== "string" ||
223
+ typeof frame.name !== "string" ||
224
+ !frame.meta ||
225
+ typeof frame.meta !== "object" ||
226
+ typeof frame.meta.sentAt !== "string") {
227
+ return;
228
+ }
187
229
  const state = registry.joinedRooms.get(makeJoinedRoomKey(frame.roomType, frame.roomId));
188
230
  if (!state) {
189
231
  return;
@@ -197,7 +239,12 @@ function installClientHandlers(socket, registry) {
197
239
  sentAt: new Date(frame.meta.sentAt),
198
240
  };
199
241
  for (const handler of handlers) {
200
- handler(frame.payload, meta);
242
+ try {
243
+ handler(frame.payload, meta);
244
+ }
245
+ catch {
246
+ // Swallow per-listener errors to avoid breaking the event loop
247
+ }
201
248
  }
202
249
  };
203
250
  socket.on(SERVER_EVENT, onServerEvent);
@@ -205,6 +252,12 @@ function installClientHandlers(socket, registry) {
205
252
  }
206
253
  if (!registry.presenceHandlerInstalled) {
207
254
  const onPresence = (frame) => {
255
+ if (!frame ||
256
+ typeof frame !== "object" ||
257
+ typeof frame.roomType !== "string" ||
258
+ typeof frame.roomId !== "string") {
259
+ return;
260
+ }
208
261
  const state = registry.joinedRooms.get(makeJoinedRoomKey(frame.roomType, frame.roomId));
209
262
  if (!state) {
210
263
  return;
@@ -214,7 +267,12 @@ function installClientHandlers(socket, registry) {
214
267
  return;
215
268
  }
216
269
  for (const handler of state.presenceListeners) {
217
- handler(state.presenceCurrent);
270
+ try {
271
+ handler(state.presenceCurrent);
272
+ }
273
+ catch {
274
+ // Swallow per-listener errors
275
+ }
218
276
  }
219
277
  };
220
278
  socket.on(PRESENCE_EVENT, onPresence);
@@ -292,7 +350,11 @@ function makeJoinedRoomKey(name, roomId) {
292
350
  }
293
351
  function emitAck(socket, eventName, payload) {
294
352
  return new Promise((resolve, reject) => {
353
+ const timer = setTimeout(() => {
354
+ reject(new Error(`Acknowledgement timeout for '${eventName}'`));
355
+ }, DEFAULT_ACK_TIMEOUT_MS);
295
356
  socket.emit(eventName, payload, (result) => {
357
+ clearTimeout(timer);
296
358
  if (!result || typeof result !== "object") {
297
359
  reject(new Error("Invalid acknowledgement payload"));
298
360
  return;
@@ -311,7 +373,12 @@ function setConnectionState(registry, next) {
311
373
  }
312
374
  registry.connectionState = next;
313
375
  for (const listener of registry.connectionListeners) {
314
- listener(next);
376
+ try {
377
+ listener(next);
378
+ }
379
+ catch {
380
+ // Swallow per-listener errors
381
+ }
315
382
  }
316
383
  }
317
384
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -2,13 +2,13 @@ import type { RoomDefinition, RoomServerAdapter, RoomServerHandle, RoomServerHan
2
2
  /**
3
3
  * Attaches room runtime handlers to a connected server socket.
4
4
  *
5
- * Returns a callable handle that unregisters all listeners for the bound
6
- * socket, and exposes room introspection helpers.
5
+ * Returns a handle that unregisters all listeners for the bound socket via
6
+ * `cleanup()`, and exposes room introspection helpers.
7
7
  *
8
8
  * @example
9
9
  * ```ts
10
10
  * io.on("connection", (socket) => {
11
- * const stop = serveRoomType(socket, chatRoomType, {
11
+ * const handle = serveRoomType(socket, chatRoomType, {
12
12
  * onAuth: async () => ({ userId: socket.id }),
13
13
  * admit: async (join, ctx) => ({
14
14
  * roomId: join.roomId,
@@ -19,7 +19,7 @@ import type { RoomDefinition, RoomServerAdapter, RoomServerHandle, RoomServerHan
19
19
  * });
20
20
  *
21
21
  * // Later if needed:
22
- * // stop();
22
+ * // handle.cleanup();
23
23
  * });
24
24
  * ```
25
25
  */
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAUR,cAAc,EAEd,iBAAiB,EAEjB,gBAAgB,EAEhB,kBAAkB,EAElB,gBAAgB,EAInB,MAAM,SAAS,CAAC;AAoCjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,KAAK,SAAS,cAAc,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,EAC5E,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,iBAAiB,GAC5B,gBAAgB,CAAC,KAAK,CAAC,CAyVzB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAUR,cAAc,EAEd,iBAAiB,EAEjB,gBAAgB,EAEhB,kBAAkB,EAElB,gBAAgB,EAInB,MAAM,SAAS,CAAC;AAkKjB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,KAAK,SAAS,cAAc,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,EAC5E,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,EAC1C,OAAO,CAAC,EAAE,iBAAiB,GAC5B,gBAAgB,CAAC,KAAK,CAAC,CAqXzB"}
package/dist/server.js CHANGED
@@ -6,17 +6,106 @@ const CLIENT_EVENT = "room-kit:client-event";
6
6
  const SERVER_EVENT = "room-kit:server-event";
7
7
  const PRESENCE_EVENT = "room-kit:presence";
8
8
  const PRESENCE_QUERY_EVENT = "room-kit:presence-query";
9
+ function assertNonEmptyString(value, label) {
10
+ if (typeof value !== "string" || value.length === 0) {
11
+ throw new ClientSafeError(`${label} must be a non-empty string`);
12
+ }
13
+ }
14
+ function assertObject(value, label) {
15
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
16
+ throw new ClientSafeError(`${label} must be an object`);
17
+ }
18
+ }
19
+ function assertArray(value, label) {
20
+ if (!Array.isArray(value)) {
21
+ throw new ClientSafeError(`${label} must be an array`);
22
+ }
23
+ }
24
+ /** Validates and narrows a join frame from the wire. */
25
+ function validateJoinFrame(raw) {
26
+ assertObject(raw, "Join frame");
27
+ assertNonEmptyString(raw.roomType, "roomType");
28
+ assertObject(raw.payload, "payload");
29
+ return { roomType: raw.roomType, payload: raw.payload };
30
+ }
31
+ /** Validates and narrows a leave frame from the wire. */
32
+ function validateLeaveFrame(raw) {
33
+ assertObject(raw, "Leave frame");
34
+ assertNonEmptyString(raw.roomType, "roomType");
35
+ assertNonEmptyString(raw.roomId, "roomId");
36
+ return { roomType: raw.roomType, roomId: raw.roomId };
37
+ }
38
+ /** Validates and narrows an RPC frame from the wire. */
39
+ function validateRpcFrame(raw) {
40
+ assertObject(raw, "RPC frame");
41
+ assertNonEmptyString(raw.roomType, "roomType");
42
+ assertNonEmptyString(raw.roomId, "roomId");
43
+ assertNonEmptyString(raw.name, "name");
44
+ assertArray(raw.args, "args");
45
+ return { roomType: raw.roomType, roomId: raw.roomId, name: raw.name, args: raw.args };
46
+ }
47
+ /** Validates and narrows a client-event frame from the wire. */
48
+ function validateClientEventFrame(raw) {
49
+ assertObject(raw, "Client-event frame");
50
+ assertNonEmptyString(raw.roomType, "roomType");
51
+ assertNonEmptyString(raw.roomId, "roomId");
52
+ assertNonEmptyString(raw.name, "name");
53
+ return { roomType: raw.roomType, roomId: raw.roomId, name: raw.name, payload: raw.payload };
54
+ }
55
+ /** Validates and narrows a presence-query frame from the wire. */
56
+ function validatePresenceQueryFrame(raw) {
57
+ assertObject(raw, "Presence-query frame");
58
+ assertNonEmptyString(raw.roomType, "roomType");
59
+ assertNonEmptyString(raw.roomId, "roomId");
60
+ if (raw.kind !== "count" && raw.kind !== "list") {
61
+ throw new ClientSafeError("Presence query kind must be 'count' or 'list'");
62
+ }
63
+ const result = { roomType: raw.roomType, roomId: raw.roomId, kind: raw.kind };
64
+ if (raw.offset !== undefined) {
65
+ if (typeof raw.offset !== "number" || !Number.isFinite(raw.offset)) {
66
+ throw new ClientSafeError("offset must be a finite number");
67
+ }
68
+ result.offset = raw.offset;
69
+ }
70
+ if (raw.limit !== undefined) {
71
+ if (typeof raw.limit !== "number" || !Number.isFinite(raw.limit)) {
72
+ throw new ClientSafeError("limit must be a finite number");
73
+ }
74
+ result.limit = raw.limit;
75
+ }
76
+ return result;
77
+ }
78
+ const DANGEROUS_PROPERTY_NAMES = new Set([
79
+ "__proto__",
80
+ "constructor",
81
+ "prototype",
82
+ "toString",
83
+ "valueOf",
84
+ "hasOwnProperty",
85
+ "isPrototypeOf",
86
+ "propertyIsEnumerable",
87
+ "toLocaleString",
88
+ ]);
89
+ function assertSafeHandlerName(name) {
90
+ if (DANGEROUS_PROPERTY_NAMES.has(name)) {
91
+ throw new ClientSafeError(`Disallowed handler name '${name}'`);
92
+ }
93
+ }
94
+ /** Maximum number of rooms that can exist within a single namespace. */
95
+ const MAX_ROOMS_PER_NAMESPACE = 10_000;
96
+ /** Maximum number of socket connections tracked per room. */
97
+ const MAX_MEMBERS_PER_ROOM = 10_000;
9
98
  const namespaceStates = new WeakMap();
10
99
  /**
11
100
  * Attaches room runtime handlers to a connected server socket.
12
101
  *
13
- * Returns a callable handle that unregisters all listeners for the bound
14
- * socket, and exposes room introspection helpers.
102
+ * Returns a handle that unregisters all listeners for the bound socket via
103
+ * `cleanup()`, and exposes room introspection helpers.
15
104
  *
16
105
  * @example
17
106
  * ```ts
18
107
  * io.on("connection", (socket) => {
19
- * const stop = serveRoomType(socket, chatRoomType, {
108
+ * const handle = serveRoomType(socket, chatRoomType, {
20
109
  * onAuth: async () => ({ userId: socket.id }),
21
110
  * admit: async (join, ctx) => ({
22
111
  * roomId: join.roomId,
@@ -27,22 +116,29 @@ const namespaceStates = new WeakMap();
27
116
  * });
28
117
  *
29
118
  * // Later if needed:
30
- * // stop();
119
+ * // handle.cleanup();
31
120
  * });
32
121
  * ```
33
122
  */
34
123
  export function serveRoomType(socket, _room, handlers, adapter) {
35
124
  const namespaceState = getNamespaceState(socket);
36
125
  const authCache = new WeakMap();
37
- const onJoin = async (frame, ack) => {
126
+ const onJoin = async (raw, ack) => {
38
127
  try {
128
+ if (ack !== undefined && typeof ack !== "function") {
129
+ return;
130
+ }
131
+ const frame = validateJoinFrame(raw);
39
132
  assertMatchingRoomName(_room, frame.roomType);
40
133
  const requestedRoomId = extractRoomId(frame.payload);
41
134
  const roomCollection = getOrCreateRoomCollection(namespaceState, frame.roomType);
135
+ if (!roomCollection.has(requestedRoomId) && roomCollection.size >= MAX_ROOMS_PER_NAMESPACE) {
136
+ throw new ClientSafeError("Maximum room limit reached for this namespace");
137
+ }
42
138
  const existingRoomState = roomCollection.get(requestedRoomId);
139
+ const auth = await resolveSocketAuth(socket, handlers, authCache, true);
43
140
  const initialState = (existingRoomState?.serverState ??
44
141
  await Promise.resolve(handlers.initState?.(frame.payload) ?? {}));
45
- const auth = await resolveSocketAuth(socket, handlers, authCache, true);
46
142
  const provisional = createContext(socket, namespaceState, {
47
143
  adapter,
48
144
  name: frame.roomType,
@@ -62,6 +158,9 @@ export function serveRoomType(socket, _room, handlers, adapter) {
62
158
  membersBySocketId: new Map(),
63
159
  socketIdsByMemberId: new Map(),
64
160
  };
161
+ if (!roomState.membersBySocketId.has(socket.id) && roomState.membersBySocketId.size >= MAX_MEMBERS_PER_ROOM) {
162
+ throw new ClientSafeError("Room is full");
163
+ }
65
164
  roomState.roomProfile = roomState.roomProfile ?? admission.roomProfile;
66
165
  roomState.serverState = roomState.serverState ?? provisional.serverState;
67
166
  roomState.membersBySocketId.set(socket.id, {
@@ -97,8 +196,12 @@ export function serveRoomType(socket, _room, handlers, adapter) {
97
196
  ack?.({ ok: false, error: toErrorMessage(error) });
98
197
  }
99
198
  };
100
- const onLeave = async (payload, ack) => {
199
+ const onLeave = async (raw, ack) => {
101
200
  try {
201
+ if (ack !== undefined && typeof ack !== "function") {
202
+ return;
203
+ }
204
+ const payload = validateLeaveFrame(raw);
102
205
  assertMatchingRoomName(_room, payload.roomType);
103
206
  const stored = getStoredMembership(namespaceState, payload.roomType, payload.roomId, socket.id);
104
207
  if (!stored) {
@@ -125,9 +228,14 @@ export function serveRoomType(socket, _room, handlers, adapter) {
125
228
  ack?.({ ok: false, error: toErrorMessage(error) });
126
229
  }
127
230
  };
128
- const onRpc = async (frame, ack) => {
231
+ const onRpc = async (raw, ack) => {
129
232
  try {
233
+ if (ack !== undefined && typeof ack !== "function") {
234
+ return;
235
+ }
236
+ const frame = validateRpcFrame(raw);
130
237
  assertMatchingRoomName(_room, frame.roomType);
238
+ assertSafeHandlerName(frame.name);
131
239
  if (!handlers.rpc || !Object.hasOwn(handlers.rpc, frame.name)) {
132
240
  throw new ClientSafeError(`Unknown RPC '${frame.name}'`);
133
241
  }
@@ -150,6 +258,9 @@ export function serveRoomType(socket, _room, handlers, adapter) {
150
258
  roomProfile: getRoomState(namespaceState, frame.roomType, frame.roomId).roomProfile,
151
259
  serverState: getRoomState(namespaceState, frame.roomType, frame.roomId).serverState,
152
260
  });
261
+ if (frame.args.length > 64) {
262
+ throw new ClientSafeError("Too many RPC arguments");
263
+ }
153
264
  const result = await handler(...frame.args, ctx);
154
265
  ack?.({ ok: true, value: result });
155
266
  }
@@ -157,9 +268,14 @@ export function serveRoomType(socket, _room, handlers, adapter) {
157
268
  ack?.({ ok: false, error: toErrorMessage(error) });
158
269
  }
159
270
  };
160
- const onClientEvent = async (frame, ack) => {
271
+ const onClientEvent = async (raw, ack) => {
161
272
  try {
273
+ if (ack !== undefined && typeof ack !== "function") {
274
+ return;
275
+ }
276
+ const frame = validateClientEventFrame(raw);
162
277
  assertMatchingRoomName(_room, frame.roomType);
278
+ assertSafeHandlerName(frame.name);
163
279
  const stored = getStoredMembership(namespaceState, frame.roomType, frame.roomId, socket.id);
164
280
  if (!stored) {
165
281
  throw new ClientSafeError("Socket is not joined to that room");
@@ -194,8 +310,12 @@ export function serveRoomType(socket, _room, handlers, adapter) {
194
310
  ack?.({ ok: false, error: toErrorMessage(error) });
195
311
  }
196
312
  };
197
- const onPresenceQuery = async (frame, ack) => {
313
+ const onPresenceQuery = async (raw, ack) => {
198
314
  try {
315
+ if (ack !== undefined && typeof ack !== "function") {
316
+ return;
317
+ }
318
+ const frame = validatePresenceQueryFrame(raw);
199
319
  assertMatchingRoomName(_room, frame.roomType);
200
320
  const stored = getStoredMembership(namespaceState, frame.roomType, frame.roomId, socket.id);
201
321
  if (!stored) {
@@ -297,7 +417,7 @@ export function serveRoomType(socket, _room, handlers, adapter) {
297
417
  })
298
418
  .catch(() => undefined);
299
419
  }
300
- const stop = () => {
420
+ const cleanup = () => {
301
421
  socket.off(JOIN_EVENT, onJoin);
302
422
  socket.off(LEAVE_EVENT, onLeave);
303
423
  socket.off(RPC_EVENT, onRpc);
@@ -305,12 +425,13 @@ export function serveRoomType(socket, _room, handlers, adapter) {
305
425
  socket.off(PRESENCE_QUERY_EVENT, onPresenceQuery);
306
426
  socket.off("disconnect", onDisconnect);
307
427
  };
308
- return Object.assign(stop, {
428
+ return {
429
+ cleanup,
309
430
  rooms: () => listRoomSnapshots(namespaceState, _room.name),
310
431
  room: (roomId) => getRoomSnapshot(namespaceState, _room.name, roomId),
311
432
  members: (roomId, query) => getPresenceMembersPage(namespaceState, _room.name, roomId, query),
312
433
  count: (roomId) => getPresenceCount(namespaceState, _room.name, roomId),
313
- });
434
+ };
314
435
  }
315
436
  function createContext(socket, namespaceState, ctxState) {
316
437
  const emit = createEventEmitApi((eventName, payload) => {
@@ -356,6 +477,10 @@ async function resolveSocketAuth(socket, handlers, authCache, revalidate) {
356
477
  if (entry.value === undefined) {
357
478
  const pending = Promise.resolve(handlers.onAuth?.(socket))
358
479
  .then((auth) => {
480
+ if (auth === false) {
481
+ authCache.delete(socket);
482
+ throw new ClientSafeError("Unauthorized");
483
+ }
359
484
  const current = authCache.get(socket) ?? {};
360
485
  current.value = auth;
361
486
  current.pending = undefined;
@@ -737,7 +862,11 @@ function extractRoomId(payload) {
737
862
  if (!payload || typeof payload !== "object" || typeof payload.roomId !== "string") {
738
863
  throw new ClientSafeError("Join request must include a string roomId");
739
864
  }
740
- return payload.roomId;
865
+ const roomId = payload.roomId;
866
+ if (roomId.length === 0 || roomId.length > 256) {
867
+ throw new ClientSafeError("roomId must be between 1 and 256 characters");
868
+ }
869
+ return roomId;
741
870
  }
742
871
  function toErrorMessage(error) {
743
872
  return error instanceof ClientSafeError ? error.message : "An internal server error occurred.";