@colyseus/shared-types 0.17.1 → 0.17.3

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.
@@ -58,7 +58,7 @@ var CloseCode = {
58
58
  CONSENTED: 4e3,
59
59
  SERVER_SHUTDOWN: 4001,
60
60
  WITH_ERROR: 4002,
61
- DEVMODE_RESTART: 4010
61
+ MAY_TRY_RECONNECT: 4010
62
62
  };
63
63
  // Annotate the CommonJS export names for ESM import in node:
64
64
  0 && (module.exports = {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Protocol.ts"],
4
- "sourcesContent": ["/**\n * Colyseus protocol codes range between 0~100\n * Use codes between 0~127 for lesser throughput (1 byte)\n */\nexport const Protocol = {\n // Room-related (10~19)\n JOIN_ROOM: 10,\n ERROR: 11,\n LEAVE_ROOM: 12,\n ROOM_DATA: 13,\n ROOM_STATE: 14,\n ROOM_STATE_PATCH: 15,\n ROOM_DATA_SCHEMA: 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES: 17,\n PING: 18,\n} as const;\nexport type Protocol = typeof Protocol[keyof typeof Protocol];\n\n/**\n * HTTP MatchMaking Error Codes\n */\nexport const ErrorCode = {\n MATCHMAKE_NO_HANDLER: 520,\n MATCHMAKE_INVALID_CRITERIA: 521,\n MATCHMAKE_INVALID_ROOM_ID: 522,\n MATCHMAKE_UNHANDLED: 523, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED: 524, // generic exception during onCreate/onJoin\n AUTH_FAILED: 525,\n APPLICATION_ERROR: 526,\n\n INVALID_PAYLOAD: 4217,\n} as const;\nexport type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];\n\n/**\n * WebSocket close codes\n * (See https://github.com/Luka967/websocket-close-codes)\n */\nexport const CloseCode = {\n NORMAL_CLOSURE: 1000,\n GOING_AWAY: 1001,\n NO_STATUS_RECEIVED: 1005,\n ABNORMAL_CLOSURE: 1006,\n\n CONSENTED: 4000,\n SERVER_SHUTDOWN: 4001,\n WITH_ERROR: 4002,\n DEVMODE_RESTART: 4010\n} as const;\nexport type CloseCode = typeof CloseCode[keyof typeof CloseCode];\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,IAAM,WAAW;AAAA;AAAA,EAEtB,WAAW;AAAA,EACX,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAClB,iBAAiB;AAAA,EACjB,MAAM;AACR;AAMO,IAAM,YAAY;AAAA,EACvB,sBAAsB;AAAA,EACtB,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA;AAAA,EACnB,aAAa;AAAA,EACb,mBAAmB;AAAA,EAEnB,iBAAiB;AACnB;AAOO,IAAM,YAAY;AAAA,EACvB,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAElB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,iBAAiB;AACnB;",
4
+ "sourcesContent": ["/**\n * Colyseus protocol codes range between 0~100\n * Use codes between 0~127 for lesser throughput (1 byte)\n */\nexport const Protocol = {\n // Room-related (10~19)\n JOIN_ROOM: 10,\n ERROR: 11,\n LEAVE_ROOM: 12,\n ROOM_DATA: 13,\n ROOM_STATE: 14,\n ROOM_STATE_PATCH: 15,\n ROOM_DATA_SCHEMA: 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES: 17,\n PING: 18,\n} as const;\nexport type Protocol = typeof Protocol[keyof typeof Protocol];\n\n/**\n * HTTP MatchMaking Error Codes\n */\nexport const ErrorCode = {\n MATCHMAKE_NO_HANDLER: 520,\n MATCHMAKE_INVALID_CRITERIA: 521,\n MATCHMAKE_INVALID_ROOM_ID: 522,\n MATCHMAKE_UNHANDLED: 523, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED: 524, // generic exception during onCreate/onJoin\n AUTH_FAILED: 525,\n APPLICATION_ERROR: 526,\n\n INVALID_PAYLOAD: 4217,\n} as const;\nexport type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];\n\n/**\n * WebSocket close codes\n * (See https://github.com/Luka967/websocket-close-codes)\n */\nexport const CloseCode = {\n NORMAL_CLOSURE: 1000,\n GOING_AWAY: 1001,\n NO_STATUS_RECEIVED: 1005,\n ABNORMAL_CLOSURE: 1006,\n\n CONSENTED: 4000,\n SERVER_SHUTDOWN: 4001,\n WITH_ERROR: 4002,\n MAY_TRY_RECONNECT: 4010\n} as const;\nexport type CloseCode = typeof CloseCode[keyof typeof CloseCode];\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,IAAM,WAAW;AAAA;AAAA,EAEtB,WAAW;AAAA,EACX,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAClB,iBAAiB;AAAA,EACjB,MAAM;AACR;AAMO,IAAM,YAAY;AAAA,EACvB,sBAAsB;AAAA,EACtB,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA;AAAA,EACnB,aAAa;AAAA,EACb,mBAAmB;AAAA,EAEnB,iBAAiB;AACnB;AAOO,IAAM,YAAY;AAAA,EACvB,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAElB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,mBAAmB;AACrB;",
6
6
  "names": []
7
7
  }
@@ -40,6 +40,6 @@ export declare const CloseCode: {
40
40
  readonly CONSENTED: 4000;
41
41
  readonly SERVER_SHUTDOWN: 4001;
42
42
  readonly WITH_ERROR: 4002;
43
- readonly DEVMODE_RESTART: 4010;
43
+ readonly MAY_TRY_RECONNECT: 4010;
44
44
  };
45
45
  export type CloseCode = typeof CloseCode[keyof typeof CloseCode];
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Colyseus protocol codes range between 0~100
3
+ * Use codes between 0~127 for lesser throughput (1 byte)
4
+ */
5
+ export const Protocol = {
6
+ // Room-related (10~19)
7
+ JOIN_ROOM: 10,
8
+ ERROR: 11,
9
+ LEAVE_ROOM: 12,
10
+ ROOM_DATA: 13,
11
+ ROOM_STATE: 14,
12
+ ROOM_STATE_PATCH: 15,
13
+ ROOM_DATA_SCHEMA: 16, // DEPRECATED: used to send schema instances via room.send()
14
+ ROOM_DATA_BYTES: 17,
15
+ PING: 18,
16
+ };
17
+ /**
18
+ * HTTP MatchMaking Error Codes
19
+ */
20
+ export const ErrorCode = {
21
+ MATCHMAKE_NO_HANDLER: 520,
22
+ MATCHMAKE_INVALID_CRITERIA: 521,
23
+ MATCHMAKE_INVALID_ROOM_ID: 522,
24
+ MATCHMAKE_UNHANDLED: 523, // generic exception during onCreate/onJoin
25
+ MATCHMAKE_EXPIRED: 524, // generic exception during onCreate/onJoin
26
+ AUTH_FAILED: 525,
27
+ APPLICATION_ERROR: 526,
28
+ INVALID_PAYLOAD: 4217,
29
+ };
30
+ /**
31
+ * WebSocket close codes
32
+ * (See https://github.com/Luka967/websocket-close-codes)
33
+ */
34
+ export const CloseCode = {
35
+ NORMAL_CLOSURE: 1000,
36
+ GOING_AWAY: 1001,
37
+ NO_STATUS_RECEIVED: 1005,
38
+ ABNORMAL_CLOSURE: 1006,
39
+ CONSENTED: 4000,
40
+ SERVER_SHUTDOWN: 4001,
41
+ WITH_ERROR: 4002,
42
+ MAY_TRY_RECONNECT: 4010
43
+ };
44
+ //# sourceMappingURL=Protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Protocol.js","sourceRoot":"","sources":["../src/Protocol.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,uBAAuB;IACvB,SAAS,EAAE,EAAE;IACb,KAAK,EAAE,EAAE;IACT,UAAU,EAAE,EAAE;IACd,SAAS,EAAE,EAAE;IACb,UAAU,EAAE,EAAE;IACd,gBAAgB,EAAE,EAAE;IACpB,gBAAgB,EAAE,EAAE,EAAE,4DAA4D;IAClF,eAAe,EAAE,EAAE;IACnB,IAAI,EAAE,EAAE;CACA,CAAC;AAGX;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,oBAAoB,EAAE,GAAG;IACzB,0BAA0B,EAAE,GAAG;IAC/B,yBAAyB,EAAE,GAAG;IAC9B,mBAAmB,EAAE,GAAG,EAAE,2CAA2C;IACrE,iBAAiB,EAAE,GAAG,EAAE,2CAA2C;IACnE,WAAW,EAAE,GAAG;IAChB,iBAAiB,EAAE,GAAG;IAEtB,eAAe,EAAE,IAAI;CACb,CAAC;AAGX;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,cAAc,EAAE,IAAI;IACpB,UAAU,EAAE,IAAI;IAChB,kBAAkB,EAAE,IAAI;IACxB,gBAAgB,EAAE,IAAI;IAEtB,SAAS,EAAE,IAAI;IACf,eAAe,EAAE,IAAI;IACrB,UAAU,EAAE,IAAI;IAChB,iBAAiB,EAAE,IAAI;CACf,CAAC","sourcesContent":["/**\n * Colyseus protocol codes range between 0~100\n * Use codes between 0~127 for lesser throughput (1 byte)\n */\nexport const Protocol = {\n // Room-related (10~19)\n JOIN_ROOM: 10,\n ERROR: 11,\n LEAVE_ROOM: 12,\n ROOM_DATA: 13,\n ROOM_STATE: 14,\n ROOM_STATE_PATCH: 15,\n ROOM_DATA_SCHEMA: 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES: 17,\n PING: 18,\n} as const;\nexport type Protocol = typeof Protocol[keyof typeof Protocol];\n\n/**\n * HTTP MatchMaking Error Codes\n */\nexport const ErrorCode = {\n MATCHMAKE_NO_HANDLER: 520,\n MATCHMAKE_INVALID_CRITERIA: 521,\n MATCHMAKE_INVALID_ROOM_ID: 522,\n MATCHMAKE_UNHANDLED: 523, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED: 524, // generic exception during onCreate/onJoin\n AUTH_FAILED: 525,\n APPLICATION_ERROR: 526,\n\n INVALID_PAYLOAD: 4217,\n} as const;\nexport type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];\n\n/**\n * WebSocket close codes\n * (See https://github.com/Luka967/websocket-close-codes)\n */\nexport const CloseCode = {\n NORMAL_CLOSURE: 1000,\n GOING_AWAY: 1001,\n NO_STATUS_RECEIVED: 1005,\n ABNORMAL_CLOSURE: 1006,\n\n CONSENTED: 4000,\n SERVER_SHUTDOWN: 4001,\n WITH_ERROR: 4002,\n MAY_TRY_RECONNECT: 4010\n} as const;\nexport type CloseCode = typeof CloseCode[keyof typeof CloseCode];\n"]}
@@ -32,7 +32,7 @@ var CloseCode = {
32
32
  CONSENTED: 4e3,
33
33
  SERVER_SHUTDOWN: 4001,
34
34
  WITH_ERROR: 4002,
35
- DEVMODE_RESTART: 4010
35
+ MAY_TRY_RECONNECT: 4010
36
36
  };
37
37
  export {
38
38
  CloseCode,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/Protocol.ts"],
4
- "sourcesContent": ["/**\n * Colyseus protocol codes range between 0~100\n * Use codes between 0~127 for lesser throughput (1 byte)\n */\nexport const Protocol = {\n // Room-related (10~19)\n JOIN_ROOM: 10,\n ERROR: 11,\n LEAVE_ROOM: 12,\n ROOM_DATA: 13,\n ROOM_STATE: 14,\n ROOM_STATE_PATCH: 15,\n ROOM_DATA_SCHEMA: 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES: 17,\n PING: 18,\n} as const;\nexport type Protocol = typeof Protocol[keyof typeof Protocol];\n\n/**\n * HTTP MatchMaking Error Codes\n */\nexport const ErrorCode = {\n MATCHMAKE_NO_HANDLER: 520,\n MATCHMAKE_INVALID_CRITERIA: 521,\n MATCHMAKE_INVALID_ROOM_ID: 522,\n MATCHMAKE_UNHANDLED: 523, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED: 524, // generic exception during onCreate/onJoin\n AUTH_FAILED: 525,\n APPLICATION_ERROR: 526,\n\n INVALID_PAYLOAD: 4217,\n} as const;\nexport type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];\n\n/**\n * WebSocket close codes\n * (See https://github.com/Luka967/websocket-close-codes)\n */\nexport const CloseCode = {\n NORMAL_CLOSURE: 1000,\n GOING_AWAY: 1001,\n NO_STATUS_RECEIVED: 1005,\n ABNORMAL_CLOSURE: 1006,\n\n CONSENTED: 4000,\n SERVER_SHUTDOWN: 4001,\n WITH_ERROR: 4002,\n DEVMODE_RESTART: 4010\n} as const;\nexport type CloseCode = typeof CloseCode[keyof typeof CloseCode];\n"],
5
- "mappings": ";AAIO,IAAM,WAAW;AAAA;AAAA,EAEtB,WAAW;AAAA,EACX,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAClB,iBAAiB;AAAA,EACjB,MAAM;AACR;AAMO,IAAM,YAAY;AAAA,EACvB,sBAAsB;AAAA,EACtB,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA;AAAA,EACnB,aAAa;AAAA,EACb,mBAAmB;AAAA,EAEnB,iBAAiB;AACnB;AAOO,IAAM,YAAY;AAAA,EACvB,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAElB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,iBAAiB;AACnB;",
4
+ "sourcesContent": ["/**\n * Colyseus protocol codes range between 0~100\n * Use codes between 0~127 for lesser throughput (1 byte)\n */\nexport const Protocol = {\n // Room-related (10~19)\n JOIN_ROOM: 10,\n ERROR: 11,\n LEAVE_ROOM: 12,\n ROOM_DATA: 13,\n ROOM_STATE: 14,\n ROOM_STATE_PATCH: 15,\n ROOM_DATA_SCHEMA: 16, // DEPRECATED: used to send schema instances via room.send()\n ROOM_DATA_BYTES: 17,\n PING: 18,\n} as const;\nexport type Protocol = typeof Protocol[keyof typeof Protocol];\n\n/**\n * HTTP MatchMaking Error Codes\n */\nexport const ErrorCode = {\n MATCHMAKE_NO_HANDLER: 520,\n MATCHMAKE_INVALID_CRITERIA: 521,\n MATCHMAKE_INVALID_ROOM_ID: 522,\n MATCHMAKE_UNHANDLED: 523, // generic exception during onCreate/onJoin\n MATCHMAKE_EXPIRED: 524, // generic exception during onCreate/onJoin\n AUTH_FAILED: 525,\n APPLICATION_ERROR: 526,\n\n INVALID_PAYLOAD: 4217,\n} as const;\nexport type ErrorCode = typeof ErrorCode[keyof typeof ErrorCode];\n\n/**\n * WebSocket close codes\n * (See https://github.com/Luka967/websocket-close-codes)\n */\nexport const CloseCode = {\n NORMAL_CLOSURE: 1000,\n GOING_AWAY: 1001,\n NO_STATUS_RECEIVED: 1005,\n ABNORMAL_CLOSURE: 1006,\n\n CONSENTED: 4000,\n SERVER_SHUTDOWN: 4001,\n WITH_ERROR: 4002,\n MAY_TRY_RECONNECT: 4010\n} as const;\nexport type CloseCode = typeof CloseCode[keyof typeof CloseCode];\n"],
5
+ "mappings": ";AAIO,IAAM,WAAW;AAAA;AAAA,EAEtB,WAAW;AAAA,EACX,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,kBAAkB;AAAA;AAAA,EAClB,iBAAiB;AAAA,EACjB,MAAM;AACR;AAMO,IAAM,YAAY;AAAA,EACvB,sBAAsB;AAAA,EACtB,4BAA4B;AAAA,EAC5B,2BAA2B;AAAA,EAC3B,qBAAqB;AAAA;AAAA,EACrB,mBAAmB;AAAA;AAAA,EACnB,aAAa;AAAA,EACb,mBAAmB;AAAA,EAEnB,iBAAiB;AACnB;AAOO,IAAM,YAAY;AAAA,EACvB,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAElB,WAAW;AAAA,EACX,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,mBAAmB;AACrB;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n// Re-export StandardSchemaV1 for convenience\nexport type { StandardSchemaV1 };\n\n// Re-export Protocol types\nexport { Protocol, ErrorCode, CloseCode } from './Protocol.js';\n\n/**\n * Minimal Room-like interface for SDK type inference.\n * Allows typing SDK methods without depending on @colyseus/core.\n */\nexport interface ServerRoomLike<State = any, Options = any> {\n state: State;\n onJoin: (client: any, options?: Options, auth?: any) => any;\n messages?: Record<string, any>;\n '~client'?: { '~messages'?: Record<string, any> };\n}\n\n/**\n * Seat reservation returned by matchmaking operations.\n */\nexport interface ISeatReservation {\n name: string;\n sessionId: string;\n roomId: string;\n publicAddress?: string;\n processId?: string;\n reconnectionToken?: string;\n devMode?: boolean;\n}\n\n/**\n * Helper types for flexible Room generics on the client SDK.\n * Allows: Room<State>, Room<ServerRoom>, or Room<ServerRoom, State>\n */\nexport type InferState<T, S> = [S] extends [never]\n ? (T extends abstract new (...args: any) => { state: infer ST }\n ? ST // Constructor type (typeof MyRoom): extract state from instance\n : T extends { state: infer ST }\n ? ST // Instance type (MyRoom): extract state directly\n : T) // State type or other: return as-is\n : S;\n\n/**\n * Extract room messages type from a Room constructor or instance type.\n * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)\n */\nexport type ExtractRoomMessages<T> = T extends abstract new (...args: any) => { messages: infer M }\n ? M\n : T extends { messages: infer M }\n ? M\n : {};\n\n/**\n * Extract client-side messages type from a Room constructor or instance type.\n * These are messages that the server can send to the client.\n */\nexport type ExtractRoomClientMessages<T> = T extends abstract new (...args: any) => { '~client': { '~messages': infer M } }\n ? M\n : T extends { '~client': { '~messages': infer M } }\n ? M\n : {};\n\n/**\n * Message handler with automatic type inference from format schema.\n * When a format is provided, the message type is automatically inferred from the schema.\n *\n * @template T - The StandardSchema type for message validation\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandlerWithFormat<T extends StandardSchemaV1 = any, Client = any, This = any> = {\n format: T;\n handler: (this: This, client: Client, message: StandardSchemaV1.InferOutput<T>) => void;\n};\n\n/**\n * Message handler type that can be either a function or a format handler with validation.\n *\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandler<Client = any, This = any> =\n | ((this: This, client: Client, message: any) => void)\n | MessageHandlerWithFormat<any, Client, This>;\n\n/**\n * Extract the message payload type from a message handler.\n * Works with both function handlers and format handlers.\n */\nexport type ExtractMessageType<T> =\n T extends { format: infer Format extends StandardSchemaV1; handler: any }\n ? StandardSchemaV1.InferOutput<Format>\n : T extends (this: any, client: any, message: infer Message) => void\n ? Message\n : any;\n\n/**\n * A map of message types to message handlers.\n *\n * @template Room - The Room class type\n * @template Client - The client type\n */\nexport type Messages<Room = any, Client = any> = Record<string, MessageHandler<Client, Room>>;\n\n/**\n * Exposed types for the client-side SDK.\n * Used by defineServer() to expose room and route types to the client.\n *\n * @template RoomTypes - Record of room names to their RegisteredHandler types\n * @template Routes - Router type from @colyseus/better-call\n */\nexport interface SDKTypes<\n RoomTypes extends Record<string, any> = any,\n Routes = any\n> {\n '~rooms': RoomTypes;\n '~routes': Routes;\n}\n"],
4
+ "sourcesContent": ["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n// Re-export StandardSchemaV1 for convenience\nexport type { StandardSchemaV1 };\n\n// Re-export Protocol types\nexport { Protocol, ErrorCode, CloseCode } from './Protocol.js';\n\n/**\n * Minimal Room-like interface for SDK type inference.\n * Allows typing SDK methods without depending on @colyseus/core.\n * Note: onJoin is optional because core Room defines it as optional.\n */\nexport interface ServerRoomLike<State = any, Options = any> {\n state: State;\n onJoin?: (client: any, options?: Options, auth?: any) => any;\n messages?: Record<string, any>;\n '~client'?: { '~messages'?: Record<string, any> };\n}\n\n/**\n * Seat reservation returned by matchmaking operations.\n */\nexport interface ISeatReservation {\n name: string;\n sessionId: string;\n roomId: string;\n publicAddress?: string;\n processId?: string;\n reconnectionToken?: string;\n devMode?: boolean;\n}\n\n/**\n * Extract instance type from a constructor type.\n * If T is not a constructor, returns T as-is.\n */\ntype Instantiate<T> = T extends abstract new (...args: any) => infer I ? I : T;\n\n/**\n * Check if a type is a Schema (has ~refId marker).\n * Schema defines ~refId as optional, so we check keyof instead of property presence.\n */\ntype IsSchema<T> = '~refId' extends keyof T ? true : false;\n\n/**\n * Check if ~state phantom property contains a useful type (not object/any/never).\n * Returns true if ~state exists and has meaningful structure.\n */\ntype HasUsefulStatePhantom<T> = T extends { '~state': infer S }\n ? [S] extends [never] ? false // never is not useful\n : unknown extends S ? false // any is not useful\n : S extends object\n ? [keyof S] extends [never] ? false // {} or object with no keys is not useful\n : true\n : false\n : false;\n\n/**\n * Extract state from a Room-like instance type.\n * Priority: useful ~state phantom > Schema state property > return T as-is\n */\ntype ExtractStateFromRoom<T> = T extends { '~state': infer S }\n ? HasUsefulStatePhantom<T> extends true\n ? S // Use ~state if it's useful\n : T extends { state: infer St }\n ? IsSchema<St> extends true ? St : T // Fallback to state if Schema\n : T\n : T extends { state: infer S }\n ? IsSchema<S> extends true ? S : T\n : T;\n\n/**\n * Infer the state type from T, or use explicit S if provided.\n *\n * Supports multiple usage patterns:\n * - Room<MyState>: T is a Schema type, return as-is\n * - Room<MyRoom>: T is a Room instance, extract from ~state or state property\n * - Room<typeof MyRoom>: T is a constructor, instantiate first then extract\n * - Room<T, ExplicitState>: S overrides all inference\n */\nexport type InferState<T, S> = [S] extends [never]\n ? Instantiate<T> extends infer I\n ? IsSchema<I> extends true\n ? I // It's a Schema, return as-is\n : ExtractStateFromRoom<I>\n : never\n : S;\n\n/**\n * Normalizes T for message extraction: returns T if it has ~state (Room type),\n * otherwise returns any (plain state type). This ensures Room<State> is equivalent\n * to Room<any, State> when State doesn't have ~state.\n */\nexport type NormalizeRoomType<T> = Instantiate<T> extends { '~state': any } ? T : any;\n\n/**\n * Extract room messages type from a Room constructor or instance type.\n * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)\n */\nexport type ExtractRoomMessages<T> = Instantiate<T> extends { messages: infer M } ? M : {};\n\n/**\n * Extract client-side messages type from a Room constructor or instance type.\n * These are messages that the server can send to the client.\n */\nexport type ExtractRoomClientMessages<T> = Instantiate<T> extends { '~client': { '~messages': infer M } } ? M : {};\n\n/**\n * Message handler with automatic type inference from format schema.\n * When a format is provided, the message type is automatically inferred from the schema.\n *\n * @template T - The StandardSchema type for message validation\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandlerWithFormat<T extends StandardSchemaV1 = any, Client = any, This = any> = {\n format: T;\n handler: (this: This, client: Client, message: StandardSchemaV1.InferOutput<T>) => void;\n};\n\n/**\n * Message handler type that can be either a function or a format handler with validation.\n *\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandler<Client = any, This = any> =\n | ((this: This, client: Client, message: any) => void)\n | MessageHandlerWithFormat<any, Client, This>;\n\n/**\n * Extract the message payload type from a message handler.\n * Works with both function handlers and format handlers.\n */\nexport type ExtractMessageType<T> =\n T extends { format: infer Format extends StandardSchemaV1; handler: any }\n ? StandardSchemaV1.InferOutput<Format>\n : T extends (this: any, client: any, message: infer Message) => void\n ? Message\n : any;\n\n/**\n * A map of message types to message handlers.\n *\n * @template Room - The Room class type\n * @template Client - The client type\n */\nexport type Messages<Room = any, Client = any> = Record<string, MessageHandler<Client, Room>>;\n\n/**\n * Exposed types for the client-side SDK.\n * Used by defineServer() to expose room and route types to the client.\n *\n * @template RoomTypes - Record of room names to their RegisteredHandler types\n * @template Routes - Router type from @colyseus/better-call\n */\nexport interface SDKTypes<\n RoomTypes extends Record<string, any> = any,\n Routes = any\n> {\n '~rooms': RoomTypes;\n '~routes': Routes;\n}\n"],
5
5
  "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,sBAA+C;",
6
6
  "names": []
7
7
  }
package/build/index.d.ts CHANGED
@@ -4,10 +4,11 @@ export { Protocol, ErrorCode, CloseCode } from './Protocol.js';
4
4
  /**
5
5
  * Minimal Room-like interface for SDK type inference.
6
6
  * Allows typing SDK methods without depending on @colyseus/core.
7
+ * Note: onJoin is optional because core Room defines it as optional.
7
8
  */
8
9
  export interface ServerRoomLike<State = any, Options = any> {
9
10
  state: State;
10
- onJoin: (client: any, options?: Options, auth?: any) => any;
11
+ onJoin?: (client: any, options?: Options, auth?: any) => any;
11
12
  messages?: Record<string, any>;
12
13
  '~client'?: {
13
14
  '~messages'?: Record<string, any>;
@@ -26,32 +27,63 @@ export interface ISeatReservation {
26
27
  devMode?: boolean;
27
28
  }
28
29
  /**
29
- * Helper types for flexible Room generics on the client SDK.
30
- * Allows: Room<State>, Room<ServerRoom>, or Room<ServerRoom, State>
30
+ * Extract instance type from a constructor type.
31
+ * If T is not a constructor, returns T as-is.
31
32
  */
32
- export type InferState<T, S> = [S] extends [never] ? (T extends abstract new (...args: any) => {
33
- state: infer ST;
34
- } ? ST : T extends {
35
- state: infer ST;
36
- } ? ST : T) : S;
33
+ type Instantiate<T> = T extends abstract new (...args: any) => infer I ? I : T;
34
+ /**
35
+ * Check if a type is a Schema (has ~refId marker).
36
+ * Schema defines ~refId as optional, so we check keyof instead of property presence.
37
+ */
38
+ type IsSchema<T> = '~refId' extends keyof T ? true : false;
39
+ /**
40
+ * Check if ~state phantom property contains a useful type (not object/any/never).
41
+ * Returns true if ~state exists and has meaningful structure.
42
+ */
43
+ type HasUsefulStatePhantom<T> = T extends {
44
+ '~state': infer S;
45
+ } ? [S] extends [never] ? false : unknown extends S ? false : S extends object ? [keyof S] extends [never] ? false : true : false : false;
46
+ /**
47
+ * Extract state from a Room-like instance type.
48
+ * Priority: useful ~state phantom > Schema state property > return T as-is
49
+ */
50
+ type ExtractStateFromRoom<T> = T extends {
51
+ '~state': infer S;
52
+ } ? HasUsefulStatePhantom<T> extends true ? S : T extends {
53
+ state: infer St;
54
+ } ? IsSchema<St> extends true ? St : T : T : T extends {
55
+ state: infer S;
56
+ } ? IsSchema<S> extends true ? S : T : T;
57
+ /**
58
+ * Infer the state type from T, or use explicit S if provided.
59
+ *
60
+ * Supports multiple usage patterns:
61
+ * - Room<MyState>: T is a Schema type, return as-is
62
+ * - Room<MyRoom>: T is a Room instance, extract from ~state or state property
63
+ * - Room<typeof MyRoom>: T is a constructor, instantiate first then extract
64
+ * - Room<T, ExplicitState>: S overrides all inference
65
+ */
66
+ export type InferState<T, S> = [S] extends [never] ? Instantiate<T> extends infer I ? IsSchema<I> extends true ? I : ExtractStateFromRoom<I> : never : S;
67
+ /**
68
+ * Normalizes T for message extraction: returns T if it has ~state (Room type),
69
+ * otherwise returns any (plain state type). This ensures Room<State> is equivalent
70
+ * to Room<any, State> when State doesn't have ~state.
71
+ */
72
+ export type NormalizeRoomType<T> = Instantiate<T> extends {
73
+ '~state': any;
74
+ } ? T : any;
37
75
  /**
38
76
  * Extract room messages type from a Room constructor or instance type.
39
77
  * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)
40
78
  */
41
- export type ExtractRoomMessages<T> = T extends abstract new (...args: any) => {
42
- messages: infer M;
43
- } ? M : T extends {
79
+ export type ExtractRoomMessages<T> = Instantiate<T> extends {
44
80
  messages: infer M;
45
81
  } ? M : {};
46
82
  /**
47
83
  * Extract client-side messages type from a Room constructor or instance type.
48
84
  * These are messages that the server can send to the client.
49
85
  */
50
- export type ExtractRoomClientMessages<T> = T extends abstract new (...args: any) => {
51
- '~client': {
52
- '~messages': infer M;
53
- };
54
- } ? M : T extends {
86
+ export type ExtractRoomClientMessages<T> = Instantiate<T> extends {
55
87
  '~client': {
56
88
  '~messages': infer M;
57
89
  };
package/build/index.js CHANGED
@@ -1,2 +1,3 @@
1
- export {};
1
+ // Re-export Protocol types
2
+ export { Protocol, ErrorCode, CloseCode } from './Protocol.js';
2
3
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"","sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n// Re-export StandardSchemaV1 for convenience\nexport type { StandardSchemaV1 };\n\n/**\n * Minimal Room-like interface for SDK type inference.\n * Allows typing SDK methods without depending on @colyseus/core.\n */\nexport interface ServerRoomLike<State = any, Options = any> {\n state: State;\n onJoin: (client: any, options?: Options, auth?: any) => any;\n messages?: Record<string, any>;\n '~client'?: { '~messages'?: Record<string, any> };\n}\n\n/**\n * Seat reservation returned by matchmaking operations.\n */\nexport interface ISeatReservation {\n name: string;\n sessionId: string;\n roomId: string;\n publicAddress?: string;\n processId?: string;\n reconnectionToken?: string;\n devMode?: boolean;\n}\n\n/**\n * Helper types for flexible Room generics on the client SDK.\n * Allows: Room<State>, Room<ServerRoom>, or Room<ServerRoom, State>\n */\nexport type InferState<T, S> = [S] extends [never]\n ? (T extends abstract new (...args: any) => { state: infer ST }\n ? ST // Constructor type (typeof MyRoom): extract state from instance\n : T extends { state: infer ST }\n ? ST // Instance type (MyRoom): extract state directly\n : T) // State type or other: return as-is\n : S;\n\n/**\n * Extract room messages type from a Room constructor or instance type.\n * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)\n */\nexport type ExtractRoomMessages<T> = T extends abstract new (...args: any) => { messages: infer M }\n ? M\n : T extends { messages: infer M }\n ? M\n : {};\n\n/**\n * Extract client-side messages type from a Room constructor or instance type.\n * These are messages that the server can send to the client.\n */\nexport type ExtractRoomClientMessages<T> = T extends abstract new (...args: any) => { '~client': { '~messages': infer M } }\n ? M\n : T extends { '~client': { '~messages': infer M } }\n ? M\n : {};\n\n/**\n * Message handler with automatic type inference from format schema.\n * When a format is provided, the message type is automatically inferred from the schema.\n *\n * @template T - The StandardSchema type for message validation\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandlerWithFormat<T extends StandardSchemaV1 = any, Client = any, This = any> = {\n format: T;\n handler: (this: This, client: Client, message: StandardSchemaV1.InferOutput<T>) => void;\n};\n\n/**\n * Message handler type that can be either a function or a format handler with validation.\n *\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandler<Client = any, This = any> =\n | ((this: This, client: Client, message: any) => void)\n | MessageHandlerWithFormat<any, Client, This>;\n\n/**\n * Extract the message payload type from a message handler.\n * Works with both function handlers and format handlers.\n */\nexport type ExtractMessageType<T> =\n T extends { format: infer Format extends StandardSchemaV1; handler: any }\n ? StandardSchemaV1.InferOutput<Format>\n : T extends (this: any, client: any, message: infer Message) => void\n ? Message\n : any;\n\n/**\n * A map of message types to message handlers.\n *\n * @template Room - The Room class type\n * @template Client - The client type\n */\nexport type Messages<Room = any, Client = any> = Record<string, MessageHandler<Client, Room>>;\n\n/**\n * Exposed types for the client-side SDK.\n * Used by defineServer() to expose room and route types to the client.\n *\n * @template RoomTypes - Record of room names to their RegisteredHandler types\n * @template Routes - Router type from @colyseus/better-call\n */\nexport interface SDKTypes<\n RoomTypes extends Record<string, any> = any,\n Routes = any\n> {\n '~rooms': RoomTypes;\n '~routes': Routes;\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,2BAA2B;AAC3B,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC","sourcesContent":["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n// Re-export StandardSchemaV1 for convenience\nexport type { StandardSchemaV1 };\n\n// Re-export Protocol types\nexport { Protocol, ErrorCode, CloseCode } from './Protocol.js';\n\n/**\n * Minimal Room-like interface for SDK type inference.\n * Allows typing SDK methods without depending on @colyseus/core.\n */\nexport interface ServerRoomLike<State = any, Options = any> {\n state: State;\n onJoin: (client: any, options?: Options, auth?: any) => any;\n messages?: Record<string, any>;\n '~client'?: { '~messages'?: Record<string, any> };\n}\n\n/**\n * Seat reservation returned by matchmaking operations.\n */\nexport interface ISeatReservation {\n name: string;\n sessionId: string;\n roomId: string;\n publicAddress?: string;\n processId?: string;\n reconnectionToken?: string;\n devMode?: boolean;\n}\n\n/**\n * Helper types for flexible Room generics on the client SDK.\n * Allows: Room<State>, Room<ServerRoom>, or Room<ServerRoom, State>\n *\n * Uses `~state` phantom property to distinguish Room types from plain state types.\n * This prevents incorrectly extracting `state` from a state type that happens to have a `state` property.\n */\nexport type InferState<T, S> = [S] extends [never]\n ? (T extends abstract new (...args: any) => { '~state': infer ST }\n ? ST // Constructor type (typeof MyRoom): extract state via ~state phantom\n : T extends { '~state': infer ST }\n ? ST // Instance type (MyRoom): extract state via ~state phantom\n : T) // State type or other: return as-is\n : S;\n\n/**\n * Normalizes T for message extraction: returns T if it has ~state (Room type),\n * otherwise returns any (plain state type). This ensures Room<State> is equivalent\n * to Room<any, State> when State doesn't have ~state.\n */\nexport type NormalizeRoomType<T> = T extends abstract new (...args: any) => { '~state': any }\n ? T // Constructor type with ~state: keep as-is\n : T extends { '~state': any }\n ? T // Instance type with ~state: keep as-is\n : any; // Plain state type: normalize to any\n\n/**\n * Extract room messages type from a Room constructor or instance type.\n * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)\n */\nexport type ExtractRoomMessages<T> = T extends abstract new (...args: any) => { messages: infer M }\n ? M\n : T extends { messages: infer M }\n ? M\n : {};\n\n/**\n * Extract client-side messages type from a Room constructor or instance type.\n * These are messages that the server can send to the client.\n */\nexport type ExtractRoomClientMessages<T> = T extends abstract new (...args: any) => { '~client': { '~messages': infer M } }\n ? M\n : T extends { '~client': { '~messages': infer M } }\n ? M\n : {};\n\n/**\n * Message handler with automatic type inference from format schema.\n * When a format is provided, the message type is automatically inferred from the schema.\n *\n * @template T - The StandardSchema type for message validation\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandlerWithFormat<T extends StandardSchemaV1 = any, Client = any, This = any> = {\n format: T;\n handler: (this: This, client: Client, message: StandardSchemaV1.InferOutput<T>) => void;\n};\n\n/**\n * Message handler type that can be either a function or a format handler with validation.\n *\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandler<Client = any, This = any> =\n | ((this: This, client: Client, message: any) => void)\n | MessageHandlerWithFormat<any, Client, This>;\n\n/**\n * Extract the message payload type from a message handler.\n * Works with both function handlers and format handlers.\n */\nexport type ExtractMessageType<T> =\n T extends { format: infer Format extends StandardSchemaV1; handler: any }\n ? StandardSchemaV1.InferOutput<Format>\n : T extends (this: any, client: any, message: infer Message) => void\n ? Message\n : any;\n\n/**\n * A map of message types to message handlers.\n *\n * @template Room - The Room class type\n * @template Client - The client type\n */\nexport type Messages<Room = any, Client = any> = Record<string, MessageHandler<Client, Room>>;\n\n/**\n * Exposed types for the client-side SDK.\n * Used by defineServer() to expose room and route types to the client.\n *\n * @template RoomTypes - Record of room names to their RegisteredHandler types\n * @template Routes - Router type from @colyseus/better-call\n */\nexport interface SDKTypes<\n RoomTypes extends Record<string, any> = any,\n Routes = any\n> {\n '~rooms': RoomTypes;\n '~routes': Routes;\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/index.ts"],
4
- "sourcesContent": ["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n// Re-export StandardSchemaV1 for convenience\nexport type { StandardSchemaV1 };\n\n// Re-export Protocol types\nexport { Protocol, ErrorCode, CloseCode } from './Protocol.js';\n\n/**\n * Minimal Room-like interface for SDK type inference.\n * Allows typing SDK methods without depending on @colyseus/core.\n */\nexport interface ServerRoomLike<State = any, Options = any> {\n state: State;\n onJoin: (client: any, options?: Options, auth?: any) => any;\n messages?: Record<string, any>;\n '~client'?: { '~messages'?: Record<string, any> };\n}\n\n/**\n * Seat reservation returned by matchmaking operations.\n */\nexport interface ISeatReservation {\n name: string;\n sessionId: string;\n roomId: string;\n publicAddress?: string;\n processId?: string;\n reconnectionToken?: string;\n devMode?: boolean;\n}\n\n/**\n * Helper types for flexible Room generics on the client SDK.\n * Allows: Room<State>, Room<ServerRoom>, or Room<ServerRoom, State>\n */\nexport type InferState<T, S> = [S] extends [never]\n ? (T extends abstract new (...args: any) => { state: infer ST }\n ? ST // Constructor type (typeof MyRoom): extract state from instance\n : T extends { state: infer ST }\n ? ST // Instance type (MyRoom): extract state directly\n : T) // State type or other: return as-is\n : S;\n\n/**\n * Extract room messages type from a Room constructor or instance type.\n * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)\n */\nexport type ExtractRoomMessages<T> = T extends abstract new (...args: any) => { messages: infer M }\n ? M\n : T extends { messages: infer M }\n ? M\n : {};\n\n/**\n * Extract client-side messages type from a Room constructor or instance type.\n * These are messages that the server can send to the client.\n */\nexport type ExtractRoomClientMessages<T> = T extends abstract new (...args: any) => { '~client': { '~messages': infer M } }\n ? M\n : T extends { '~client': { '~messages': infer M } }\n ? M\n : {};\n\n/**\n * Message handler with automatic type inference from format schema.\n * When a format is provided, the message type is automatically inferred from the schema.\n *\n * @template T - The StandardSchema type for message validation\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandlerWithFormat<T extends StandardSchemaV1 = any, Client = any, This = any> = {\n format: T;\n handler: (this: This, client: Client, message: StandardSchemaV1.InferOutput<T>) => void;\n};\n\n/**\n * Message handler type that can be either a function or a format handler with validation.\n *\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandler<Client = any, This = any> =\n | ((this: This, client: Client, message: any) => void)\n | MessageHandlerWithFormat<any, Client, This>;\n\n/**\n * Extract the message payload type from a message handler.\n * Works with both function handlers and format handlers.\n */\nexport type ExtractMessageType<T> =\n T extends { format: infer Format extends StandardSchemaV1; handler: any }\n ? StandardSchemaV1.InferOutput<Format>\n : T extends (this: any, client: any, message: infer Message) => void\n ? Message\n : any;\n\n/**\n * A map of message types to message handlers.\n *\n * @template Room - The Room class type\n * @template Client - The client type\n */\nexport type Messages<Room = any, Client = any> = Record<string, MessageHandler<Client, Room>>;\n\n/**\n * Exposed types for the client-side SDK.\n * Used by defineServer() to expose room and route types to the client.\n *\n * @template RoomTypes - Record of room names to their RegisteredHandler types\n * @template Routes - Router type from @colyseus/better-call\n */\nexport interface SDKTypes<\n RoomTypes extends Record<string, any> = any,\n Routes = any\n> {\n '~rooms': RoomTypes;\n '~routes': Routes;\n}\n"],
4
+ "sourcesContent": ["import type { StandardSchemaV1 } from '@standard-schema/spec';\n\n// Re-export StandardSchemaV1 for convenience\nexport type { StandardSchemaV1 };\n\n// Re-export Protocol types\nexport { Protocol, ErrorCode, CloseCode } from './Protocol.js';\n\n/**\n * Minimal Room-like interface for SDK type inference.\n * Allows typing SDK methods without depending on @colyseus/core.\n * Note: onJoin is optional because core Room defines it as optional.\n */\nexport interface ServerRoomLike<State = any, Options = any> {\n state: State;\n onJoin?: (client: any, options?: Options, auth?: any) => any;\n messages?: Record<string, any>;\n '~client'?: { '~messages'?: Record<string, any> };\n}\n\n/**\n * Seat reservation returned by matchmaking operations.\n */\nexport interface ISeatReservation {\n name: string;\n sessionId: string;\n roomId: string;\n publicAddress?: string;\n processId?: string;\n reconnectionToken?: string;\n devMode?: boolean;\n}\n\n/**\n * Extract instance type from a constructor type.\n * If T is not a constructor, returns T as-is.\n */\ntype Instantiate<T> = T extends abstract new (...args: any) => infer I ? I : T;\n\n/**\n * Check if a type is a Schema (has ~refId marker).\n * Schema defines ~refId as optional, so we check keyof instead of property presence.\n */\ntype IsSchema<T> = '~refId' extends keyof T ? true : false;\n\n/**\n * Check if ~state phantom property contains a useful type (not object/any/never).\n * Returns true if ~state exists and has meaningful structure.\n */\ntype HasUsefulStatePhantom<T> = T extends { '~state': infer S }\n ? [S] extends [never] ? false // never is not useful\n : unknown extends S ? false // any is not useful\n : S extends object\n ? [keyof S] extends [never] ? false // {} or object with no keys is not useful\n : true\n : false\n : false;\n\n/**\n * Extract state from a Room-like instance type.\n * Priority: useful ~state phantom > Schema state property > return T as-is\n */\ntype ExtractStateFromRoom<T> = T extends { '~state': infer S }\n ? HasUsefulStatePhantom<T> extends true\n ? S // Use ~state if it's useful\n : T extends { state: infer St }\n ? IsSchema<St> extends true ? St : T // Fallback to state if Schema\n : T\n : T extends { state: infer S }\n ? IsSchema<S> extends true ? S : T\n : T;\n\n/**\n * Infer the state type from T, or use explicit S if provided.\n *\n * Supports multiple usage patterns:\n * - Room<MyState>: T is a Schema type, return as-is\n * - Room<MyRoom>: T is a Room instance, extract from ~state or state property\n * - Room<typeof MyRoom>: T is a constructor, instantiate first then extract\n * - Room<T, ExplicitState>: S overrides all inference\n */\nexport type InferState<T, S> = [S] extends [never]\n ? Instantiate<T> extends infer I\n ? IsSchema<I> extends true\n ? I // It's a Schema, return as-is\n : ExtractStateFromRoom<I>\n : never\n : S;\n\n/**\n * Normalizes T for message extraction: returns T if it has ~state (Room type),\n * otherwise returns any (plain state type). This ensures Room<State> is equivalent\n * to Room<any, State> when State doesn't have ~state.\n */\nexport type NormalizeRoomType<T> = Instantiate<T> extends { '~state': any } ? T : any;\n\n/**\n * Extract room messages type from a Room constructor or instance type.\n * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)\n */\nexport type ExtractRoomMessages<T> = Instantiate<T> extends { messages: infer M } ? M : {};\n\n/**\n * Extract client-side messages type from a Room constructor or instance type.\n * These are messages that the server can send to the client.\n */\nexport type ExtractRoomClientMessages<T> = Instantiate<T> extends { '~client': { '~messages': infer M } } ? M : {};\n\n/**\n * Message handler with automatic type inference from format schema.\n * When a format is provided, the message type is automatically inferred from the schema.\n *\n * @template T - The StandardSchema type for message validation\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandlerWithFormat<T extends StandardSchemaV1 = any, Client = any, This = any> = {\n format: T;\n handler: (this: This, client: Client, message: StandardSchemaV1.InferOutput<T>) => void;\n};\n\n/**\n * Message handler type that can be either a function or a format handler with validation.\n *\n * @template Client - The client type (from @colyseus/core Transport)\n * @template This - The Room class context\n */\nexport type MessageHandler<Client = any, This = any> =\n | ((this: This, client: Client, message: any) => void)\n | MessageHandlerWithFormat<any, Client, This>;\n\n/**\n * Extract the message payload type from a message handler.\n * Works with both function handlers and format handlers.\n */\nexport type ExtractMessageType<T> =\n T extends { format: infer Format extends StandardSchemaV1; handler: any }\n ? StandardSchemaV1.InferOutput<Format>\n : T extends (this: any, client: any, message: infer Message) => void\n ? Message\n : any;\n\n/**\n * A map of message types to message handlers.\n *\n * @template Room - The Room class type\n * @template Client - The client type\n */\nexport type Messages<Room = any, Client = any> = Record<string, MessageHandler<Client, Room>>;\n\n/**\n * Exposed types for the client-side SDK.\n * Used by defineServer() to expose room and route types to the client.\n *\n * @template RoomTypes - Record of room names to their RegisteredHandler types\n * @template Routes - Router type from @colyseus/better-call\n */\nexport interface SDKTypes<\n RoomTypes extends Record<string, any> = any,\n Routes = any\n> {\n '~rooms': RoomTypes;\n '~routes': Routes;\n}\n"],
5
5
  "mappings": ";AAMA,SAAS,UAAU,WAAW,iBAAiB;",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@colyseus/shared-types",
3
- "version": "0.17.1",
3
+ "version": "0.17.3",
4
4
  "description": "Shared TypeScript types for Colyseus server and client SDK",
5
5
  "type": "module",
6
6
  "input": "./src/index.ts",
@@ -45,7 +45,15 @@
45
45
  "dependencies": {
46
46
  "@standard-schema/spec": "^1.0.0"
47
47
  },
48
+ "devDependencies": {
49
+ "vitest": "^3.1.1",
50
+ "@colyseus/core": "^0.17.21",
51
+ "@colyseus/sdk": "^0.17.21"
52
+ },
48
53
  "publishConfig": {
49
54
  "access": "public"
55
+ },
56
+ "scripts": {
57
+ "test": "vitest"
50
58
  }
51
59
  }
package/src/Protocol.ts CHANGED
@@ -45,6 +45,6 @@ export const CloseCode = {
45
45
  CONSENTED: 4000,
46
46
  SERVER_SHUTDOWN: 4001,
47
47
  WITH_ERROR: 4002,
48
- DEVMODE_RESTART: 4010
48
+ MAY_TRY_RECONNECT: 4010
49
49
  } as const;
50
50
  export type CloseCode = typeof CloseCode[keyof typeof CloseCode];
package/src/index.ts CHANGED
@@ -9,10 +9,11 @@ export { Protocol, ErrorCode, CloseCode } from './Protocol.js';
9
9
  /**
10
10
  * Minimal Room-like interface for SDK type inference.
11
11
  * Allows typing SDK methods without depending on @colyseus/core.
12
+ * Note: onJoin is optional because core Room defines it as optional.
12
13
  */
13
14
  export interface ServerRoomLike<State = any, Options = any> {
14
15
  state: State;
15
- onJoin: (client: any, options?: Options, auth?: any) => any;
16
+ onJoin?: (client: any, options?: Options, auth?: any) => any;
16
17
  messages?: Record<string, any>;
17
18
  '~client'?: { '~messages'?: Record<string, any> };
18
19
  }
@@ -31,36 +32,79 @@ export interface ISeatReservation {
31
32
  }
32
33
 
33
34
  /**
34
- * Helper types for flexible Room generics on the client SDK.
35
- * Allows: Room<State>, Room<ServerRoom>, or Room<ServerRoom, State>
35
+ * Extract instance type from a constructor type.
36
+ * If T is not a constructor, returns T as-is.
37
+ */
38
+ type Instantiate<T> = T extends abstract new (...args: any) => infer I ? I : T;
39
+
40
+ /**
41
+ * Check if a type is a Schema (has ~refId marker).
42
+ * Schema defines ~refId as optional, so we check keyof instead of property presence.
43
+ */
44
+ type IsSchema<T> = '~refId' extends keyof T ? true : false;
45
+
46
+ /**
47
+ * Check if ~state phantom property contains a useful type (not object/any/never).
48
+ * Returns true if ~state exists and has meaningful structure.
49
+ */
50
+ type HasUsefulStatePhantom<T> = T extends { '~state': infer S }
51
+ ? [S] extends [never] ? false // never is not useful
52
+ : unknown extends S ? false // any is not useful
53
+ : S extends object
54
+ ? [keyof S] extends [never] ? false // {} or object with no keys is not useful
55
+ : true
56
+ : false
57
+ : false;
58
+
59
+ /**
60
+ * Extract state from a Room-like instance type.
61
+ * Priority: useful ~state phantom > Schema state property > return T as-is
62
+ */
63
+ type ExtractStateFromRoom<T> = T extends { '~state': infer S }
64
+ ? HasUsefulStatePhantom<T> extends true
65
+ ? S // Use ~state if it's useful
66
+ : T extends { state: infer St }
67
+ ? IsSchema<St> extends true ? St : T // Fallback to state if Schema
68
+ : T
69
+ : T extends { state: infer S }
70
+ ? IsSchema<S> extends true ? S : T
71
+ : T;
72
+
73
+ /**
74
+ * Infer the state type from T, or use explicit S if provided.
75
+ *
76
+ * Supports multiple usage patterns:
77
+ * - Room<MyState>: T is a Schema type, return as-is
78
+ * - Room<MyRoom>: T is a Room instance, extract from ~state or state property
79
+ * - Room<typeof MyRoom>: T is a constructor, instantiate first then extract
80
+ * - Room<T, ExplicitState>: S overrides all inference
36
81
  */
37
82
  export type InferState<T, S> = [S] extends [never]
38
- ? (T extends abstract new (...args: any) => { state: infer ST }
39
- ? ST // Constructor type (typeof MyRoom): extract state from instance
40
- : T extends { state: infer ST }
41
- ? ST // Instance type (MyRoom): extract state directly
42
- : T) // State type or other: return as-is
83
+ ? Instantiate<T> extends infer I
84
+ ? IsSchema<I> extends true
85
+ ? I // It's a Schema, return as-is
86
+ : ExtractStateFromRoom<I>
87
+ : never
43
88
  : S;
44
89
 
90
+ /**
91
+ * Normalizes T for message extraction: returns T if it has ~state (Room type),
92
+ * otherwise returns any (plain state type). This ensures Room<State> is equivalent
93
+ * to Room<any, State> when State doesn't have ~state.
94
+ */
95
+ export type NormalizeRoomType<T> = Instantiate<T> extends { '~state': any } ? T : any;
96
+
45
97
  /**
46
98
  * Extract room messages type from a Room constructor or instance type.
47
99
  * Supports both constructor types (typeof MyRoom) and instance types (MyRoom)
48
100
  */
49
- export type ExtractRoomMessages<T> = T extends abstract new (...args: any) => { messages: infer M }
50
- ? M
51
- : T extends { messages: infer M }
52
- ? M
53
- : {};
101
+ export type ExtractRoomMessages<T> = Instantiate<T> extends { messages: infer M } ? M : {};
54
102
 
55
103
  /**
56
104
  * Extract client-side messages type from a Room constructor or instance type.
57
105
  * These are messages that the server can send to the client.
58
106
  */
59
- export type ExtractRoomClientMessages<T> = T extends abstract new (...args: any) => { '~client': { '~messages': infer M } }
60
- ? M
61
- : T extends { '~client': { '~messages': infer M } }
62
- ? M
63
- : {};
107
+ export type ExtractRoomClientMessages<T> = Instantiate<T> extends { '~client': { '~messages': infer M } } ? M : {};
64
108
 
65
109
  /**
66
110
  * Message handler with automatic type inference from format schema.