@colyseus/core 0.18.24 → 0.18.26

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.
@@ -32,7 +32,9 @@ var import_nanoevents = require("./utils/nanoevents.cjs");
32
32
  var import_StandardSchema = require("./utils/StandardSchema.cjs");
33
33
  var import_DevMode = require("./utils/DevMode.cjs");
34
34
  var import_Debug = require("./Debug.cjs");
35
+ var import_Logger = require("./Logger.cjs");
35
36
  var import_RoomExceptions = require("./errors/RoomExceptions.cjs");
37
+ var import_Transport = require("./Transport.cjs");
36
38
  function toResponseError(e) {
37
39
  if (e instanceof Error) {
38
40
  const code = e.code;
@@ -203,6 +205,9 @@ var RoomMessages = class {
203
205
  }
204
206
  const handler = this.events.events[messageType]?.[0];
205
207
  if (handler === void 0) {
208
+ if (this.room.onUnhandledMessage !== void 0) {
209
+ this.#reportUnhandled(client, messageType, message);
210
+ }
206
211
  this.#replyToRequest(client, requestId, import_shared_types.ResponseStatus.ERROR, {
207
212
  name: "no_handler",
208
213
  message: `room "${this.room.roomName}" has no onMessage("${messageType}") handler to answer this request.`
@@ -270,15 +275,39 @@ var RoomMessages = class {
270
275
  client.enqueueRaw(import_Protocol.getMessageBytes[import_shared_types.Protocol.ROOM_RESPONSE](requestId, status, payload));
271
276
  }
272
277
  /** No handler for the type: error the client in dev, drop it in production. */
273
- #noHandler(client, messageType, _message) {
278
+ #noHandler(client, messageType, message) {
274
279
  const errorMessage = `room onMessage for "${messageType}" not registered.`;
275
- (0, import_Debug.debugMessage)(`${errorMessage} (roomId: ${this.room.roomId})`);
280
+ if (this.room.onUnhandledMessage !== void 0) {
281
+ this.#reportUnhandled(client, messageType, message);
282
+ } else {
283
+ import_Logger.logger.warn(`room "${this.room.roomName}" (roomId: ${this.room.roomId}): client '${client.sessionId}' sent "${messageType}", which has no onMessage() handler. ${import_DevMode.isDevMode ? "Replied with an INVALID_PAYLOAD error (outside devMode, the client is disconnected)." : "Disconnecting the client."}`);
284
+ }
276
285
  if (import_DevMode.isDevMode) {
277
286
  client.error(import_shared_types.ErrorCode.INVALID_PAYLOAD, errorMessage);
278
287
  } else {
288
+ client.state = import_Transport.ClientState.LEAVING;
279
289
  client.leave(import_shared_types.CloseCode.WITH_ERROR, errorMessage);
280
290
  }
281
291
  }
292
+ /** Call `room.onUnhandledMessage`. A throw or rejection goes to
293
+ * `onUncaughtException` when set, or is logged; it never stops the default. */
294
+ #reportUnhandled(client, messageType, message) {
295
+ const report = (e) => {
296
+ if (this.room.onUncaughtException !== void 0) {
297
+ this.room.onUncaughtException(new import_RoomExceptions.OnMessageException(e, e?.message, client, message, messageType), "onMessage");
298
+ } else {
299
+ (0, import_Debug.debugAndPrintError)(e);
300
+ }
301
+ };
302
+ try {
303
+ const result = this.room.onUnhandledMessage(client, messageType, message);
304
+ if (typeof result?.catch === "function") {
305
+ result.catch(report);
306
+ }
307
+ } catch (e) {
308
+ report(e);
309
+ }
310
+ }
282
311
  };
283
312
  // Annotate the CommonJS export names for ESM import in node:
284
313
  0 && (module.exports = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/RoomMessages.ts"],
4
- "sourcesContent": ["import { decode, type Iterator } from '@colyseus/schema';\nimport { unpack } from 'msgpackr';\n\nimport { CloseCode, ErrorCode, Protocol, ResponseStatus, type MessageContext } from '@colyseus/shared-types';\nimport { getMessageBytes } from './Protocol.ts';\nimport { createNanoEvents } from './utils/nanoevents.ts';\nimport { standardValidate, type StandardSchemaV1 } from './utils/StandardSchema.ts';\nimport { isDevMode } from './utils/DevMode.ts';\nimport { debugMessage, debugAndPrintError } from './Debug.ts';\nimport { OnMessageException } from './errors/RoomExceptions.ts';\nimport type { Client, ClientPrivate } from './Transport.ts';\nimport type { Room } from './Room.ts';\n\n/** Normalize a thrown value into the `{ name, message, code? }` shape echoed in a\n * ROOM_RESPONSE error reply. */\nfunction toResponseError(e: any): { name: string; message: string; code?: any } {\n if (e instanceof Error) {\n const code = (e as any).code;\n return (code !== undefined)\n ? { name: e.name, message: e.message, code }\n : { name: e.name, message: e.message };\n }\n return { name: \"Error\", message: String(e) };\n}\n\n// The handler's terminal action \u2014 NOT the wire ResponseStatus: `none` and\n// `resolved` both reply OK (plain return value vs `ctx.resolve(value)`), a\n// distinction a single OK can't carry; ERROR is never a handler decision\n// (thrown/no-handler). Projected onto ResponseStatus once, in onRequest's reply.\nconst OUTCOME_NONE = 0, OUTCOME_REJECTED = 1, OUTCOME_RESOLVED = 2;\n\n/**\n * Runtime {@link MessageContext} for a {@link Protocol.ROOM_REQUEST} dispatch.\n * `reject`/`resolve` record the decision on the ctx; `onRequest` reads it after the\n * handler returns, so a bare side-effecting call works as well as `return\n * ctx.reject(r)` (the branded return is compile-time only \u2014 the runtime return is\n * unused).\n */\nclass DispatchContext implements MessageContext {\n readonly id: number | undefined;\n _outcome = OUTCOME_NONE;\n _reason: any = undefined;\n _value: any = undefined;\n\n constructor(id: number | undefined) {\n this.id = id;\n }\n\n reject(reason?: any): any {\n this._outcome = OUTCOME_REJECTED;\n this._reason = reason;\n return undefined;\n }\n\n resolve(value?: any): any {\n this._outcome = OUTCOME_RESOLVED;\n this._value = value;\n return undefined;\n }\n}\n\n/** Shared no-op context for fire-and-forget `ROOM_DATA` (no reply channel, so\n * `reject`/`resolve` go nowhere). `id` is `undefined`; zero per-message allocation. */\nconst SEND_CONTEXT: MessageContext = Object.freeze({\n id: undefined,\n reject: () => undefined as any,\n resolve: () => undefined as any,\n});\n\n/**\n * Per-room message-routing layer, owned by {@link Room}. Owns the user-message\n * handler registry (`onMessage`/`onMessageBytes` + per-type validators) and\n * decodes/dispatches the user-message wire frames (ROOM_DATA / ROOM_REQUEST /\n * ROOM_DATA_BYTES). Room's `_onMessage` keeps the protocol/lifecycle frames\n * (JOIN/PING/LEAVE/input) and calls one of these per frame, preserving the\n * original dispatch order. Reads back into its Room only for `onUncaughtException`\n * (handler wrapping) and `roomId`/`roomName` (logging).\n *\n * @internal\n */\nexport class RoomMessages {\n private room: Room<any>;\n\n /** Handler registry (nanoevents). Public so Room can re-expose it as\n * `onMessageEvents` for @colyseus/playground introspection and\n * @colyseus/testing handler-swapping. */\n events = createNanoEvents();\n\n /** Per-type StandardSchema validators. Public for the same reason as\n * {@link events} (`onMessageValidators`). */\n // null-prototype: keyed by client-supplied message type (colyseus/colyseus#951)\n validators: { [type: string]: StandardSchemaV1 } = Object.create(null);\n\n constructor(room: Room<any>) {\n this.room = room;\n }\n\n /**\n * Register a handler (body of {@link Room.onMessage}). Wraps the callback via\n * the room's `onUncaughtException` when set, stores the validator, and returns\n * the unbind closure.\n */\n on(\n _messageType: '*' | string | number,\n _validationSchema: StandardSchemaV1 | ((...args: any[]) => void),\n _callback?: (...args: any[]) => void,\n ): () => void {\n const messageType = _messageType.toString();\n\n const validationSchema = (typeof _callback === 'function')\n ? _validationSchema as StandardSchemaV1\n : undefined;\n\n const callback = (validationSchema === undefined)\n ? _validationSchema as (...args: any[]) => void\n : _callback;\n\n const removeListener = this.events.on(messageType, (this.room.onUncaughtException !== undefined)\n ? this.#wrapMessageHandler(callback, _messageType)\n : callback);\n\n if (validationSchema !== undefined) {\n this.validators[messageType] = validationSchema;\n }\n\n // returns a method to unbind the callback\n return () => {\n removeListener();\n if (this.events.events[messageType].length === 0) {\n delete this.validators[messageType];\n }\n };\n }\n\n /**\n * Wrap a user message handler so a sync throw or async rejection is reported to\n * `room.onUncaughtException` as an {@link OnMessageException} carrying the correct\n * `client` / `payload` / `type`. The handler is invoked with a different argument\n * shape per dispatch path, so those fields are normalized here rather than relying\n * on positional forwarding (which silently mis-mapped `type` once a context arg was\n * added \u2014 see ROOM_DATA/ROOM_REQUEST passing `SEND_CONTEXT`/`ctx`):\n *\n * - typed handler (DATA / REQUEST / BYTES): `(client, payload, ctx?)` \u2192 `type` is\n * the registered `_messageType`.\n * - wildcard `'*'` handler: `(client, type, payload)` \u2192 `type` is\n * the received message type.\n *\n * The error is swallowed (never rethrown) so a faulty handler can't break dispatch;\n * the handler's return value (or promise) is passed through for the REQUEST path.\n */\n #wrapMessageHandler(callback: (...args: any[]) => any, registeredType: '*' | string | number) {\n const onError = this.room.onUncaughtException!.bind(this.room);\n const isWildcard = (registeredType === '*');\n\n return (...args: any[]): any => {\n const report = (e: Error) => onError(\n new OnMessageException(\n e, e.message,\n args[0], // client\n isWildcard ? args[2] : args[1], // payload\n isWildcard ? args[1] : registeredType, // type\n ),\n 'onMessage',\n );\n\n try {\n const result = callback(...args);\n return (typeof result?.catch === 'function') ? result.catch(report) : result;\n } catch (e: any) {\n report(e);\n }\n };\n }\n\n /** Dispatch a `ROOM_DATA` frame: decode type + msgpack payload, validate, emit. */\n onData(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n // custom message validation\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[messageType]) {\n this.#dispatch(this.events.events[messageType], client, message, SEND_CONTEXT);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /**\n * Call the handlers of a fire-and-forget frame. With no reply to carry an error\n * and nothing above this to catch one, a throw would reach the socket's event\n * listener and shut the process down, along with every other room in it. So\n * errors are logged, and the remaining handlers still run. Handlers wrapped for\n * `onUncaughtException` report their own errors and never throw here.\n */\n #dispatch(handlers: Array<(...args: any[]) => any>, ...args: any[]): void {\n for (let i = 0, length = handlers.length; i < length; i++) {\n try {\n const result = handlers[i](...args);\n if (typeof result?.catch === 'function') {\n result.catch(debugAndPrintError);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n }\n }\n }\n\n /** Dispatch a `ROOM_REQUEST` frame: same handlers as ROOM_DATA, but the client\n * opted into a reply, so echo the `requestId` with the outcome. The handler's\n * reply is decided by what it returns / does to `ctx`: `ctx.reject(reason)` \u2192\n * `REJECTED(reason)`, `ctx.resolve(value)` \u2192 `OK(value)`, a thrown error or\n * missing handler \u2192 `ERROR`, else `OK(return)`. */\n onRequest(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const requestId = decode.number(buffer, it);\n\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"request #%d: '%s' -> %j (roomId: %s)\", requestId, messageType, message, this.room.roomId);\n\n // custom message validation (shared with the ROOM_DATA path)\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n // Reply with an error so the caller's pending request resolves instead of timing out.\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n // Answered by the FIRST handler registered for the type (emit would discard\n // returns); wildcard handlers have no response contract, so they're ineligible.\n const handler = this.events.events[messageType as string]?.[0];\n\n if (handler === undefined) {\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, {\n name: \"no_handler\",\n message: `room \"${this.room.roomName}\" has no onMessage(\"${messageType}\") handler to answer this request.`,\n });\n return;\n }\n\n const ctx = new DispatchContext(requestId);\n\n // Sync handlers reply in this tick with no promise machinery; only a thenable\n // return defers to the microtask queue. A sync throw (unwrapped handler) and an\n // async rejection both fall to the ERROR reply. With onUncaughtException set the\n // handler is wrapped (swallows its own errors), so it never throws / its promise\n // resolves undefined, and the error reports there.\n let response: any;\n try {\n response = handler(client, message, ctx);\n if (response !== null && typeof response === 'object' && typeof response.then === 'function') {\n response.then(\n (resolved: any) => this.#finalizeRequest(client, requestId, ctx, resolved),\n (e: any) => {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n },\n );\n return;\n }\n } catch (e: any) {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n this.#finalizeRequest(client, requestId, ctx, response);\n }\n\n /** Finalize a request: project the handler's outcome onto a ROOM_RESPONSE reply \u2014\n * `ctx.reject` \u2192 REJECTED(reason), `ctx.resolve(value)` \u2192 OK(value), else OK(return). */\n #finalizeRequest(client: Client, requestId: number, ctx: DispatchContext, response: any): void {\n if (ctx._outcome === OUTCOME_REJECTED) {\n this.#replyToRequest(client, requestId, ResponseStatus.REJECTED, ctx._reason);\n } else if (ctx._outcome === OUTCOME_RESOLVED) {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, ctx._value);\n } else {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, response);\n }\n }\n\n /** Dispatch a `ROOM_DATA_BYTES` frame: raw bytes routed to `_$b`-prefixed handlers. */\n onDataBytes(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message: any = buffer.subarray(it.offset, buffer.byteLength);\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n const bytesMessageType = `_$b${messageType}`;\n\n // custom message validation\n try {\n if (this.validators[bytesMessageType] !== undefined) {\n message = standardValidate(this.validators[bytesMessageType], message);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[bytesMessageType]) {\n this.#dispatch(this.events.events[bytesMessageType], client, message);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /** Emit a ROOM_RESPONSE reply immediately. */\n #replyToRequest(client: Client, requestId: number, status: ResponseStatus, payload?: any): void {\n debugMessage(\"response #%d: status=%d -> %j (roomId: %s)\", requestId, status, payload, this.room.roomId);\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_RESPONSE](requestId, status, payload));\n }\n\n /** No handler for the type: error the client in dev, drop it in production. */\n #noHandler(client: Client, messageType: string | number, _message: unknown): void {\n const errorMessage = `room onMessage for \"${messageType}\" not registered.`;\n debugMessage(`${errorMessage} (roomId: ${this.room.roomId})`);\n if (isDevMode) {\n client.error(ErrorCode.INVALID_PAYLOAD, errorMessage);\n } else {\n client.leave(CloseCode.WITH_ERROR, errorMessage);\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { decode, type Iterator } from '@colyseus/schema';\nimport { unpack } from 'msgpackr';\n\nimport { CloseCode, ErrorCode, Protocol, ResponseStatus, type MessageContext } from '@colyseus/shared-types';\nimport { getMessageBytes } from './Protocol.ts';\nimport { createNanoEvents } from './utils/nanoevents.ts';\nimport { standardValidate, type StandardSchemaV1 } from './utils/StandardSchema.ts';\nimport { isDevMode } from './utils/DevMode.ts';\nimport { debugMessage, debugAndPrintError } from './Debug.ts';\nimport { logger } from './Logger.ts';\nimport { OnMessageException } from './errors/RoomExceptions.ts';\nimport { ClientState, type Client, type ClientPrivate } from './Transport.ts';\nimport type { Room } from './Room.ts';\n\n/** Normalize a thrown value into the `{ name, message, code? }` shape echoed in a\n * ROOM_RESPONSE error reply. */\nfunction toResponseError(e: any): { name: string; message: string; code?: any } {\n if (e instanceof Error) {\n const code = (e as any).code;\n return (code !== undefined)\n ? { name: e.name, message: e.message, code }\n : { name: e.name, message: e.message };\n }\n return { name: \"Error\", message: String(e) };\n}\n\n// The handler's terminal action \u2014 NOT the wire ResponseStatus: `none` and\n// `resolved` both reply OK (plain return value vs `ctx.resolve(value)`), a\n// distinction a single OK can't carry; ERROR is never a handler decision\n// (thrown/no-handler). Projected onto ResponseStatus once, in onRequest's reply.\nconst OUTCOME_NONE = 0, OUTCOME_REJECTED = 1, OUTCOME_RESOLVED = 2;\n\n/**\n * Runtime {@link MessageContext} for a {@link Protocol.ROOM_REQUEST} dispatch.\n * `reject`/`resolve` record the decision on the ctx; `onRequest` reads it after the\n * handler returns, so a bare side-effecting call works as well as `return\n * ctx.reject(r)` (the branded return is compile-time only \u2014 the runtime return is\n * unused).\n */\nclass DispatchContext implements MessageContext {\n readonly id: number | undefined;\n _outcome = OUTCOME_NONE;\n _reason: any = undefined;\n _value: any = undefined;\n\n constructor(id: number | undefined) {\n this.id = id;\n }\n\n reject(reason?: any): any {\n this._outcome = OUTCOME_REJECTED;\n this._reason = reason;\n return undefined;\n }\n\n resolve(value?: any): any {\n this._outcome = OUTCOME_RESOLVED;\n this._value = value;\n return undefined;\n }\n}\n\n/** Shared no-op context for fire-and-forget `ROOM_DATA` (no reply channel, so\n * `reject`/`resolve` go nowhere). `id` is `undefined`; zero per-message allocation. */\nconst SEND_CONTEXT: MessageContext = Object.freeze({\n id: undefined,\n reject: () => undefined as any,\n resolve: () => undefined as any,\n});\n\n/**\n * Per-room message-routing layer, owned by {@link Room}. Owns the user-message\n * handler registry (`onMessage`/`onMessageBytes` + per-type validators) and\n * decodes/dispatches the user-message wire frames (ROOM_DATA / ROOM_REQUEST /\n * ROOM_DATA_BYTES). Room's `_onMessage` keeps the protocol/lifecycle frames\n * (JOIN/PING/LEAVE/input) and calls one of these per frame, preserving the\n * original dispatch order. Reads back into its Room only for `onUncaughtException`\n * (handler wrapping), `onUnhandledMessage` and `roomId`/`roomName` (logging).\n *\n * @internal\n */\nexport class RoomMessages {\n private room: Room<any>;\n\n /** Handler registry (nanoevents). Public so Room can re-expose it as\n * `onMessageEvents` for @colyseus/playground introspection and\n * @colyseus/testing handler-swapping. */\n events = createNanoEvents();\n\n /** Per-type StandardSchema validators. Public for the same reason as\n * {@link events} (`onMessageValidators`). */\n // null-prototype: keyed by client-supplied message type (colyseus/colyseus#951)\n validators: { [type: string]: StandardSchemaV1 } = Object.create(null);\n\n constructor(room: Room<any>) {\n this.room = room;\n }\n\n /**\n * Register a handler (body of {@link Room.onMessage}). Wraps the callback via\n * the room's `onUncaughtException` when set, stores the validator, and returns\n * the unbind closure.\n */\n on(\n _messageType: '*' | string | number,\n _validationSchema: StandardSchemaV1 | ((...args: any[]) => void),\n _callback?: (...args: any[]) => void,\n ): () => void {\n const messageType = _messageType.toString();\n\n const validationSchema = (typeof _callback === 'function')\n ? _validationSchema as StandardSchemaV1\n : undefined;\n\n const callback = (validationSchema === undefined)\n ? _validationSchema as (...args: any[]) => void\n : _callback;\n\n const removeListener = this.events.on(messageType, (this.room.onUncaughtException !== undefined)\n ? this.#wrapMessageHandler(callback, _messageType)\n : callback);\n\n if (validationSchema !== undefined) {\n this.validators[messageType] = validationSchema;\n }\n\n // returns a method to unbind the callback\n return () => {\n removeListener();\n if (this.events.events[messageType].length === 0) {\n delete this.validators[messageType];\n }\n };\n }\n\n /**\n * Wrap a user message handler so a sync throw or async rejection is reported to\n * `room.onUncaughtException` as an {@link OnMessageException} carrying the correct\n * `client` / `payload` / `type`. The handler is invoked with a different argument\n * shape per dispatch path, so those fields are normalized here rather than relying\n * on positional forwarding (which silently mis-mapped `type` once a context arg was\n * added \u2014 see ROOM_DATA/ROOM_REQUEST passing `SEND_CONTEXT`/`ctx`):\n *\n * - typed handler (DATA / REQUEST / BYTES): `(client, payload, ctx?)` \u2192 `type` is\n * the registered `_messageType`.\n * - wildcard `'*'` handler: `(client, type, payload)` \u2192 `type` is\n * the received message type.\n *\n * The error is swallowed (never rethrown) so a faulty handler can't break dispatch;\n * the handler's return value (or promise) is passed through for the REQUEST path.\n */\n #wrapMessageHandler(callback: (...args: any[]) => any, registeredType: '*' | string | number) {\n const onError = this.room.onUncaughtException!.bind(this.room);\n const isWildcard = (registeredType === '*');\n\n return (...args: any[]): any => {\n const report = (e: Error) => onError(\n new OnMessageException(\n e, e.message,\n args[0], // client\n isWildcard ? args[2] : args[1], // payload\n isWildcard ? args[1] : registeredType, // type\n ),\n 'onMessage',\n );\n\n try {\n const result = callback(...args);\n return (typeof result?.catch === 'function') ? result.catch(report) : result;\n } catch (e: any) {\n report(e);\n }\n };\n }\n\n /** Dispatch a `ROOM_DATA` frame: decode type + msgpack payload, validate, emit. */\n onData(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n // custom message validation\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[messageType]) {\n this.#dispatch(this.events.events[messageType], client, message, SEND_CONTEXT);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /**\n * Call the handlers of a fire-and-forget frame. With no reply to carry an error\n * and nothing above this to catch one, a throw would reach the socket's event\n * listener and shut the process down, along with every other room in it. So\n * errors are logged, and the remaining handlers still run. Handlers wrapped for\n * `onUncaughtException` report their own errors and never throw here.\n */\n #dispatch(handlers: Array<(...args: any[]) => any>, ...args: any[]): void {\n for (let i = 0, length = handlers.length; i < length; i++) {\n try {\n const result = handlers[i](...args);\n if (typeof result?.catch === 'function') {\n result.catch(debugAndPrintError);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n }\n }\n }\n\n /** Dispatch a `ROOM_REQUEST` frame: same handlers as ROOM_DATA, but the client\n * opted into a reply, so echo the `requestId` with the outcome. The handler's\n * reply is decided by what it returns / does to `ctx`: `ctx.reject(reason)` \u2192\n * `REJECTED(reason)`, `ctx.resolve(value)` \u2192 `OK(value)`, a thrown error or\n * missing handler \u2192 `ERROR`, else `OK(return)`. */\n onRequest(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const requestId = decode.number(buffer, it);\n\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"request #%d: '%s' -> %j (roomId: %s)\", requestId, messageType, message, this.room.roomId);\n\n // custom message validation (shared with the ROOM_DATA path)\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n // Reply with an error so the caller's pending request resolves instead of timing out.\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n // Answered by the FIRST handler registered for the type (emit would discard\n // returns); wildcard handlers have no response contract, so they're ineligible.\n const handler = this.events.events[messageType as string]?.[0];\n\n if (handler === undefined) {\n // no warning without the hook: the client already gets an explicit error\n if (this.room.onUnhandledMessage !== undefined) {\n this.#reportUnhandled(client, messageType, message);\n }\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, {\n name: \"no_handler\",\n message: `room \"${this.room.roomName}\" has no onMessage(\"${messageType}\") handler to answer this request.`,\n });\n return;\n }\n\n const ctx = new DispatchContext(requestId);\n\n // Sync handlers reply in this tick with no promise machinery; only a thenable\n // return defers to the microtask queue. A sync throw (unwrapped handler) and an\n // async rejection both fall to the ERROR reply. With onUncaughtException set the\n // handler is wrapped (swallows its own errors), so it never throws / its promise\n // resolves undefined, and the error reports there.\n let response: any;\n try {\n response = handler(client, message, ctx);\n if (response !== null && typeof response === 'object' && typeof response.then === 'function') {\n response.then(\n (resolved: any) => this.#finalizeRequest(client, requestId, ctx, resolved),\n (e: any) => {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n },\n );\n return;\n }\n } catch (e: any) {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n this.#finalizeRequest(client, requestId, ctx, response);\n }\n\n /** Finalize a request: project the handler's outcome onto a ROOM_RESPONSE reply \u2014\n * `ctx.reject` \u2192 REJECTED(reason), `ctx.resolve(value)` \u2192 OK(value), else OK(return). */\n #finalizeRequest(client: Client, requestId: number, ctx: DispatchContext, response: any): void {\n if (ctx._outcome === OUTCOME_REJECTED) {\n this.#replyToRequest(client, requestId, ResponseStatus.REJECTED, ctx._reason);\n } else if (ctx._outcome === OUTCOME_RESOLVED) {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, ctx._value);\n } else {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, response);\n }\n }\n\n /** Dispatch a `ROOM_DATA_BYTES` frame: raw bytes routed to `_$b`-prefixed handlers. */\n onDataBytes(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message: any = buffer.subarray(it.offset, buffer.byteLength);\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n const bytesMessageType = `_$b${messageType}`;\n\n // custom message validation\n try {\n if (this.validators[bytesMessageType] !== undefined) {\n message = standardValidate(this.validators[bytesMessageType], message);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[bytesMessageType]) {\n this.#dispatch(this.events.events[bytesMessageType], client, message);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /** Emit a ROOM_RESPONSE reply immediately. */\n #replyToRequest(client: Client, requestId: number, status: ResponseStatus, payload?: any): void {\n debugMessage(\"response #%d: status=%d -> %j (roomId: %s)\", requestId, status, payload, this.room.roomId);\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_RESPONSE](requestId, status, payload));\n }\n\n /** No handler for the type: error the client in dev, drop it in production. */\n #noHandler(client: Client, messageType: string | number, message: unknown): void {\n const errorMessage = `room onMessage for \"${messageType}\" not registered.`;\n\n if (this.room.onUnhandledMessage !== undefined) {\n this.#reportUnhandled(client, messageType, message);\n\n } else {\n logger.warn(`room \"${this.room.roomName}\" (roomId: ${this.room.roomId}): client '${client.sessionId}' sent \"${messageType}\", which has no onMessage() handler. ${(isDevMode)\n ? \"Replied with an INVALID_PAYLOAD error (outside devMode, the client is disconnected).\"\n : \"Disconnecting the client.\"}`);\n }\n\n if (isDevMode) {\n client.error(ErrorCode.INVALID_PAYLOAD, errorMessage);\n } else {\n // frames already received still arrive after leave(); Room#_onMessage skips them\n client.state = ClientState.LEAVING;\n client.leave(CloseCode.WITH_ERROR, errorMessage);\n }\n }\n\n /** Call `room.onUnhandledMessage`. A throw or rejection goes to\n * `onUncaughtException` when set, or is logged; it never stops the default. */\n #reportUnhandled(client: Client, messageType: string | number, message: unknown): void {\n const report = (e: any) => {\n if (this.room.onUncaughtException !== undefined) {\n this.room.onUncaughtException(new OnMessageException<Room<any>, any>(e, e?.message, client, message, messageType), 'onMessage');\n } else {\n debugAndPrintError(e);\n }\n };\n\n try {\n const result = this.room.onUnhandledMessage!(client, messageType, message) as any;\n if (typeof result?.catch === 'function') {\n result.catch(report);\n }\n } catch (e: any) {\n report(e);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  import { type Iterator } from '@colyseus/schema';
2
2
  import { type StandardSchemaV1 } from './utils/StandardSchema.ts';
3
- import type { Client, ClientPrivate } from './Transport.ts';
3
+ import { type Client, type ClientPrivate } from './Transport.ts';
4
4
  import type { Room } from './Room.ts';
5
5
  /**
6
6
  * Per-room message-routing layer, owned by {@link Room}. Owns the user-message
@@ -9,7 +9,7 @@ import type { Room } from './Room.ts';
9
9
  * ROOM_DATA_BYTES). Room's `_onMessage` keeps the protocol/lifecycle frames
10
10
  * (JOIN/PING/LEAVE/input) and calls one of these per frame, preserving the
11
11
  * original dispatch order. Reads back into its Room only for `onUncaughtException`
12
- * (handler wrapping) and `roomId`/`roomName` (logging).
12
+ * (handler wrapping), `onUnhandledMessage` and `roomId`/`roomName` (logging).
13
13
  *
14
14
  * @internal
15
15
  */
@@ -7,7 +7,9 @@ import { createNanoEvents } from "./utils/nanoevents.mjs";
7
7
  import { standardValidate } from "./utils/StandardSchema.mjs";
8
8
  import { isDevMode } from "./utils/DevMode.mjs";
9
9
  import { debugMessage, debugAndPrintError } from "./Debug.mjs";
10
+ import { logger } from "./Logger.mjs";
10
11
  import { OnMessageException } from "./errors/RoomExceptions.mjs";
12
+ import { ClientState } from "./Transport.mjs";
11
13
  function toResponseError(e) {
12
14
  if (e instanceof Error) {
13
15
  const code = e.code;
@@ -178,6 +180,9 @@ var RoomMessages = class {
178
180
  }
179
181
  const handler = this.events.events[messageType]?.[0];
180
182
  if (handler === void 0) {
183
+ if (this.room.onUnhandledMessage !== void 0) {
184
+ this.#reportUnhandled(client, messageType, message);
185
+ }
181
186
  this.#replyToRequest(client, requestId, ResponseStatus.ERROR, {
182
187
  name: "no_handler",
183
188
  message: `room "${this.room.roomName}" has no onMessage("${messageType}") handler to answer this request.`
@@ -245,15 +250,39 @@ var RoomMessages = class {
245
250
  client.enqueueRaw(getMessageBytes[Protocol.ROOM_RESPONSE](requestId, status, payload));
246
251
  }
247
252
  /** No handler for the type: error the client in dev, drop it in production. */
248
- #noHandler(client, messageType, _message) {
253
+ #noHandler(client, messageType, message) {
249
254
  const errorMessage = `room onMessage for "${messageType}" not registered.`;
250
- debugMessage(`${errorMessage} (roomId: ${this.room.roomId})`);
255
+ if (this.room.onUnhandledMessage !== void 0) {
256
+ this.#reportUnhandled(client, messageType, message);
257
+ } else {
258
+ logger.warn(`room "${this.room.roomName}" (roomId: ${this.room.roomId}): client '${client.sessionId}' sent "${messageType}", which has no onMessage() handler. ${isDevMode ? "Replied with an INVALID_PAYLOAD error (outside devMode, the client is disconnected)." : "Disconnecting the client."}`);
259
+ }
251
260
  if (isDevMode) {
252
261
  client.error(ErrorCode.INVALID_PAYLOAD, errorMessage);
253
262
  } else {
263
+ client.state = ClientState.LEAVING;
254
264
  client.leave(CloseCode.WITH_ERROR, errorMessage);
255
265
  }
256
266
  }
267
+ /** Call `room.onUnhandledMessage`. A throw or rejection goes to
268
+ * `onUncaughtException` when set, or is logged; it never stops the default. */
269
+ #reportUnhandled(client, messageType, message) {
270
+ const report = (e) => {
271
+ if (this.room.onUncaughtException !== void 0) {
272
+ this.room.onUncaughtException(new OnMessageException(e, e?.message, client, message, messageType), "onMessage");
273
+ } else {
274
+ debugAndPrintError(e);
275
+ }
276
+ };
277
+ try {
278
+ const result = this.room.onUnhandledMessage(client, messageType, message);
279
+ if (typeof result?.catch === "function") {
280
+ result.catch(report);
281
+ }
282
+ } catch (e) {
283
+ report(e);
284
+ }
285
+ }
257
286
  };
258
287
  export {
259
288
  RoomMessages
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/RoomMessages.ts"],
4
- "sourcesContent": ["import { decode, type Iterator } from '@colyseus/schema';\nimport { unpack } from 'msgpackr';\n\nimport { CloseCode, ErrorCode, Protocol, ResponseStatus, type MessageContext } from '@colyseus/shared-types';\nimport { getMessageBytes } from './Protocol.ts';\nimport { createNanoEvents } from './utils/nanoevents.ts';\nimport { standardValidate, type StandardSchemaV1 } from './utils/StandardSchema.ts';\nimport { isDevMode } from './utils/DevMode.ts';\nimport { debugMessage, debugAndPrintError } from './Debug.ts';\nimport { OnMessageException } from './errors/RoomExceptions.ts';\nimport type { Client, ClientPrivate } from './Transport.ts';\nimport type { Room } from './Room.ts';\n\n/** Normalize a thrown value into the `{ name, message, code? }` shape echoed in a\n * ROOM_RESPONSE error reply. */\nfunction toResponseError(e: any): { name: string; message: string; code?: any } {\n if (e instanceof Error) {\n const code = (e as any).code;\n return (code !== undefined)\n ? { name: e.name, message: e.message, code }\n : { name: e.name, message: e.message };\n }\n return { name: \"Error\", message: String(e) };\n}\n\n// The handler's terminal action \u2014 NOT the wire ResponseStatus: `none` and\n// `resolved` both reply OK (plain return value vs `ctx.resolve(value)`), a\n// distinction a single OK can't carry; ERROR is never a handler decision\n// (thrown/no-handler). Projected onto ResponseStatus once, in onRequest's reply.\nconst OUTCOME_NONE = 0, OUTCOME_REJECTED = 1, OUTCOME_RESOLVED = 2;\n\n/**\n * Runtime {@link MessageContext} for a {@link Protocol.ROOM_REQUEST} dispatch.\n * `reject`/`resolve` record the decision on the ctx; `onRequest` reads it after the\n * handler returns, so a bare side-effecting call works as well as `return\n * ctx.reject(r)` (the branded return is compile-time only \u2014 the runtime return is\n * unused).\n */\nclass DispatchContext implements MessageContext {\n readonly id: number | undefined;\n _outcome = OUTCOME_NONE;\n _reason: any = undefined;\n _value: any = undefined;\n\n constructor(id: number | undefined) {\n this.id = id;\n }\n\n reject(reason?: any): any {\n this._outcome = OUTCOME_REJECTED;\n this._reason = reason;\n return undefined;\n }\n\n resolve(value?: any): any {\n this._outcome = OUTCOME_RESOLVED;\n this._value = value;\n return undefined;\n }\n}\n\n/** Shared no-op context for fire-and-forget `ROOM_DATA` (no reply channel, so\n * `reject`/`resolve` go nowhere). `id` is `undefined`; zero per-message allocation. */\nconst SEND_CONTEXT: MessageContext = Object.freeze({\n id: undefined,\n reject: () => undefined as any,\n resolve: () => undefined as any,\n});\n\n/**\n * Per-room message-routing layer, owned by {@link Room}. Owns the user-message\n * handler registry (`onMessage`/`onMessageBytes` + per-type validators) and\n * decodes/dispatches the user-message wire frames (ROOM_DATA / ROOM_REQUEST /\n * ROOM_DATA_BYTES). Room's `_onMessage` keeps the protocol/lifecycle frames\n * (JOIN/PING/LEAVE/input) and calls one of these per frame, preserving the\n * original dispatch order. Reads back into its Room only for `onUncaughtException`\n * (handler wrapping) and `roomId`/`roomName` (logging).\n *\n * @internal\n */\nexport class RoomMessages {\n private room: Room<any>;\n\n /** Handler registry (nanoevents). Public so Room can re-expose it as\n * `onMessageEvents` for @colyseus/playground introspection and\n * @colyseus/testing handler-swapping. */\n events = createNanoEvents();\n\n /** Per-type StandardSchema validators. Public for the same reason as\n * {@link events} (`onMessageValidators`). */\n // null-prototype: keyed by client-supplied message type (colyseus/colyseus#951)\n validators: { [type: string]: StandardSchemaV1 } = Object.create(null);\n\n constructor(room: Room<any>) {\n this.room = room;\n }\n\n /**\n * Register a handler (body of {@link Room.onMessage}). Wraps the callback via\n * the room's `onUncaughtException` when set, stores the validator, and returns\n * the unbind closure.\n */\n on(\n _messageType: '*' | string | number,\n _validationSchema: StandardSchemaV1 | ((...args: any[]) => void),\n _callback?: (...args: any[]) => void,\n ): () => void {\n const messageType = _messageType.toString();\n\n const validationSchema = (typeof _callback === 'function')\n ? _validationSchema as StandardSchemaV1\n : undefined;\n\n const callback = (validationSchema === undefined)\n ? _validationSchema as (...args: any[]) => void\n : _callback;\n\n const removeListener = this.events.on(messageType, (this.room.onUncaughtException !== undefined)\n ? this.#wrapMessageHandler(callback, _messageType)\n : callback);\n\n if (validationSchema !== undefined) {\n this.validators[messageType] = validationSchema;\n }\n\n // returns a method to unbind the callback\n return () => {\n removeListener();\n if (this.events.events[messageType].length === 0) {\n delete this.validators[messageType];\n }\n };\n }\n\n /**\n * Wrap a user message handler so a sync throw or async rejection is reported to\n * `room.onUncaughtException` as an {@link OnMessageException} carrying the correct\n * `client` / `payload` / `type`. The handler is invoked with a different argument\n * shape per dispatch path, so those fields are normalized here rather than relying\n * on positional forwarding (which silently mis-mapped `type` once a context arg was\n * added \u2014 see ROOM_DATA/ROOM_REQUEST passing `SEND_CONTEXT`/`ctx`):\n *\n * - typed handler (DATA / REQUEST / BYTES): `(client, payload, ctx?)` \u2192 `type` is\n * the registered `_messageType`.\n * - wildcard `'*'` handler: `(client, type, payload)` \u2192 `type` is\n * the received message type.\n *\n * The error is swallowed (never rethrown) so a faulty handler can't break dispatch;\n * the handler's return value (or promise) is passed through for the REQUEST path.\n */\n #wrapMessageHandler(callback: (...args: any[]) => any, registeredType: '*' | string | number) {\n const onError = this.room.onUncaughtException!.bind(this.room);\n const isWildcard = (registeredType === '*');\n\n return (...args: any[]): any => {\n const report = (e: Error) => onError(\n new OnMessageException(\n e, e.message,\n args[0], // client\n isWildcard ? args[2] : args[1], // payload\n isWildcard ? args[1] : registeredType, // type\n ),\n 'onMessage',\n );\n\n try {\n const result = callback(...args);\n return (typeof result?.catch === 'function') ? result.catch(report) : result;\n } catch (e: any) {\n report(e);\n }\n };\n }\n\n /** Dispatch a `ROOM_DATA` frame: decode type + msgpack payload, validate, emit. */\n onData(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n // custom message validation\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[messageType]) {\n this.#dispatch(this.events.events[messageType], client, message, SEND_CONTEXT);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /**\n * Call the handlers of a fire-and-forget frame. With no reply to carry an error\n * and nothing above this to catch one, a throw would reach the socket's event\n * listener and shut the process down, along with every other room in it. So\n * errors are logged, and the remaining handlers still run. Handlers wrapped for\n * `onUncaughtException` report their own errors and never throw here.\n */\n #dispatch(handlers: Array<(...args: any[]) => any>, ...args: any[]): void {\n for (let i = 0, length = handlers.length; i < length; i++) {\n try {\n const result = handlers[i](...args);\n if (typeof result?.catch === 'function') {\n result.catch(debugAndPrintError);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n }\n }\n }\n\n /** Dispatch a `ROOM_REQUEST` frame: same handlers as ROOM_DATA, but the client\n * opted into a reply, so echo the `requestId` with the outcome. The handler's\n * reply is decided by what it returns / does to `ctx`: `ctx.reject(reason)` \u2192\n * `REJECTED(reason)`, `ctx.resolve(value)` \u2192 `OK(value)`, a thrown error or\n * missing handler \u2192 `ERROR`, else `OK(return)`. */\n onRequest(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const requestId = decode.number(buffer, it);\n\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"request #%d: '%s' -> %j (roomId: %s)\", requestId, messageType, message, this.room.roomId);\n\n // custom message validation (shared with the ROOM_DATA path)\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n // Reply with an error so the caller's pending request resolves instead of timing out.\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n // Answered by the FIRST handler registered for the type (emit would discard\n // returns); wildcard handlers have no response contract, so they're ineligible.\n const handler = this.events.events[messageType as string]?.[0];\n\n if (handler === undefined) {\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, {\n name: \"no_handler\",\n message: `room \"${this.room.roomName}\" has no onMessage(\"${messageType}\") handler to answer this request.`,\n });\n return;\n }\n\n const ctx = new DispatchContext(requestId);\n\n // Sync handlers reply in this tick with no promise machinery; only a thenable\n // return defers to the microtask queue. A sync throw (unwrapped handler) and an\n // async rejection both fall to the ERROR reply. With onUncaughtException set the\n // handler is wrapped (swallows its own errors), so it never throws / its promise\n // resolves undefined, and the error reports there.\n let response: any;\n try {\n response = handler(client, message, ctx);\n if (response !== null && typeof response === 'object' && typeof response.then === 'function') {\n response.then(\n (resolved: any) => this.#finalizeRequest(client, requestId, ctx, resolved),\n (e: any) => {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n },\n );\n return;\n }\n } catch (e: any) {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n this.#finalizeRequest(client, requestId, ctx, response);\n }\n\n /** Finalize a request: project the handler's outcome onto a ROOM_RESPONSE reply \u2014\n * `ctx.reject` \u2192 REJECTED(reason), `ctx.resolve(value)` \u2192 OK(value), else OK(return). */\n #finalizeRequest(client: Client, requestId: number, ctx: DispatchContext, response: any): void {\n if (ctx._outcome === OUTCOME_REJECTED) {\n this.#replyToRequest(client, requestId, ResponseStatus.REJECTED, ctx._reason);\n } else if (ctx._outcome === OUTCOME_RESOLVED) {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, ctx._value);\n } else {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, response);\n }\n }\n\n /** Dispatch a `ROOM_DATA_BYTES` frame: raw bytes routed to `_$b`-prefixed handlers. */\n onDataBytes(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message: any = buffer.subarray(it.offset, buffer.byteLength);\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n const bytesMessageType = `_$b${messageType}`;\n\n // custom message validation\n try {\n if (this.validators[bytesMessageType] !== undefined) {\n message = standardValidate(this.validators[bytesMessageType], message);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[bytesMessageType]) {\n this.#dispatch(this.events.events[bytesMessageType], client, message);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /** Emit a ROOM_RESPONSE reply immediately. */\n #replyToRequest(client: Client, requestId: number, status: ResponseStatus, payload?: any): void {\n debugMessage(\"response #%d: status=%d -> %j (roomId: %s)\", requestId, status, payload, this.room.roomId);\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_RESPONSE](requestId, status, payload));\n }\n\n /** No handler for the type: error the client in dev, drop it in production. */\n #noHandler(client: Client, messageType: string | number, _message: unknown): void {\n const errorMessage = `room onMessage for \"${messageType}\" not registered.`;\n debugMessage(`${errorMessage} (roomId: ${this.room.roomId})`);\n if (isDevMode) {\n client.error(ErrorCode.INVALID_PAYLOAD, errorMessage);\n } else {\n client.leave(CloseCode.WITH_ERROR, errorMessage);\n }\n }\n}\n"],
5
- "mappings": ";AAAA,SAAS,cAA6B;AACtC,SAAS,cAAc;AAEvB,SAAS,WAAW,WAAW,UAAU,sBAA2C;AACpF,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,wBAA+C;AACxD,SAAS,iBAAiB;AAC1B,SAAS,cAAc,0BAA0B;AACjD,SAAS,0BAA0B;AAMnC,SAAS,gBAAgB,GAAuD;AAC9E,MAAI,aAAa,OAAO;AACtB,UAAM,OAAQ,EAAU;AACxB,WAAQ,SAAS,SACb,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,SAAS,KAAK,IACzC,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ;AAAA,EACzC;AACA,SAAO,EAAE,MAAM,SAAS,SAAS,OAAO,CAAC,EAAE;AAC7C;AAMA,IAAM,eAAe;AAArB,IAAwB,mBAAmB;AAA3C,IAA8C,mBAAmB;AASjE,IAAM,kBAAN,MAAgD;AAAA,EAM9C,YAAY,IAAwB;AAJpC,oBAAW;AACX,mBAAe;AACf,kBAAc;AAGZ,SAAK,KAAK;AAAA,EACZ;AAAA,EAEA,OAAO,QAAmB;AACxB,SAAK,WAAW;AAChB,SAAK,UAAU;AACf,WAAO;AAAA,EACT;AAAA,EAEA,QAAQ,OAAkB;AACxB,SAAK,WAAW;AAChB,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AACF;AAIA,IAAM,eAA+B,OAAO,OAAO;AAAA,EACjD,IAAI;AAAA,EACJ,QAAQ,MAAM;AAAA,EACd,SAAS,MAAM;AACjB,CAAC;AAaM,IAAM,eAAN,MAAmB;AAAA,EAaxB,YAAY,MAAiB;AAP7B;AAAA;AAAA;AAAA,kBAAS,iBAAiB;AAK1B;AAAA;AAAA;AAAA,sBAAmD,uBAAO,OAAO,IAAI;AAGnE,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,GACE,cACA,mBACA,WACY;AACZ,UAAM,cAAc,aAAa,SAAS;AAE1C,UAAM,mBAAoB,OAAO,cAAc,aAC3C,oBACA;AAEJ,UAAM,WAAY,qBAAqB,SACnC,oBACA;AAEJ,UAAM,iBAAiB,KAAK,OAAO,GAAG,aAAc,KAAK,KAAK,wBAAwB,SAClF,KAAK,oBAAoB,UAAU,YAAY,IAC/C,QAAQ;AAEZ,QAAI,qBAAqB,QAAW;AAClC,WAAK,WAAW,WAAW,IAAI;AAAA,IACjC;AAGA,WAAO,MAAM;AACX,qBAAe;AACf,UAAI,KAAK,OAAO,OAAO,WAAW,EAAE,WAAW,GAAG;AAChD,eAAO,KAAK,WAAW,WAAW;AAAA,MACpC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,oBAAoB,UAAmC,gBAAuC;AAC5F,UAAM,UAAU,KAAK,KAAK,oBAAqB,KAAK,KAAK,IAAI;AAC7D,UAAM,aAAc,mBAAmB;AAEvC,WAAO,IAAI,SAAqB;AAC9B,YAAM,SAAS,CAAC,MAAa;AAAA,QAC3B,IAAI;AAAA,UACF;AAAA,UAAG,EAAE;AAAA,UACL,KAAK,CAAC;AAAA;AAAA,UACN,aAAa,KAAK,CAAC,IAAI,KAAK,CAAC;AAAA;AAAA,UAC7B,aAAa,KAAK,CAAC,IAAI;AAAA;AAAA,QACzB;AAAA,QACA;AAAA,MACF;AAEA,UAAI;AACF,cAAM,SAAS,SAAS,GAAG,IAAI;AAC/B,eAAQ,OAAO,QAAQ,UAAU,aAAc,OAAO,MAAM,MAAM,IAAI;AAAA,MACxE,SAAS,GAAQ;AACf,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,OAAO,QAAgC,QAAgB,IAAoB;AACzE,UAAM,cAAe,OAAO,YAAY,QAAQ,EAAE,IAC9C,OAAO,OAAO,QAAQ,EAAE,IACxB,OAAO,OAAO,QAAQ,EAAE;AAE5B,QAAI;AACJ,QAAI;AACF,gBAAW,OAAO,aAAa,GAAG,SAC9B,OAAO,OAAO,SAAS,GAAG,QAAQ,OAAO,UAAU,CAAC,IACpD;AACJ,mBAAa,qCAAqC,aAAa,SAAS,KAAK,KAAK,MAAM;AAGxF,UAAI,KAAK,WAAW,WAAW,MAAM,QAAW;AAC9C,kBAAU,iBAAiB,KAAK,WAAW,WAAW,GAAG,OAAO;AAAA,MAClE;AAAA,IAEF,SAAS,GAAQ;AACf,yBAAmB,CAAC;AACpB,aAAO,MAAM,UAAU,UAAU;AACjC;AAAA,IACF;AAEA,QAAI,KAAK,OAAO,OAAO,WAAW,GAAG;AACnC,WAAK,UAAU,KAAK,OAAO,OAAO,WAAW,GAAG,QAAQ,SAAS,YAAY;AAAA,IAE/E,WAAW,KAAK,OAAO,OAAO,GAAG,GAAG;AAClC,WAAK,UAAU,KAAK,OAAO,OAAO,GAAG,GAAG,QAAQ,aAAa,OAAO;AAAA,IAEtE,OAAO;AACL,WAAK,WAAW,QAAQ,aAAa,OAAO;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,UAAU,aAA6C,MAAmB;AACxE,aAAS,IAAI,GAAG,SAAS,SAAS,QAAQ,IAAI,QAAQ,KAAK;AACzD,UAAI;AACF,cAAM,SAAS,SAAS,CAAC,EAAE,GAAG,IAAI;AAClC,YAAI,OAAO,QAAQ,UAAU,YAAY;AACvC,iBAAO,MAAM,kBAAkB;AAAA,QACjC;AAAA,MACF,SAAS,GAAQ;AACf,2BAAmB,CAAC;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,UAAU,QAAgC,QAAgB,IAAoB;AAC5E,UAAM,YAAY,OAAO,OAAO,QAAQ,EAAE;AAE1C,UAAM,cAAe,OAAO,YAAY,QAAQ,EAAE,IAC9C,OAAO,OAAO,QAAQ,EAAE,IACxB,OAAO,OAAO,QAAQ,EAAE;AAE5B,QAAI;AACJ,QAAI;AACF,gBAAW,OAAO,aAAa,GAAG,SAC9B,OAAO,OAAO,SAAS,GAAG,QAAQ,OAAO,UAAU,CAAC,IACpD;AACJ,mBAAa,wCAAwC,WAAW,aAAa,SAAS,KAAK,KAAK,MAAM;AAGtG,UAAI,KAAK,WAAW,WAAW,MAAM,QAAW;AAC9C,kBAAU,iBAAiB,KAAK,WAAW,WAAW,GAAG,OAAO;AAAA,MAClE;AAAA,IAEF,SAAS,GAAQ;AAEf,yBAAmB,CAAC;AACpB,WAAK,gBAAgB,QAAQ,WAAW,eAAe,OAAO,gBAAgB,CAAC,CAAC;AAChF;AAAA,IACF;AAIA,UAAM,UAAU,KAAK,OAAO,OAAO,WAAqB,IAAI,CAAC;AAE7D,QAAI,YAAY,QAAW;AACzB,WAAK,gBAAgB,QAAQ,WAAW,eAAe,OAAO;AAAA,QAC5D,MAAM;AAAA,QACN,SAAS,SAAS,KAAK,KAAK,QAAQ,uBAAuB,WAAW;AAAA,MACxE,CAAC;AACD;AAAA,IACF;AAEA,UAAM,MAAM,IAAI,gBAAgB,SAAS;AAOzC,QAAI;AACJ,QAAI;AACF,iBAAW,QAAQ,QAAQ,SAAS,GAAG;AACvC,UAAI,aAAa,QAAQ,OAAO,aAAa,YAAY,OAAO,SAAS,SAAS,YAAY;AAC5F,iBAAS;AAAA,UACP,CAAC,aAAkB,KAAK,iBAAiB,QAAQ,WAAW,KAAK,QAAQ;AAAA,UACzE,CAAC,MAAW;AACV,+BAAmB,CAAC;AACpB,iBAAK,gBAAgB,QAAQ,WAAW,eAAe,OAAO,gBAAgB,CAAC,CAAC;AAAA,UAClF;AAAA,QACF;AACA;AAAA,MACF;AAAA,IACF,SAAS,GAAQ;AACf,yBAAmB,CAAC;AACpB,WAAK,gBAAgB,QAAQ,WAAW,eAAe,OAAO,gBAAgB,CAAC,CAAC;AAChF;AAAA,IACF;AAEA,SAAK,iBAAiB,QAAQ,WAAW,KAAK,QAAQ;AAAA,EACxD;AAAA;AAAA;AAAA,EAIA,iBAAiB,QAAgB,WAAmB,KAAsB,UAAqB;AAC7F,QAAI,IAAI,aAAa,kBAAkB;AACrC,WAAK,gBAAgB,QAAQ,WAAW,eAAe,UAAU,IAAI,OAAO;AAAA,IAC9E,WAAW,IAAI,aAAa,kBAAkB;AAC5C,WAAK,gBAAgB,QAAQ,WAAW,eAAe,IAAI,IAAI,MAAM;AAAA,IACvE,OAAO;AACL,WAAK,gBAAgB,QAAQ,WAAW,eAAe,IAAI,QAAQ;AAAA,IACrE;AAAA,EACF;AAAA;AAAA,EAGA,YAAY,QAAgC,QAAgB,IAAoB;AAC9E,UAAM,cAAe,OAAO,YAAY,QAAQ,EAAE,IAC9C,OAAO,OAAO,QAAQ,EAAE,IACxB,OAAO,OAAO,QAAQ,EAAE;AAE5B,QAAI,UAAe,OAAO,SAAS,GAAG,QAAQ,OAAO,UAAU;AAC/D,iBAAa,qCAAqC,aAAa,SAAS,KAAK,KAAK,MAAM;AAExF,UAAM,mBAAmB,MAAM,WAAW;AAG1C,QAAI;AACF,UAAI,KAAK,WAAW,gBAAgB,MAAM,QAAW;AACnD,kBAAU,iBAAiB,KAAK,WAAW,gBAAgB,GAAG,OAAO;AAAA,MACvE;AAAA,IACF,SAAS,GAAQ;AACf,yBAAmB,CAAC;AACpB,aAAO,MAAM,UAAU,UAAU;AACjC;AAAA,IACF;AAEA,QAAI,KAAK,OAAO,OAAO,gBAAgB,GAAG;AACxC,WAAK,UAAU,KAAK,OAAO,OAAO,gBAAgB,GAAG,QAAQ,OAAO;AAAA,IAEtE,WAAW,KAAK,OAAO,OAAO,GAAG,GAAG;AAClC,WAAK,UAAU,KAAK,OAAO,OAAO,GAAG,GAAG,QAAQ,aAAa,OAAO;AAAA,IAEtE,OAAO;AACL,WAAK,WAAW,QAAQ,aAAa,OAAO;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA,EAGA,gBAAgB,QAAgB,WAAmB,QAAwB,SAAqB;AAC9F,iBAAa,8CAA8C,WAAW,QAAQ,SAAS,KAAK,KAAK,MAAM;AACvG,WAAO,WAAW,gBAAgB,SAAS,aAAa,EAAE,WAAW,QAAQ,OAAO,CAAC;AAAA,EACvF;AAAA;AAAA,EAGA,WAAW,QAAgB,aAA8B,UAAyB;AAChF,UAAM,eAAe,uBAAuB,WAAW;AACvD,iBAAa,GAAG,YAAY,aAAa,KAAK,KAAK,MAAM,GAAG;AAC5D,QAAI,WAAW;AACb,aAAO,MAAM,UAAU,iBAAiB,YAAY;AAAA,IACtD,OAAO;AACL,aAAO,MAAM,UAAU,YAAY,YAAY;AAAA,IACjD;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { decode, type Iterator } from '@colyseus/schema';\nimport { unpack } from 'msgpackr';\n\nimport { CloseCode, ErrorCode, Protocol, ResponseStatus, type MessageContext } from '@colyseus/shared-types';\nimport { getMessageBytes } from './Protocol.ts';\nimport { createNanoEvents } from './utils/nanoevents.ts';\nimport { standardValidate, type StandardSchemaV1 } from './utils/StandardSchema.ts';\nimport { isDevMode } from './utils/DevMode.ts';\nimport { debugMessage, debugAndPrintError } from './Debug.ts';\nimport { logger } from './Logger.ts';\nimport { OnMessageException } from './errors/RoomExceptions.ts';\nimport { ClientState, type Client, type ClientPrivate } from './Transport.ts';\nimport type { Room } from './Room.ts';\n\n/** Normalize a thrown value into the `{ name, message, code? }` shape echoed in a\n * ROOM_RESPONSE error reply. */\nfunction toResponseError(e: any): { name: string; message: string; code?: any } {\n if (e instanceof Error) {\n const code = (e as any).code;\n return (code !== undefined)\n ? { name: e.name, message: e.message, code }\n : { name: e.name, message: e.message };\n }\n return { name: \"Error\", message: String(e) };\n}\n\n// The handler's terminal action \u2014 NOT the wire ResponseStatus: `none` and\n// `resolved` both reply OK (plain return value vs `ctx.resolve(value)`), a\n// distinction a single OK can't carry; ERROR is never a handler decision\n// (thrown/no-handler). Projected onto ResponseStatus once, in onRequest's reply.\nconst OUTCOME_NONE = 0, OUTCOME_REJECTED = 1, OUTCOME_RESOLVED = 2;\n\n/**\n * Runtime {@link MessageContext} for a {@link Protocol.ROOM_REQUEST} dispatch.\n * `reject`/`resolve` record the decision on the ctx; `onRequest` reads it after the\n * handler returns, so a bare side-effecting call works as well as `return\n * ctx.reject(r)` (the branded return is compile-time only \u2014 the runtime return is\n * unused).\n */\nclass DispatchContext implements MessageContext {\n readonly id: number | undefined;\n _outcome = OUTCOME_NONE;\n _reason: any = undefined;\n _value: any = undefined;\n\n constructor(id: number | undefined) {\n this.id = id;\n }\n\n reject(reason?: any): any {\n this._outcome = OUTCOME_REJECTED;\n this._reason = reason;\n return undefined;\n }\n\n resolve(value?: any): any {\n this._outcome = OUTCOME_RESOLVED;\n this._value = value;\n return undefined;\n }\n}\n\n/** Shared no-op context for fire-and-forget `ROOM_DATA` (no reply channel, so\n * `reject`/`resolve` go nowhere). `id` is `undefined`; zero per-message allocation. */\nconst SEND_CONTEXT: MessageContext = Object.freeze({\n id: undefined,\n reject: () => undefined as any,\n resolve: () => undefined as any,\n});\n\n/**\n * Per-room message-routing layer, owned by {@link Room}. Owns the user-message\n * handler registry (`onMessage`/`onMessageBytes` + per-type validators) and\n * decodes/dispatches the user-message wire frames (ROOM_DATA / ROOM_REQUEST /\n * ROOM_DATA_BYTES). Room's `_onMessage` keeps the protocol/lifecycle frames\n * (JOIN/PING/LEAVE/input) and calls one of these per frame, preserving the\n * original dispatch order. Reads back into its Room only for `onUncaughtException`\n * (handler wrapping), `onUnhandledMessage` and `roomId`/`roomName` (logging).\n *\n * @internal\n */\nexport class RoomMessages {\n private room: Room<any>;\n\n /** Handler registry (nanoevents). Public so Room can re-expose it as\n * `onMessageEvents` for @colyseus/playground introspection and\n * @colyseus/testing handler-swapping. */\n events = createNanoEvents();\n\n /** Per-type StandardSchema validators. Public for the same reason as\n * {@link events} (`onMessageValidators`). */\n // null-prototype: keyed by client-supplied message type (colyseus/colyseus#951)\n validators: { [type: string]: StandardSchemaV1 } = Object.create(null);\n\n constructor(room: Room<any>) {\n this.room = room;\n }\n\n /**\n * Register a handler (body of {@link Room.onMessage}). Wraps the callback via\n * the room's `onUncaughtException` when set, stores the validator, and returns\n * the unbind closure.\n */\n on(\n _messageType: '*' | string | number,\n _validationSchema: StandardSchemaV1 | ((...args: any[]) => void),\n _callback?: (...args: any[]) => void,\n ): () => void {\n const messageType = _messageType.toString();\n\n const validationSchema = (typeof _callback === 'function')\n ? _validationSchema as StandardSchemaV1\n : undefined;\n\n const callback = (validationSchema === undefined)\n ? _validationSchema as (...args: any[]) => void\n : _callback;\n\n const removeListener = this.events.on(messageType, (this.room.onUncaughtException !== undefined)\n ? this.#wrapMessageHandler(callback, _messageType)\n : callback);\n\n if (validationSchema !== undefined) {\n this.validators[messageType] = validationSchema;\n }\n\n // returns a method to unbind the callback\n return () => {\n removeListener();\n if (this.events.events[messageType].length === 0) {\n delete this.validators[messageType];\n }\n };\n }\n\n /**\n * Wrap a user message handler so a sync throw or async rejection is reported to\n * `room.onUncaughtException` as an {@link OnMessageException} carrying the correct\n * `client` / `payload` / `type`. The handler is invoked with a different argument\n * shape per dispatch path, so those fields are normalized here rather than relying\n * on positional forwarding (which silently mis-mapped `type` once a context arg was\n * added \u2014 see ROOM_DATA/ROOM_REQUEST passing `SEND_CONTEXT`/`ctx`):\n *\n * - typed handler (DATA / REQUEST / BYTES): `(client, payload, ctx?)` \u2192 `type` is\n * the registered `_messageType`.\n * - wildcard `'*'` handler: `(client, type, payload)` \u2192 `type` is\n * the received message type.\n *\n * The error is swallowed (never rethrown) so a faulty handler can't break dispatch;\n * the handler's return value (or promise) is passed through for the REQUEST path.\n */\n #wrapMessageHandler(callback: (...args: any[]) => any, registeredType: '*' | string | number) {\n const onError = this.room.onUncaughtException!.bind(this.room);\n const isWildcard = (registeredType === '*');\n\n return (...args: any[]): any => {\n const report = (e: Error) => onError(\n new OnMessageException(\n e, e.message,\n args[0], // client\n isWildcard ? args[2] : args[1], // payload\n isWildcard ? args[1] : registeredType, // type\n ),\n 'onMessage',\n );\n\n try {\n const result = callback(...args);\n return (typeof result?.catch === 'function') ? result.catch(report) : result;\n } catch (e: any) {\n report(e);\n }\n };\n }\n\n /** Dispatch a `ROOM_DATA` frame: decode type + msgpack payload, validate, emit. */\n onData(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n // custom message validation\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[messageType]) {\n this.#dispatch(this.events.events[messageType], client, message, SEND_CONTEXT);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /**\n * Call the handlers of a fire-and-forget frame. With no reply to carry an error\n * and nothing above this to catch one, a throw would reach the socket's event\n * listener and shut the process down, along with every other room in it. So\n * errors are logged, and the remaining handlers still run. Handlers wrapped for\n * `onUncaughtException` report their own errors and never throw here.\n */\n #dispatch(handlers: Array<(...args: any[]) => any>, ...args: any[]): void {\n for (let i = 0, length = handlers.length; i < length; i++) {\n try {\n const result = handlers[i](...args);\n if (typeof result?.catch === 'function') {\n result.catch(debugAndPrintError);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n }\n }\n }\n\n /** Dispatch a `ROOM_REQUEST` frame: same handlers as ROOM_DATA, but the client\n * opted into a reply, so echo the `requestId` with the outcome. The handler's\n * reply is decided by what it returns / does to `ctx`: `ctx.reject(reason)` \u2192\n * `REJECTED(reason)`, `ctx.resolve(value)` \u2192 `OK(value)`, a thrown error or\n * missing handler \u2192 `ERROR`, else `OK(return)`. */\n onRequest(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const requestId = decode.number(buffer, it);\n\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message;\n try {\n message = (buffer.byteLength > it.offset)\n ? unpack(buffer.subarray(it.offset, buffer.byteLength))\n : undefined;\n debugMessage(\"request #%d: '%s' -> %j (roomId: %s)\", requestId, messageType, message, this.room.roomId);\n\n // custom message validation (shared with the ROOM_DATA path)\n if (this.validators[messageType] !== undefined) {\n message = standardValidate(this.validators[messageType], message);\n }\n\n } catch (e: any) {\n // Reply with an error so the caller's pending request resolves instead of timing out.\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n // Answered by the FIRST handler registered for the type (emit would discard\n // returns); wildcard handlers have no response contract, so they're ineligible.\n const handler = this.events.events[messageType as string]?.[0];\n\n if (handler === undefined) {\n // no warning without the hook: the client already gets an explicit error\n if (this.room.onUnhandledMessage !== undefined) {\n this.#reportUnhandled(client, messageType, message);\n }\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, {\n name: \"no_handler\",\n message: `room \"${this.room.roomName}\" has no onMessage(\"${messageType}\") handler to answer this request.`,\n });\n return;\n }\n\n const ctx = new DispatchContext(requestId);\n\n // Sync handlers reply in this tick with no promise machinery; only a thenable\n // return defers to the microtask queue. A sync throw (unwrapped handler) and an\n // async rejection both fall to the ERROR reply. With onUncaughtException set the\n // handler is wrapped (swallows its own errors), so it never throws / its promise\n // resolves undefined, and the error reports there.\n let response: any;\n try {\n response = handler(client, message, ctx);\n if (response !== null && typeof response === 'object' && typeof response.then === 'function') {\n response.then(\n (resolved: any) => this.#finalizeRequest(client, requestId, ctx, resolved),\n (e: any) => {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n },\n );\n return;\n }\n } catch (e: any) {\n debugAndPrintError(e);\n this.#replyToRequest(client, requestId, ResponseStatus.ERROR, toResponseError(e));\n return;\n }\n\n this.#finalizeRequest(client, requestId, ctx, response);\n }\n\n /** Finalize a request: project the handler's outcome onto a ROOM_RESPONSE reply \u2014\n * `ctx.reject` \u2192 REJECTED(reason), `ctx.resolve(value)` \u2192 OK(value), else OK(return). */\n #finalizeRequest(client: Client, requestId: number, ctx: DispatchContext, response: any): void {\n if (ctx._outcome === OUTCOME_REJECTED) {\n this.#replyToRequest(client, requestId, ResponseStatus.REJECTED, ctx._reason);\n } else if (ctx._outcome === OUTCOME_RESOLVED) {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, ctx._value);\n } else {\n this.#replyToRequest(client, requestId, ResponseStatus.OK, response);\n }\n }\n\n /** Dispatch a `ROOM_DATA_BYTES` frame: raw bytes routed to `_$b`-prefixed handlers. */\n onDataBytes(client: Client & ClientPrivate, buffer: Buffer, it: Iterator): void {\n const messageType = (decode.stringCheck(buffer, it))\n ? decode.string(buffer, it)\n : decode.number(buffer, it);\n\n let message: any = buffer.subarray(it.offset, buffer.byteLength);\n debugMessage(\"received: '%s' -> %j (roomId: %s)\", messageType, message, this.room.roomId);\n\n const bytesMessageType = `_$b${messageType}`;\n\n // custom message validation\n try {\n if (this.validators[bytesMessageType] !== undefined) {\n message = standardValidate(this.validators[bytesMessageType], message);\n }\n } catch (e: any) {\n debugAndPrintError(e);\n client.leave(CloseCode.WITH_ERROR);\n return;\n }\n\n if (this.events.events[bytesMessageType]) {\n this.#dispatch(this.events.events[bytesMessageType], client, message);\n\n } else if (this.events.events['*']) {\n this.#dispatch(this.events.events['*'], client, messageType, message);\n\n } else {\n this.#noHandler(client, messageType, message);\n }\n }\n\n /** Emit a ROOM_RESPONSE reply immediately. */\n #replyToRequest(client: Client, requestId: number, status: ResponseStatus, payload?: any): void {\n debugMessage(\"response #%d: status=%d -> %j (roomId: %s)\", requestId, status, payload, this.room.roomId);\n client.enqueueRaw(getMessageBytes[Protocol.ROOM_RESPONSE](requestId, status, payload));\n }\n\n /** No handler for the type: error the client in dev, drop it in production. */\n #noHandler(client: Client, messageType: string | number, message: unknown): void {\n const errorMessage = `room onMessage for \"${messageType}\" not registered.`;\n\n if (this.room.onUnhandledMessage !== undefined) {\n this.#reportUnhandled(client, messageType, message);\n\n } else {\n logger.warn(`room \"${this.room.roomName}\" (roomId: ${this.room.roomId}): client '${client.sessionId}' sent \"${messageType}\", which has no onMessage() handler. ${(isDevMode)\n ? \"Replied with an INVALID_PAYLOAD error (outside devMode, the client is disconnected).\"\n : \"Disconnecting the client.\"}`);\n }\n\n if (isDevMode) {\n client.error(ErrorCode.INVALID_PAYLOAD, errorMessage);\n } else {\n // frames already received still arrive after leave(); Room#_onMessage skips them\n client.state = ClientState.LEAVING;\n client.leave(CloseCode.WITH_ERROR, errorMessage);\n }\n }\n\n /** Call `room.onUnhandledMessage`. A throw or rejection goes to\n * `onUncaughtException` when set, or is logged; it never stops the default. */\n #reportUnhandled(client: Client, messageType: string | number, message: unknown): void {\n const report = (e: any) => {\n if (this.room.onUncaughtException !== undefined) {\n this.room.onUncaughtException(new OnMessageException<Room<any>, any>(e, e?.message, client, message, messageType), 'onMessage');\n } else {\n debugAndPrintError(e);\n }\n };\n\n try {\n const result = this.room.onUnhandledMessage!(client, messageType, message) as any;\n if (typeof result?.catch === 'function') {\n result.catch(report);\n }\n } catch (e: any) {\n report(e);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/build/index.cjs CHANGED
@@ -70,6 +70,7 @@ __export(index_exports, {
70
70
  attachToTestRoom: () => import_RoomPlugin.attachToTestRoom,
71
71
  basicAuth: () => import_router.basicAuth,
72
72
  compileSanitizer: () => import_InputBuffer.compileSanitizer,
73
+ compileSnapshot: () => import_InputBuffer.compileSnapshot,
73
74
  connectClientToRoom: () => import_Transport.connectClientToRoom,
74
75
  createAuthContext: () => import_Transport.createAuthContext,
75
76
  createEndpoint: () => import_router.createEndpoint,
@@ -183,6 +184,7 @@ var import_Logger = require("./Logger.cjs");
183
184
  attachToTestRoom,
184
185
  basicAuth,
185
186
  compileSanitizer,
187
+ compileSnapshot,
186
188
  connectClientToRoom,
187
189
  createAuthContext,
188
190
  createEndpoint,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import { ClockTimer as Clock, Delayed } from '@colyseus/timer';\n\n// Shared types - re-export from @colyseus/shared-types for convenience\nexport {\n Protocol,\n ProtocolModifier,\n ErrorCode,\n CloseCode,\n ResponseStatus,\n type InferState,\n type ExtractRoomMessages,\n type ExtractRoomClientMessages,\n type MessageContext,\n type Rejection,\n type Resolution,\n type ExtractRejectReason,\n type ExtractResponseType,\n} from '@colyseus/shared-types';\n\n// Core classes\nexport { Server, defineRoom, defineServer, registerRoomDefinitions, unregisterRoomDefinitions, applySimulatedLatency, parseLatencyEnv, type RoomDefinitions, type ServerOptions, type SDKTypes } from './Server.ts';\nexport { Room, RoomInternalState, validate, type RoomOptions, type DefineInputOptions, type SimulationCallback, type FixedTimestepCallback, type StepContext, type MessageHandlerWithFormat, type Messages, type ExtractRoomState, type ExtractRoomMetadata, type ExtractRoomClient } from './Room.ts';\nexport { InputBufferImpl, compileSanitizer } from './input/InputBuffer.ts';\nexport { type InputAccessor, type InputAPI, type NormalizedInputOptions, type ConsumeOptions, type IdleInput, type IdleContext, type SanitizeInput, type NumericFieldsOf } from './input/types.ts';\nexport { Rewind, RewindView, type RewindOptions, type RewindMode } from './Rewind.ts';\nexport {\n RoomPlugin, definePlugins, attachToTestRoom,\n type RoomPluginOrder, type PluginDependencies, type RoomPluginClass,\n} from './RoomPlugin.ts';\nexport { getMessageBytes } from './Protocol.ts';\nexport { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';\nexport { ServerError } from './errors/ServerError.ts';\n\nexport {\n type RoomException,\n type RoomMethodName,\n OnCreateException,\n OnAuthException,\n OnJoinException,\n OnLeaveException,\n OnDisposeException,\n OnMessageException,\n TimestepException,\n SimulationIntervalException,\n TimedEventException,\n} from './errors/RoomExceptions.ts';\n\n// MatchMaker\nimport * as matchMaker from './MatchMaker.ts';\nexport { matchMaker };\nexport { updateLobby, subscribeLobby } from './matchmaker/Lobby.ts';\nexport { createNodeMatchmakingMiddleware } from './router/node.ts';\n\n// Driver\nexport * from './matchmaker/LocalDriver/LocalDriver.ts';\nexport { initializeRoomCache } from './matchmaker/driver.ts';\n\n// Transport\nexport { type Client, type ClientPrivate, type AuthContext, ClientState, ClientArray, Transport, type ISendOptions, type BeforeUpgradeHandler, runBeforeUpgrade, createAuthContext, connectClientToRoom, enqueueClientRaw } from './Transport.ts';\n\n// Presence\nexport { type Presence } from './presence/Presence.ts';\nexport { LocalPresence } from './presence/LocalPresence.ts';\n\n// Serializers\nexport { type Serializer } from './serializer/Serializer.ts';\nexport { SchemaSerializer } from './serializer/SchemaSerializer.ts';\n\n// Utilities\nexport { Clock, Delayed };\nexport { generateId, Deferred, spliceOne, getBearerToken, dynamicImport } from './utils/Utils.ts';\nexport { isDevMode, setDevMode } from './utils/DevMode.ts';\n\n// IPC\nexport { subscribeIPC, requestFromIPC } from './IPC.ts';\n\n// Debug\nexport {\n debugMatchMaking,\n debugMessage,\n debugPatch,\n debugError,\n debugConnection,\n debugDriver,\n debugPresence,\n debugAndPrintError,\n} from './Debug.ts';\n\n// Default rooms\nexport { LobbyRoom } from './rooms/LobbyRoom.ts';\nexport { RelayRoom } from './rooms/RelayRoom.ts';\nexport { QueueRoom, type QueueOptions, type QueueMatchGroup, type QueueMatchTeam, type QueueClientData } from './rooms/QueueRoom.ts';\n\n// Router / Endpoints\nexport {\n createEndpoint,\n createInternalContext,\n createMiddleware,\n createRouter,\n basicAuth,\n type BasicAuthOptions,\n toNodeHandler,\n dualModeEndpoints,\n type Router,\n type RouterConfig,\n type Endpoint,\n type EndpointHandler,\n type EndpointOptions,\n type EndpointContext,\n type StrictEndpoint,\n type ExpressMiddleware,\n type NodeHandler,\n type DualModeHelpers,\n} from './router/index.ts';\n\n// Abstract logging support\nexport { logger } from './Logger.ts';\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6C;AAG7C,0BAcO;AAGP,oBAAsM;AACtM,kBAA2R;AAC3R,yBAAkD;AAClD,mBAAgL;AAChL,oBAAwE;AACxE,wBAGO;AACP,sBAAgC;AAChC,+BAAkC;AAClC,yBAA4B;AAE5B,4BAYO;AAGP,iBAA4B;AAE5B,mBAA4C;AAC5C,kBAAgD;AAGhD,0BAAc,qDAtDd;AAuDA,oBAAoC;AAGpC,uBAAiO;AAGjO,sBAA8B;AAC9B,2BAA8B;AAG9B,wBAAgC;AAChC,8BAAiC;AAIjC,mBAA+E;AAC/E,qBAAsC;AAGtC,iBAA6C;AAG7C,mBASO;AAGP,uBAA0B;AAC1B,uBAA0B;AAC1B,uBAA8G;AAG9G,oBAmBO;AAGP,oBAAuB;",
4
+ "sourcesContent": ["import { ClockTimer as Clock, Delayed } from '@colyseus/timer';\n\n// Shared types - re-export from @colyseus/shared-types for convenience\nexport {\n Protocol,\n ProtocolModifier,\n ErrorCode,\n CloseCode,\n ResponseStatus,\n type InferState,\n type ExtractRoomMessages,\n type ExtractRoomClientMessages,\n type MessageContext,\n type Rejection,\n type Resolution,\n type ExtractRejectReason,\n type ExtractResponseType,\n} from '@colyseus/shared-types';\n\n// Core classes\nexport { Server, defineRoom, defineServer, registerRoomDefinitions, unregisterRoomDefinitions, applySimulatedLatency, parseLatencyEnv, type RoomDefinitions, type ServerOptions, type SDKTypes } from './Server.ts';\nexport { Room, RoomInternalState, validate, type RoomOptions, type DefineInputOptions, type SimulationCallback, type FixedTimestepCallback, type StepContext, type MessageHandlerWithFormat, type Messages, type ExtractRoomState, type ExtractRoomMetadata, type ExtractRoomClient } from './Room.ts';\nexport { InputBufferImpl, compileSanitizer, compileSnapshot } from './input/InputBuffer.ts';\nexport { type InputAccessor, type InputAPI, type NormalizedInputOptions, type ConsumeOptions, type IdleInput, type IdleContext, type SanitizeInput, type NumericFieldsOf } from './input/types.ts';\nexport { Rewind, RewindView, type RewindOptions, type RewindMode } from './Rewind.ts';\nexport {\n RoomPlugin, definePlugins, attachToTestRoom,\n type RoomPluginOrder, type PluginDependencies, type RoomPluginClass,\n} from './RoomPlugin.ts';\nexport { getMessageBytes } from './Protocol.ts';\nexport { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';\nexport { ServerError } from './errors/ServerError.ts';\n\nexport {\n type RoomException,\n type RoomMethodName,\n OnCreateException,\n OnAuthException,\n OnJoinException,\n OnLeaveException,\n OnDisposeException,\n OnMessageException,\n TimestepException,\n SimulationIntervalException,\n TimedEventException,\n} from './errors/RoomExceptions.ts';\n\n// MatchMaker\nimport * as matchMaker from './MatchMaker.ts';\nexport { matchMaker };\nexport { updateLobby, subscribeLobby } from './matchmaker/Lobby.ts';\nexport { createNodeMatchmakingMiddleware } from './router/node.ts';\n\n// Driver\nexport * from './matchmaker/LocalDriver/LocalDriver.ts';\nexport { initializeRoomCache } from './matchmaker/driver.ts';\n\n// Transport\nexport { type Client, type ClientPrivate, type AuthContext, ClientState, ClientArray, Transport, type ISendOptions, type BeforeUpgradeHandler, runBeforeUpgrade, createAuthContext, connectClientToRoom, enqueueClientRaw } from './Transport.ts';\n\n// Presence\nexport { type Presence } from './presence/Presence.ts';\nexport { LocalPresence } from './presence/LocalPresence.ts';\n\n// Serializers\nexport { type Serializer } from './serializer/Serializer.ts';\nexport { SchemaSerializer } from './serializer/SchemaSerializer.ts';\n\n// Utilities\nexport { Clock, Delayed };\nexport { generateId, Deferred, spliceOne, getBearerToken, dynamicImport } from './utils/Utils.ts';\nexport { isDevMode, setDevMode } from './utils/DevMode.ts';\n\n// IPC\nexport { subscribeIPC, requestFromIPC } from './IPC.ts';\n\n// Debug\nexport {\n debugMatchMaking,\n debugMessage,\n debugPatch,\n debugError,\n debugConnection,\n debugDriver,\n debugPresence,\n debugAndPrintError,\n} from './Debug.ts';\n\n// Default rooms\nexport { LobbyRoom } from './rooms/LobbyRoom.ts';\nexport { RelayRoom } from './rooms/RelayRoom.ts';\nexport { QueueRoom, type QueueOptions, type QueueMatchGroup, type QueueMatchTeam, type QueueClientData } from './rooms/QueueRoom.ts';\n\n// Router / Endpoints\nexport {\n createEndpoint,\n createInternalContext,\n createMiddleware,\n createRouter,\n basicAuth,\n type BasicAuthOptions,\n toNodeHandler,\n dualModeEndpoints,\n type Router,\n type RouterConfig,\n type Endpoint,\n type EndpointHandler,\n type EndpointOptions,\n type EndpointContext,\n type StrictEndpoint,\n type ExpressMiddleware,\n type NodeHandler,\n type DualModeHelpers,\n} from './router/index.ts';\n\n// Abstract logging support\nexport { logger } from './Logger.ts';\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA,4BAAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAA6C;AAG7C,0BAcO;AAGP,oBAAsM;AACtM,kBAA2R;AAC3R,yBAAmE;AACnE,mBAAgL;AAChL,oBAAwE;AACxE,wBAGO;AACP,sBAAgC;AAChC,+BAAkC;AAClC,yBAA4B;AAE5B,4BAYO;AAGP,iBAA4B;AAE5B,mBAA4C;AAC5C,kBAAgD;AAGhD,0BAAc,qDAtDd;AAuDA,oBAAoC;AAGpC,uBAAiO;AAGjO,sBAA8B;AAC9B,2BAA8B;AAG9B,wBAAgC;AAChC,8BAAiC;AAIjC,mBAA+E;AAC/E,qBAAsC;AAGtC,iBAA6C;AAG7C,mBASO;AAGP,uBAA0B;AAC1B,uBAA0B;AAC1B,uBAA8G;AAG9G,oBAmBO;AAGP,oBAAuB;",
6
6
  "names": ["Clock"]
7
7
  }
package/build/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { ClockTimer as Clock, Delayed } from '@colyseus/timer';
2
2
  export { Protocol, ProtocolModifier, ErrorCode, CloseCode, ResponseStatus, type InferState, type ExtractRoomMessages, type ExtractRoomClientMessages, type MessageContext, type Rejection, type Resolution, type ExtractRejectReason, type ExtractResponseType, } from '@colyseus/shared-types';
3
3
  export { Server, defineRoom, defineServer, registerRoomDefinitions, unregisterRoomDefinitions, applySimulatedLatency, parseLatencyEnv, type RoomDefinitions, type ServerOptions, type SDKTypes } from './Server.ts';
4
4
  export { Room, RoomInternalState, validate, type RoomOptions, type DefineInputOptions, type SimulationCallback, type FixedTimestepCallback, type StepContext, type MessageHandlerWithFormat, type Messages, type ExtractRoomState, type ExtractRoomMetadata, type ExtractRoomClient } from './Room.ts';
5
- export { InputBufferImpl, compileSanitizer } from './input/InputBuffer.ts';
5
+ export { InputBufferImpl, compileSanitizer, compileSnapshot } from './input/InputBuffer.ts';
6
6
  export { type InputAccessor, type InputAPI, type NormalizedInputOptions, type ConsumeOptions, type IdleInput, type IdleContext, type SanitizeInput, type NumericFieldsOf } from './input/types.ts';
7
7
  export { Rewind, RewindView, type RewindOptions, type RewindMode } from './Rewind.ts';
8
8
  export { RoomPlugin, definePlugins, attachToTestRoom, type RoomPluginOrder, type PluginDependencies, type RoomPluginClass, } from './RoomPlugin.ts';
package/build/index.mjs CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  } from "@colyseus/shared-types";
10
10
  import { Server, defineRoom, defineServer, registerRoomDefinitions, unregisterRoomDefinitions, applySimulatedLatency, parseLatencyEnv } from "./Server.mjs";
11
11
  import { Room, RoomInternalState, validate } from "./Room.mjs";
12
- import { InputBufferImpl, compileSanitizer } from "./input/InputBuffer.mjs";
12
+ import { InputBufferImpl, compileSanitizer, compileSnapshot } from "./input/InputBuffer.mjs";
13
13
  import {} from "./input/types.mjs";
14
14
  import { Rewind, RewindView } from "./Rewind.mjs";
15
15
  import {
@@ -106,6 +106,7 @@ export {
106
106
  attachToTestRoom,
107
107
  basicAuth,
108
108
  compileSanitizer,
109
+ compileSnapshot,
109
110
  connectClientToRoom,
110
111
  createAuthContext,
111
112
  createEndpoint,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import { ClockTimer as Clock, Delayed } from '@colyseus/timer';\n\n// Shared types - re-export from @colyseus/shared-types for convenience\nexport {\n Protocol,\n ProtocolModifier,\n ErrorCode,\n CloseCode,\n ResponseStatus,\n type InferState,\n type ExtractRoomMessages,\n type ExtractRoomClientMessages,\n type MessageContext,\n type Rejection,\n type Resolution,\n type ExtractRejectReason,\n type ExtractResponseType,\n} from '@colyseus/shared-types';\n\n// Core classes\nexport { Server, defineRoom, defineServer, registerRoomDefinitions, unregisterRoomDefinitions, applySimulatedLatency, parseLatencyEnv, type RoomDefinitions, type ServerOptions, type SDKTypes } from './Server.ts';\nexport { Room, RoomInternalState, validate, type RoomOptions, type DefineInputOptions, type SimulationCallback, type FixedTimestepCallback, type StepContext, type MessageHandlerWithFormat, type Messages, type ExtractRoomState, type ExtractRoomMetadata, type ExtractRoomClient } from './Room.ts';\nexport { InputBufferImpl, compileSanitizer } from './input/InputBuffer.ts';\nexport { type InputAccessor, type InputAPI, type NormalizedInputOptions, type ConsumeOptions, type IdleInput, type IdleContext, type SanitizeInput, type NumericFieldsOf } from './input/types.ts';\nexport { Rewind, RewindView, type RewindOptions, type RewindMode } from './Rewind.ts';\nexport {\n RoomPlugin, definePlugins, attachToTestRoom,\n type RoomPluginOrder, type PluginDependencies, type RoomPluginClass,\n} from './RoomPlugin.ts';\nexport { getMessageBytes } from './Protocol.ts';\nexport { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';\nexport { ServerError } from './errors/ServerError.ts';\n\nexport {\n type RoomException,\n type RoomMethodName,\n OnCreateException,\n OnAuthException,\n OnJoinException,\n OnLeaveException,\n OnDisposeException,\n OnMessageException,\n TimestepException,\n SimulationIntervalException,\n TimedEventException,\n} from './errors/RoomExceptions.ts';\n\n// MatchMaker\nimport * as matchMaker from './MatchMaker.ts';\nexport { matchMaker };\nexport { updateLobby, subscribeLobby } from './matchmaker/Lobby.ts';\nexport { createNodeMatchmakingMiddleware } from './router/node.ts';\n\n// Driver\nexport * from './matchmaker/LocalDriver/LocalDriver.ts';\nexport { initializeRoomCache } from './matchmaker/driver.ts';\n\n// Transport\nexport { type Client, type ClientPrivate, type AuthContext, ClientState, ClientArray, Transport, type ISendOptions, type BeforeUpgradeHandler, runBeforeUpgrade, createAuthContext, connectClientToRoom, enqueueClientRaw } from './Transport.ts';\n\n// Presence\nexport { type Presence } from './presence/Presence.ts';\nexport { LocalPresence } from './presence/LocalPresence.ts';\n\n// Serializers\nexport { type Serializer } from './serializer/Serializer.ts';\nexport { SchemaSerializer } from './serializer/SchemaSerializer.ts';\n\n// Utilities\nexport { Clock, Delayed };\nexport { generateId, Deferred, spliceOne, getBearerToken, dynamicImport } from './utils/Utils.ts';\nexport { isDevMode, setDevMode } from './utils/DevMode.ts';\n\n// IPC\nexport { subscribeIPC, requestFromIPC } from './IPC.ts';\n\n// Debug\nexport {\n debugMatchMaking,\n debugMessage,\n debugPatch,\n debugError,\n debugConnection,\n debugDriver,\n debugPresence,\n debugAndPrintError,\n} from './Debug.ts';\n\n// Default rooms\nexport { LobbyRoom } from './rooms/LobbyRoom.ts';\nexport { RelayRoom } from './rooms/RelayRoom.ts';\nexport { QueueRoom, type QueueOptions, type QueueMatchGroup, type QueueMatchTeam, type QueueClientData } from './rooms/QueueRoom.ts';\n\n// Router / Endpoints\nexport {\n createEndpoint,\n createInternalContext,\n createMiddleware,\n createRouter,\n basicAuth,\n type BasicAuthOptions,\n toNodeHandler,\n dualModeEndpoints,\n type Router,\n type RouterConfig,\n type Endpoint,\n type EndpointHandler,\n type EndpointOptions,\n type EndpointContext,\n type StrictEndpoint,\n type ExpressMiddleware,\n type NodeHandler,\n type DualModeHelpers,\n} from './router/index.ts';\n\n// Abstract logging support\nexport { logger } from './Logger.ts';\n"],
5
- "mappings": ";AAAA,SAAS,cAAc,OAAO,eAAe;AAG7C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OASK;AAGP,SAAS,QAAQ,YAAY,cAAc,yBAAyB,2BAA2B,uBAAuB,uBAAgF;AACtM,SAAS,MAAM,mBAAmB,gBAAyP;AAC3R,SAAS,iBAAiB,wBAAwB;AAClD,eAAgL;AAChL,SAAS,QAAQ,kBAAuD;AACxE;AAAA,EACE;AAAA,EAAY;AAAA,EAAe;AAAA,OAEtB;AACP,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAE5B;AAAA,EAGE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,YAAY,gBAAgB;AAE5B,SAAS,aAAa,sBAAsB;AAC5C,SAAS,uCAAuC;AAGhD,cAAc;AACd,SAAS,2BAA2B;AAGpC,SAA4D,aAAa,aAAa,WAAyD,kBAAkB,mBAAmB,qBAAqB,wBAAwB;AAGjO,eAA8B;AAC9B,SAAS,qBAAqB;AAG9B,eAAgC;AAChC,SAAS,wBAAwB;AAIjC,SAAS,YAAY,UAAU,WAAW,gBAAgB,qBAAqB;AAC/E,SAAS,WAAW,kBAAkB;AAGtC,SAAS,cAAc,sBAAsB;AAG7C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAC1B,SAAS,iBAAqG;AAG9G;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OAWK;AAGP,SAAS,cAAc;",
4
+ "sourcesContent": ["import { ClockTimer as Clock, Delayed } from '@colyseus/timer';\n\n// Shared types - re-export from @colyseus/shared-types for convenience\nexport {\n Protocol,\n ProtocolModifier,\n ErrorCode,\n CloseCode,\n ResponseStatus,\n type InferState,\n type ExtractRoomMessages,\n type ExtractRoomClientMessages,\n type MessageContext,\n type Rejection,\n type Resolution,\n type ExtractRejectReason,\n type ExtractResponseType,\n} from '@colyseus/shared-types';\n\n// Core classes\nexport { Server, defineRoom, defineServer, registerRoomDefinitions, unregisterRoomDefinitions, applySimulatedLatency, parseLatencyEnv, type RoomDefinitions, type ServerOptions, type SDKTypes } from './Server.ts';\nexport { Room, RoomInternalState, validate, type RoomOptions, type DefineInputOptions, type SimulationCallback, type FixedTimestepCallback, type StepContext, type MessageHandlerWithFormat, type Messages, type ExtractRoomState, type ExtractRoomMetadata, type ExtractRoomClient } from './Room.ts';\nexport { InputBufferImpl, compileSanitizer, compileSnapshot } from './input/InputBuffer.ts';\nexport { type InputAccessor, type InputAPI, type NormalizedInputOptions, type ConsumeOptions, type IdleInput, type IdleContext, type SanitizeInput, type NumericFieldsOf } from './input/types.ts';\nexport { Rewind, RewindView, type RewindOptions, type RewindMode } from './Rewind.ts';\nexport {\n RoomPlugin, definePlugins, attachToTestRoom,\n type RoomPluginOrder, type PluginDependencies, type RoomPluginClass,\n} from './RoomPlugin.ts';\nexport { getMessageBytes } from './Protocol.ts';\nexport { RegisteredHandler } from './matchmaker/RegisteredHandler.ts';\nexport { ServerError } from './errors/ServerError.ts';\n\nexport {\n type RoomException,\n type RoomMethodName,\n OnCreateException,\n OnAuthException,\n OnJoinException,\n OnLeaveException,\n OnDisposeException,\n OnMessageException,\n TimestepException,\n SimulationIntervalException,\n TimedEventException,\n} from './errors/RoomExceptions.ts';\n\n// MatchMaker\nimport * as matchMaker from './MatchMaker.ts';\nexport { matchMaker };\nexport { updateLobby, subscribeLobby } from './matchmaker/Lobby.ts';\nexport { createNodeMatchmakingMiddleware } from './router/node.ts';\n\n// Driver\nexport * from './matchmaker/LocalDriver/LocalDriver.ts';\nexport { initializeRoomCache } from './matchmaker/driver.ts';\n\n// Transport\nexport { type Client, type ClientPrivate, type AuthContext, ClientState, ClientArray, Transport, type ISendOptions, type BeforeUpgradeHandler, runBeforeUpgrade, createAuthContext, connectClientToRoom, enqueueClientRaw } from './Transport.ts';\n\n// Presence\nexport { type Presence } from './presence/Presence.ts';\nexport { LocalPresence } from './presence/LocalPresence.ts';\n\n// Serializers\nexport { type Serializer } from './serializer/Serializer.ts';\nexport { SchemaSerializer } from './serializer/SchemaSerializer.ts';\n\n// Utilities\nexport { Clock, Delayed };\nexport { generateId, Deferred, spliceOne, getBearerToken, dynamicImport } from './utils/Utils.ts';\nexport { isDevMode, setDevMode } from './utils/DevMode.ts';\n\n// IPC\nexport { subscribeIPC, requestFromIPC } from './IPC.ts';\n\n// Debug\nexport {\n debugMatchMaking,\n debugMessage,\n debugPatch,\n debugError,\n debugConnection,\n debugDriver,\n debugPresence,\n debugAndPrintError,\n} from './Debug.ts';\n\n// Default rooms\nexport { LobbyRoom } from './rooms/LobbyRoom.ts';\nexport { RelayRoom } from './rooms/RelayRoom.ts';\nexport { QueueRoom, type QueueOptions, type QueueMatchGroup, type QueueMatchTeam, type QueueClientData } from './rooms/QueueRoom.ts';\n\n// Router / Endpoints\nexport {\n createEndpoint,\n createInternalContext,\n createMiddleware,\n createRouter,\n basicAuth,\n type BasicAuthOptions,\n toNodeHandler,\n dualModeEndpoints,\n type Router,\n type RouterConfig,\n type Endpoint,\n type EndpointHandler,\n type EndpointOptions,\n type EndpointContext,\n type StrictEndpoint,\n type ExpressMiddleware,\n type NodeHandler,\n type DualModeHelpers,\n} from './router/index.ts';\n\n// Abstract logging support\nexport { logger } from './Logger.ts';\n"],
5
+ "mappings": ";AAAA,SAAS,cAAc,OAAO,eAAe;AAG7C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OASK;AAGP,SAAS,QAAQ,YAAY,cAAc,yBAAyB,2BAA2B,uBAAuB,uBAAgF;AACtM,SAAS,MAAM,mBAAmB,gBAAyP;AAC3R,SAAS,iBAAiB,kBAAkB,uBAAuB;AACnE,eAAgL;AAChL,SAAS,QAAQ,kBAAuD;AACxE;AAAA,EACE;AAAA,EAAY;AAAA,EAAe;AAAA,OAEtB;AACP,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,mBAAmB;AAE5B;AAAA,EAGE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,YAAY,gBAAgB;AAE5B,SAAS,aAAa,sBAAsB;AAC5C,SAAS,uCAAuC;AAGhD,cAAc;AACd,SAAS,2BAA2B;AAGpC,SAA4D,aAAa,aAAa,WAAyD,kBAAkB,mBAAmB,qBAAqB,wBAAwB;AAGjO,eAA8B;AAC9B,SAAS,qBAAqB;AAG9B,eAAgC;AAChC,SAAS,wBAAwB;AAIjC,SAAS,YAAY,UAAU,WAAW,gBAAgB,qBAAqB;AAC/E,SAAS,WAAW,kBAAkB;AAGtC,SAAS,cAAc,sBAAsB;AAG7C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAC1B,SAAS,iBAAqG;AAG9G;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,OAWK;AAGP,SAAS,cAAc;",
6
6
  "names": []
7
7
  }
@@ -25,10 +25,12 @@ __export(InputBuffer_exports, {
25
25
  InputBufferImpl: () => InputBufferImpl,
26
26
  NO_OP_INPUT_ACCESSOR: () => NO_OP_INPUT_ACCESSOR,
27
27
  compileSanitizer: () => compileSanitizer,
28
+ compileSnapshot: () => compileSnapshot,
28
29
  seedInputZeroValues: () => seedInputZeroValues,
29
30
  validateSubSteps: () => validateSubSteps
30
31
  });
31
32
  module.exports = __toCommonJS(InputBuffer_exports);
33
+ var import_schema = require("@colyseus/schema");
32
34
  var import_Utils = require("../utils/Utils.cjs");
33
35
  function validateSubSteps(subSteps, source) {
34
36
  if (subSteps === void 0) {
@@ -58,7 +60,10 @@ function compileSanitizer(spec) {
58
60
  if (v === void 0) {
59
61
  continue;
60
62
  }
61
- inst[names[i]] = v >= mins[i] ? v <= maxs[i] ? v : maxs[i] : mins[i];
63
+ const clamped = v >= mins[i] ? v <= maxs[i] ? v : maxs[i] : mins[i];
64
+ if (clamped !== v) {
65
+ inst[names[i]] = clamped;
66
+ }
62
67
  }
63
68
  };
64
69
  }
@@ -74,6 +79,51 @@ function fieldNamesOf(ctor) {
74
79
  }
75
80
  return names;
76
81
  }
82
+ var _snapshots = /* @__PURE__ */ new WeakMap();
83
+ function compileSnapshot(ctor) {
84
+ let snapshot = _snapshots.get(ctor);
85
+ if (snapshot !== void 0) {
86
+ return snapshot;
87
+ }
88
+ const md = ctor[import_Utils.$METADATA];
89
+ const names = fieldNamesOf(ctor);
90
+ const plain = md !== void 0 && names.every((_, i) => {
91
+ const type = md[i].type;
92
+ return typeof type === "string" || type !== null && typeof type === "object" && "quantized" in type;
93
+ });
94
+ if (plain) {
95
+ let Frame2 = function(values) {
96
+ this[import_schema.$values] = values;
97
+ };
98
+ var Frame = Frame2;
99
+ const fields = {};
100
+ names.forEach((name, i) => {
101
+ fields[name] = {
102
+ get() {
103
+ return this[import_schema.$values][i];
104
+ },
105
+ set(value) {
106
+ this[import_schema.$values][i] = value;
107
+ },
108
+ enumerable: true,
109
+ configurable: true
110
+ };
111
+ });
112
+ Frame2.prototype = Object.create(ctor.prototype, fields);
113
+ const count = names.length;
114
+ snapshot = (input) => {
115
+ const values = new Array(count);
116
+ for (let i = 0; i < count; i++) {
117
+ values[i] = input[names[i]];
118
+ }
119
+ return new Frame2(values);
120
+ };
121
+ } else {
122
+ snapshot = (input) => input.clone();
123
+ }
124
+ _snapshots.set(ctor, snapshot);
125
+ return snapshot;
126
+ }
77
127
  function seedInputZeroValues(instance, ctor) {
78
128
  const md = ctor[import_Utils.$METADATA];
79
129
  if (!md) {
@@ -558,6 +608,7 @@ var NO_OP_INPUT_ACCESSOR = Object.freeze({
558
608
  InputBufferImpl,
559
609
  NO_OP_INPUT_ACCESSOR,
560
610
  compileSanitizer,
611
+ compileSnapshot,
561
612
  seedInputZeroValues,
562
613
  validateSubSteps
563
614
  });