@signe/room 2.4.6 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/testing.ts +9 -0
package/dist/index.d.ts
CHANGED
|
@@ -685,12 +685,14 @@ declare function testRoom(Room: any, options?: {
|
|
|
685
685
|
query?: Record<string, string>;
|
|
686
686
|
headers?: Record<string, string>;
|
|
687
687
|
}) => Promise<MockPartyClient>;
|
|
688
|
+
getServerUser: (client: any, prop?: string) => Promise<any>;
|
|
688
689
|
}>;
|
|
689
690
|
declare function request(room: Server | Shard, path: string, options?: {
|
|
690
691
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
691
692
|
body?: any;
|
|
692
693
|
headers?: Record<string, string>;
|
|
693
694
|
}): Promise<Response>;
|
|
695
|
+
declare function tick(ms?: number): Promise<unknown>;
|
|
694
696
|
|
|
695
697
|
interface RoomInterceptorPacket {
|
|
696
698
|
interceptorPacket(user: any, obj: any, conn: Connection): Promise<any> | null | any;
|
|
@@ -915,4 +917,4 @@ declare function createRequireSessionGuard(storage: Storage$1): (sender: Connect
|
|
|
915
917
|
*/
|
|
916
918
|
declare const requireSession: (sender: Connection, value: any, room: Room$1) => Promise<boolean>;
|
|
917
919
|
|
|
918
|
-
export { Action, ClientIo, Guard, MockConnection, Request, type RequestOptions, Room, RoomGuard, type RoomInterceptorPacket, type RoomMethods, type RoomOnJoin, type RoomOnLeave, type RoomOptions, Server, ServerIo, ServerResponse, Shard, type ShardOptions, WorldRoom, createRequireSessionGuard, request, requireSession, testRoom };
|
|
920
|
+
export { Action, ClientIo, Guard, MockConnection, Request, type RequestOptions, Room, RoomGuard, type RoomInterceptorPacket, type RoomMethods, type RoomOnJoin, type RoomOnLeave, type RoomOptions, Server, ServerIo, ServerResponse, Shard, type ShardOptions, WorldRoom, createRequireSessionGuard, request, requireSession, testRoom, tick };
|
package/dist/index.js
CHANGED
|
@@ -1763,7 +1763,12 @@ async function testRoom(Room3, options = {}) {
|
|
|
1763
1763
|
createClient: /* @__PURE__ */ __name(async (id2, opts) => {
|
|
1764
1764
|
const client = await io.connection(server, id2, opts);
|
|
1765
1765
|
return client;
|
|
1766
|
-
}, "createClient")
|
|
1766
|
+
}, "createClient"),
|
|
1767
|
+
getServerUser: /* @__PURE__ */ __name(async (client, prop = "users") => {
|
|
1768
|
+
const privateId = client.conn.id;
|
|
1769
|
+
const session = await server.getSession(privateId);
|
|
1770
|
+
return server.subRoom[prop]()[session?.publicId];
|
|
1771
|
+
}, "getServerUser")
|
|
1767
1772
|
};
|
|
1768
1773
|
}
|
|
1769
1774
|
__name(testRoom, "testRoom");
|
|
@@ -1776,6 +1781,10 @@ async function request(room, path, options = {
|
|
|
1776
1781
|
return response2;
|
|
1777
1782
|
}
|
|
1778
1783
|
__name(request, "request");
|
|
1784
|
+
function tick(ms = 0) {
|
|
1785
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1786
|
+
}
|
|
1787
|
+
__name(tick, "tick");
|
|
1779
1788
|
|
|
1780
1789
|
// src/mock.ts
|
|
1781
1790
|
var MockPartyClient = class {
|
|
@@ -2670,6 +2679,7 @@ export {
|
|
|
2670
2679
|
createRequireSessionGuard,
|
|
2671
2680
|
request,
|
|
2672
2681
|
requireSession,
|
|
2673
|
-
testRoom
|
|
2682
|
+
testRoom,
|
|
2683
|
+
tick
|
|
2674
2684
|
};
|
|
2675
2685
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/decorators.ts","../../sync/src/utils.ts","../src/storage.ts","../src/server.ts","../src/utils.ts","../src/request/response.ts","../src/request/cors.ts","../src/shard.ts","../src/testing.ts","../src/mock.ts","../src/world.ts","../src/types/party.ts","../src/jwt.ts","../src/world.guard.ts","../src/session.guard.ts"],"sourcesContent":["import type * as Party from \"./types/party\"\nimport type { z } from \"zod\"\ntype GuardFn = (sender: Party.Connection, value: any | Party.Request, room: Party.Room) => boolean | Promise<boolean | Response>;\ntype RoomGuardFn = (conn: Party.Connection, ctx: Party.ConnectionContext, room: Party.Room) => boolean | Promise<boolean | Response>;\n\nexport function Action(name: string, bodyValidation?: z.ZodSchema) {\n return function (target: any, propertyKey: string) {\n if (!target.constructor._actionMetadata) {\n target.constructor._actionMetadata = new Map();\n }\n target.constructor._actionMetadata.set(name, {\n key: propertyKey,\n bodyValidation,\n });\n };\n}\n\n/**\n * Request decorator for handling HTTP requests with path and method routing\n * @param options Configuration for the HTTP request handler\n * @param bodyValidation Optional Zod schema for request body validation\n */\nexport interface RequestOptions {\n path: string;\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';\n}\n\nexport function Request(options: RequestOptions, bodyValidation?: z.ZodSchema) {\n return function (target: any, propertyKey: string) {\n if (!target.constructor._requestMetadata) {\n target.constructor._requestMetadata = new Map();\n }\n \n // Format the path to ensure it starts with a slash\n const path = options.path.startsWith('/') ? options.path : `/${options.path}`;\n const method = options.method || 'GET';\n \n // Create a unique key for this route using method and path\n const routeKey = `${method}:${path}`;\n \n target.constructor._requestMetadata.set(routeKey, {\n key: propertyKey,\n path,\n method,\n bodyValidation,\n });\n };\n}\n\nexport interface RoomOptions {\n path: string;\n maxUsers?: number;\n throttleStorage?: number;\n throttleSync?: number;\n hibernate?: boolean;\n guards?: RoomGuardFn[];\n sessionExpiryTime?: number;\n}\n\nexport function Room(options: RoomOptions) {\n return function (target: any) {\n target.path = options.path;\n target.prototype.maxUsers = options.maxUsers;\n target.prototype.throttleStorage = options.throttleStorage;\n target.prototype.throttleSync = options.throttleSync;\n target.prototype.sessionExpiryTime = options.sessionExpiryTime ?? 5 * 60 * 1000;\n if (options.guards) {\n target['_roomGuards'] = options.guards;\n }\n };\n}\n\n/**\n * Room guard decorator\n * @param guards Array of guard functions to check on connection\n */\nexport function RoomGuard(guards: RoomGuardFn[]) {\n return function (target: any) {\n target['_roomGuards'] = guards;\n };\n}\n\n/**\n * Action guard decorator\n * @param guards Array of guard functions to check before action execution\n */\nexport function Guard(guards: GuardFn[]) {\n return function (\n target: any,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n if (!target.constructor['_actionGuards']) {\n target.constructor['_actionGuards'] = new Map();\n }\n if (!Array.isArray(guards)) {\n guards = [guards]\n }\n target.constructor['_actionGuards'].set(propertyKey, guards);\n };\n}","/**\n * Checks if the given value is a function.\n *\n * @param {unknown} val - The value to check.\n * @returns {boolean} - True if the value is a function, false otherwise.\n * @example\n * isFunction(function() {}); // true\n * isFunction(() => {}); // true\n * isFunction(123); // false\n */\nexport function isFunction(val: unknown): boolean {\n return {}.toString.call(val) === \"[object Function]\";\n}\n\n/**\n * Checks if the given object is a class.\n *\n * @param {any} obj - The object to check.\n * @returns {boolean} - True if the object is a class, false otherwise.\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: any): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n/**\n * Checks if the given item is an object.\n *\n * @param {any} item - The item to check.\n * @returns {boolean} - True if the item is an object, false otherwise.\n * @example\n * isObject({}); // true\n * isObject(null); // false\n * isObject([]); // false\n */\nexport const isObject = (item: any): boolean =>\n item && typeof item === \"object\" && !Array.isArray(item) && item !== null;\n\n/**\n * Checks if the given value is an instance of a class.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - True if the value is an instance of a class, false otherwise.\n * @example\n * class MyClass {}\n * const instance = new MyClass();\n * isInstanceOfClass(instance); // true\n * isInstanceOfClass({}); // false\n */\nexport function isInstanceOfClass(value: unknown): boolean {\n if (\n value === null ||\n typeof value !== \"object\" ||\n value === undefined ||\n Array.isArray(value)\n ) {\n return false;\n }\n return Object.getPrototypeOf(value) !== Object.prototype;\n}\n\nexport function generateShortUUID(): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let uuid = '';\n for (let i = 0; i < 8; i++) {\n const randomIndex = Math.floor(Math.random() * chars.length);\n uuid += chars[randomIndex];\n }\n return uuid;\n}","export class Storage {\n private memory = new Map();\n async put(key, value) {\n this.memory.set(key, value);\n }\n async get(key) {\n return this.memory.get(key);\n }\n async delete(key) {\n this.memory.delete(key);\n }\n async list() {\n return this.memory;\n }\n}\n","import { dset } from \"dset\";\nimport z from \"zod\";\nimport {\n createStatesSnapshot,\n getByPath,\n load,\n syncClass,\n DELETE_TOKEN,\n generateShortUUID\n} from \"@signe/sync\";\nimport type * as Party from \"./types/party\";\nimport {\n awaitReturn,\n buildObject,\n extractParams,\n isClass,\n throttle,\n} from \"./utils\";\nimport { ServerResponse } from \"./request/response\";\nimport { createCorsInterceptor } from \"./request/cors\";\nimport { Signal, WritableSignal } from \"@signe/reactive\";\n\nconst Message = z.object({\n action: z.string(),\n value: z.any(),\n});\n\ntype CreateRoomOptions = {\n getMemoryAll?: boolean;\n sessionExpiryTime?: number;\n throttleSync?: number;\n throttleStorage?: number;\n};\n\n/**\n * @class Server\n * @implements {Party.Server}\n * @description Represents a server that manages rooms and connections for a multiplayer game or application.\n * \n * @example\n * ```typescript\n * import { Room, Server, ServerIo } from \"@yourpackage/room\";\n * \n * @Room({ path: \"game\" })\n * class GameRoom {\n * // Room implementation\n * }\n * \n * class MyServer extends Server {\n * rooms = [GameRoom];\n * }\n * \n * const server = new MyServer(new ServerIo(\"game\"));\n * server.onStart();\n * ```\n */\nexport class Server implements Party.Server {\n subRoom = null;\n rooms: any[] = [];\n\n /**\n * @constructor\n * @param {Party.Room} room - The room object representing the current game or application instance.\n * \n * @example\n * ```typescript\n * const server = new MyServer(new ServerIo(\"game\"));\n * ```\n */\n constructor(readonly room: Party.Room) { }\n\n /**\n * @readonly\n * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.\n * \n * @example\n * ```typescript\n * if (!server.isHibernate) {\n * console.log(\"Server is active\");\n * }\n * ```\n */\n get isHibernate(): boolean {\n return !!this[\"options\"]?.hibernate;\n }\n\n get roomStorage(): Party.Storage {\n return this.room.storage\n }\n\n async send(conn: Party.Connection, obj: any, subRoom: any) {\n obj = structuredClone(obj);\n if (subRoom.interceptorPacket) {\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = conn.state as any;\n const user = signal?.()[publicId];\n obj = await awaitReturn(subRoom[\"interceptorPacket\"]?.(user, obj, conn));\n if (obj === null) return;\n }\n conn.send(JSON.stringify(obj));\n }\n\n broadcast(obj: any, subRoom: any) {\n for (let conn of this.room.getConnections()) {\n this.send(conn, obj, subRoom);\n }\n }\n\n /**\n * @method onStart\n * @async\n * @description Initializes the server and creates the initial room if not in hibernate mode.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * async function initServer() {\n * await server.onStart();\n * console.log(\"Server started\");\n * }\n * ```\n */\n\n async onStart() {\n // Only create a room if not in hibernate mode\n // This prevents unnecessary resource allocation for inactive rooms\n if (!this.isHibernate) {\n this.subRoom = await this.createRoom();\n }\n }\n\n async runGarbageCollector() {\n await this.garbageCollector({ sessionExpiryTime: -1 });\n }\n\n private async garbageCollector(options: { sessionExpiryTime: number }) {\n const subRoom = await this.getSubRoom();\n if (!subRoom) return;\n\n // Get active connections\n const activeConnections = [...this.room.getConnections()];\n const activePrivateIds = new Set(activeConnections.map(conn => conn.id));\n\n try {\n // Get all sessions from storage\n const sessions = await this.room.storage.list();\n const users = this.getUsersProperty(subRoom);\n const usersPropName = this.getUsersPropName(subRoom);\n\n // Store valid publicIds from sessions\n const validPublicIds = new Set<string>();\n const expiredPublicIds = new Set<string>();\n const SESSION_EXPIRY_TIME = options.sessionExpiryTime\n const now = Date.now();\n\n for (const [key, session] of sessions) {\n // Only process session entries\n if (!key.startsWith('session:')) continue;\n\n const privateId = key.replace('session:', '');\n const typedSession = session as { publicId: string, created: number, connected: boolean };\n\n // Check if session should be deleted based on:\n // 1. Connection is not active\n // 2. Session is marked as disconnected\n // 3. Session is older than expiry time\n if (!activePrivateIds.has(privateId) &&\n !typedSession.connected &&\n (now - typedSession.created) > SESSION_EXPIRY_TIME) {\n // Delete expired session\n await this.deleteSession(privateId);\n expiredPublicIds.add(typedSession.publicId);\n } else if (typedSession && typedSession.publicId) {\n // Keep track of valid publicIds from active or recent sessions\n validPublicIds.add(typedSession.publicId);\n }\n }\n\n // Clean up users only if ALL their sessions are expired\n if (users && usersPropName) {\n const currentUsers = users();\n for (const publicId in currentUsers) {\n // Only delete user if they have an expired session and no valid sessions\n if (expiredPublicIds.has(publicId) && !validPublicIds.has(publicId)) {\n delete currentUsers[publicId];\n await this.room.storage.delete(`${usersPropName}.${publicId}`);\n }\n }\n }\n\n } catch (error) {\n console.error('Error in garbage collector:', error);\n }\n }\n\n /**\n * @method createRoom\n * @private\n * @async\n * @param {CreateRoomOptions} [options={}] - Options for creating the room.\n * @returns {Promise<Object>} The created room instance.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalCreateRoom() {\n * const room = await this.createRoom({ getMemoryAll: true });\n * console.log(\"Room created:\", room);\n * }\n * ```\n */\n private async createRoom(options: CreateRoomOptions = {}) {\n let instance\n let init = true\n let initPersist = true\n\n // Find the appropriate room based on the current room ID\n for (let room of this.rooms) {\n const params = extractParams(room.path, this.room.id);\n if (params) {\n instance = new room(this.room, params);\n break;\n }\n }\n\n if (!instance) {\n return null;\n }\n\n // Load the room's memory from storage\n // This ensures persistence across server restarts\n const loadMemory = async () => {\n const root = await this.room.storage.get(\".\");\n const memory = await this.room.storage.list();\n const tmpObject: any = root || {};\n for (let [key, value] of memory) {\n if (key.startsWith('session:')) {\n continue;\n }\n if (key == \".\") {\n continue;\n }\n dset(tmpObject, key, value);\n }\n load(instance, tmpObject, true);\n };\n\n instance.$memoryAll = {}\n instance.$send = (conn: Party.Connection, obj: any) => {\n return this.send(conn, obj, instance)\n }\n instance.$broadcast = (obj: any) => {\n return this.broadcast(obj, instance)\n }\n instance.$sessionTransfer = async (conn: Party.Connection, targetRoomId: string) => {\n let user: any;\n \n const signal = this.getUsersProperty(instance);\n \n if (!signal) {\n console.error('[sessionTransfer] `users` property not defined in the room.');\n return null;\n }\n \n const { publicId } = conn.state as any;\n user = signal()[publicId];\n\n if (!user) {\n console.error(`[sessionTransfer] User with publicId ${publicId} not found.`);\n return null;\n }\n\n const sessions = await this.room.storage.list();\n let userSession: any = null;\n let privateId: string | null = null;\n\n for (const [key, session] of sessions) {\n if (key.startsWith('session:') && (session as any).publicId === publicId) {\n userSession = session;\n privateId = key.replace('session:', '');\n break;\n }\n }\n\n if (!userSession || !privateId) {\n console.error(`[sessionTransfer] Session for publicId ${publicId} not found.`);\n return null;\n }\n\n const usersPropName = this.getUsersPropName(instance);\n if (!usersPropName) {\n console.error('[sessionTransfer] `users` property not defined in the room.');\n return null;\n }\n\n // Create a snapshot of the user state\n const userSnapshot = createStatesSnapshot(user);\n\n const transferData = {\n privateId,\n userSnapshot,\n sessionState: userSession.state,\n publicId\n };\n\n try {\n const targetRoomParty = await this.room.context.parties.main.get(targetRoomId);\n const response = await targetRoomParty.fetch('/session-transfer', {\n method: 'POST',\n body: JSON.stringify(transferData),\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Transfer request failed: ${await response.text()}`);\n }\n\n const { transferToken } = await response.json();\n \n return transferToken;\n } catch (error) {\n console.error(`[sessionTransfer] Failed to transfer session to room ${targetRoomId}:`, error);\n return null;\n }\n };\n\n // Sync callback: Broadcast changes to all clients\n const syncCb = (values) => {\n if (options.getMemoryAll) {\n buildObject(values, instance.$memoryAll);\n }\n if (init && this.isHibernate) {\n init = false;\n return;\n }\n const packet = buildObject(values, instance.$memoryAll);\n this.broadcast(\n {\n type: \"sync\",\n value: packet,\n },\n instance\n );\n values.clear();\n }\n\n // Persist callback: Save changes to storage\n const persistCb = async (values: Map<string, any>) => {\n if (initPersist) {\n values.clear();\n return;\n }\n for (let [path, value] of values) {\n const _instance =\n path == \".\" ? instance : getByPath(instance, path);\n const itemValue = createStatesSnapshot(_instance);\n if (value == DELETE_TOKEN) {\n await this.room.storage.delete(path);\n } else {\n await this.room.storage.put(path, itemValue);\n }\n }\n values.clear();\n }\n\n // Set up syncing and persistence with throttling to optimize performance\n syncClass(instance, {\n onSync: throttle(syncCb, instance[\"throttleSync\"] ?? 500),\n onPersist: throttle(persistCb, instance[\"throttleStorage\"] ?? 2000),\n });\n\n await loadMemory();\n\n initPersist = false\n\n return instance\n }\n\n /**\n * @method getSubRoom\n * @private\n * @async\n * @param {Object} [options={}] - Options for getting the sub-room.\n * @returns {Promise<Object>} The sub-room instance.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalGetSubRoom() {\n * const subRoom = await this.getSubRoom();\n * console.log(\"Sub-room retrieved:\", subRoom);\n * }\n * ```\n */\n private async getSubRoom(options = {}): Promise<any | null> {\n let subRoom // instance of the room or null\n if (this.isHibernate) {\n subRoom = await this.createRoom(options)\n }\n else {\n subRoom = this.subRoom\n }\n return subRoom\n }\n\n /**\n * @method getUsersProperty\n * @private\n * @param {Object} subRoom - The sub-room instance.\n * @returns {Object|null} The users property of the sub-room, or null if not found.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * function internalGetUsers(subRoom) {\n * const users = this.getUsersProperty(subRoom);\n * console.log(\"Users:\", users);\n * }\n * ```\n */\n\n private getUsersProperty(subRoom) {\n const meta = subRoom.constructor[\"_propertyMetadata\"];\n const propId = meta?.get(\"users\");\n if (propId) {\n return subRoom[propId];\n }\n return null;\n }\n\n private getUsersPropName(subRoom) {\n if (!subRoom) return null;\n const metadata = subRoom.constructor._propertyMetadata;\n if (!metadata) return null;\n return metadata.get(\"users\");\n }\n\n /**\n * Retrieves the connection status property from a user object.\n * \n * @param {any} user - The user object to get the connection property from.\n * @returns {Function|null} - The connection property signal function or null if not found.\n * @private\n */\n private getUserConnectionProperty(user: any): WritableSignal<boolean> | null {\n if (!user) return null;\n \n const metadata = user.constructor._propertyMetadata;\n if (!metadata) return null;\n \n const connectedPropName = metadata.get(\"connected\");\n if (!connectedPropName) return null;\n\n return user[connectedPropName];\n }\n \n /**\n * Updates a user's connection status in the signal.\n * \n * @param {any} user - The user object to update.\n * @param {boolean} isConnected - The new connection status.\n * @returns {boolean} - Whether the update was successful.\n * @private\n */\n private updateUserConnectionStatus(user: any, isConnected: boolean): boolean {\n const connectionSignal = this.getUserConnectionProperty(user);\n\n if (connectionSignal) {\n connectionSignal.set(isConnected);\n return true;\n }\n \n return false;\n }\n\n /**\n * @method getSession\n * @private\n * @param {string} privateId - The private ID of the session.\n * @returns {Promise<Object|null>} The session object, or null if not found.\n * \n * @example\n * ```typescript\n * const session = await server.getSession(\"privateId\");\n * console.log(session);\n * ```\n */\n async getSession(privateId: string): Promise<{ publicId: string, state?: any, created?: number, connected?: boolean } | null> {\n if (!privateId) return null;\n try {\n const session = await this.room.storage.get(`session:${privateId}`);\n return session as { publicId: string, state?: any, created: number, connected: boolean } | null;\n } catch (e) {\n return null;\n }\n }\n\n private async saveSession(privateId: string, data: { publicId: string, state?: any, created?: number, connected?: boolean }) {\n const sessionData = {\n ...data,\n created: data.created || Date.now(),\n connected: data.connected !== undefined ? data.connected : true\n };\n await this.room.storage.put(`session:${privateId}`, sessionData);\n }\n\n private async updateSessionConnection(privateId: string, connected: boolean) {\n const session = await this.getSession(privateId);\n if (session) {\n await this.saveSession(privateId, { ...session, connected });\n }\n }\n\n /**\n * @method deleteSession\n * @private\n * @param {string} privateId - The private ID of the session to delete.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * await server.deleteSession(\"privateId\");\n * ```\n */\n async deleteSession(privateId: string) {\n await this.room.storage.delete(`session:${privateId}`);\n }\n\n async onConnectClient(conn: Party.Connection, ctx: Party.ConnectionContext) {\n const subRoom = await this.getSubRoom({\n getMemoryAll: true,\n })\n\n if (!subRoom) {\n conn.close();\n return;\n }\n\n const sessionExpiryTime = subRoom.constructor.sessionExpiryTime;\n await this.garbageCollector({ sessionExpiryTime });\n\n // Check room guards\n const roomGuards = subRoom.constructor['_roomGuards'] || [];\n for (const guard of roomGuards) {\n const isAuthorized = await guard(conn, ctx, this.room);\n if (!isAuthorized) {\n conn.close();\n return;\n }\n }\n\n // Handle session transfer\n let transferToken = null;\n if (ctx.request?.url) {\n const url = new URL(ctx.request.url);\n transferToken = url.searchParams.get('transferToken');\n }\n let transferData: any = null;\n if (transferToken) {\n transferData = await this.room.storage.get(`transfer:${transferToken}`);\n if (transferData) {\n await this.room.storage.delete(`transfer:${transferToken}`);\n }\n }\n\n // Check for existing session\n const existingSession = await this.getSession(conn.id)\n\n // Generate IDs\n const publicId = existingSession?.publicId || transferData?.publicId || generateShortUUID();\n\n let user = null;\n const signal = this.getUsersProperty(subRoom);\n const usersPropName = this.getUsersPropName(subRoom);\n\n if (signal) {\n const { classType } = signal.options;\n\n // Restore state if exists\n if (!existingSession?.publicId) {\n // Check if we have a transferred user already restored\n if (transferData?.restored && signal()[publicId]) {\n user = signal()[publicId];\n } else {\n user = isClass(classType) ? new classType() : classType(conn, ctx);\n signal()[publicId] = user;\n const snapshot = createStatesSnapshot(user);\n this.room.storage.put(`${usersPropName}.${publicId}`, snapshot);\n }\n }\n else {\n user = signal()[existingSession.publicId];\n }\n\n // Only store new session if it doesn't exist\n if (!existingSession) {\n // Use the transferred privateId if available, otherwise use connection id\n const sessionPrivateId = transferData?.privateId || conn.id;\n await this.saveSession(sessionPrivateId, {\n publicId\n });\n }\n else {\n await this.updateSessionConnection(conn.id, true);\n }\n }\n // Update user connection status if applicable\n this.updateUserConnectionStatus(user, true);\n\n // Store both IDs in connection state\n conn.setState({\n ...conn.state,\n publicId\n });\n\n // Call the room's onJoin method if it exists\n await awaitReturn(subRoom[\"onJoin\"]?.(user, conn, ctx));\n\n // Send initial sync data with both IDs to the new connection\n this.send(conn, {\n type: \"sync\",\n value: {\n pId: publicId,\n ...subRoom.$memoryAll,\n },\n }, subRoom)\n }\n\n /**\n * @method onConnect\n * @async\n * @param {Party.Connection} conn - The connection object for the new user.\n * @param {Party.ConnectionContext} ctx - The context of the connection.\n * @description Handles a new user connection, creates a user object, and sends initial sync data.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onConnect = async (conn, ctx) => {\n * await server.onConnect(conn, ctx);\n * console.log(\"New user connected:\", conn.id);\n * };\n * ```\n */\n async onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {\n if (ctx.request?.headers.has('x-shard-id')) {\n this.onConnectShard(conn, ctx);\n }\n else {\n await this.onConnectClient(conn, ctx);\n }\n }\n\n /**\n * @method onConnectShard\n * @private\n * @param {Party.Connection} conn - The connection object for the new shard.\n * @param {Party.ConnectionContext} ctx - The context of the shard connection.\n * @description Handles a new shard connection, setting up the necessary state.\n * @returns {void}\n */\n onConnectShard(conn: Party.Connection, ctx: Party.ConnectionContext) {\n // Set shard metadata in connection state\n const shardId = ctx.request?.headers.get('x-shard-id') || 'unknown-shard';\n conn.setState({\n shard: true,\n shardId,\n clients: new Map() // Track clients connected through this shard\n });\n }\n\n /**\n * @method onMessage\n * @async\n * @param {string} message - The message received from a user or shard.\n * @param {Party.Connection} sender - The connection object of the sender.\n * @description Processes incoming messages, handling differently based on if sender is shard or client.\n * @returns {Promise<void>}\n */\n async onMessage(message: string, sender: Party.Connection) {\n // Check if message is from a shard\n if (sender.state && (sender.state as any).shard) {\n await this.handleShardMessage(message, sender);\n return;\n }\n\n // Regular client message handling\n let json\n try {\n json = JSON.parse(message)\n }\n catch (e) {\n return;\n }\n\n // Validate incoming messages\n const result = Message.safeParse(json);\n if (!result.success) {\n return;\n }\n\n const subRoom = await this.getSubRoom()\n\n if (!subRoom) {\n console.warn(\"Room not found\");\n return;\n }\n\n // Check room guards\n const roomGuards = subRoom.constructor['_roomGuards'] || [];\n for (const guard of roomGuards) {\n const isAuthorized = await guard(sender, result.data.value, this.room);\n if (!isAuthorized) {\n return;\n }\n }\n\n const actions = subRoom.constructor[\"_actionMetadata\"];\n if (actions) {\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = sender.state as any;\n const user = signal?.()[publicId];\n const actionName = actions.get(result.data.action);\n if (actionName) {\n\n // Check all guards if they exist\n const guards = subRoom.constructor['_actionGuards']?.get(actionName.key) || [];\n for (const guard of guards) {\n const isAuthorized = await guard(sender, result.data.value);\n if (!isAuthorized) {\n return;\n }\n }\n\n // Validate action body if a validation schema is defined\n if (actionName.bodyValidation) {\n const bodyResult = actionName.bodyValidation.safeParse(\n result.data.value\n );\n if (!bodyResult.success) {\n return;\n }\n }\n // Execute the action\n await awaitReturn(\n subRoom[actionName.key](user, result.data.value, sender)\n );\n }\n }\n }\n\n /**\n * @method handleShardMessage\n * @private\n * @async\n * @param {string} message - The message received from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Processes messages from shards, extracting client information.\n * @returns {Promise<void>}\n */\n private async handleShardMessage(message: string, shardConnection: Party.Connection) {\n let parsedMessage;\n try {\n parsedMessage = JSON.parse(message);\n } catch (e) {\n console.error(\"Error parsing shard message:\", e);\n return;\n }\n\n const shardState = shardConnection.state as any;\n const clients = shardState.clients;\n\n switch (parsedMessage.type) {\n case 'shard.clientConnected':\n // Handle new client connection through shard\n await this.handleShardClientConnect(parsedMessage, shardConnection);\n break;\n\n case 'shard.clientMessage':\n // Handle message from a client through shard\n await this.handleShardClientMessage(parsedMessage, shardConnection);\n break;\n\n case 'shard.clientDisconnected':\n // Handle client disconnection through shard\n await this.handleShardClientDisconnect(parsedMessage, shardConnection);\n break;\n\n default:\n console.warn(`Unknown shard message type: ${parsedMessage.type}`);\n }\n }\n\n /**\n * @method handleShardClientConnect\n * @private\n * @async\n * @param {Object} message - The client connection message from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Handles a new client connection via a shard.\n * @returns {Promise<void>}\n */\n private async handleShardClientConnect(message: any, shardConnection: Party.Connection) {\n const { privateId, requestInfo } = message;\n const shardState = shardConnection.state as any;\n\n // Create a virtual connection context for the client\n const virtualContext: Party.ConnectionContext = {\n request: requestInfo ? {\n headers: new Headers(requestInfo.headers),\n method: requestInfo.method,\n url: requestInfo.url\n } as unknown as Party.Request : undefined\n };\n\n // Create a virtual connection for the client\n const virtualConnection: Partial<Party.Connection> = {\n id: privateId,\n send: (data: string) => {\n // Forward to the actual client through the shard\n shardConnection.send(JSON.stringify({\n targetClientId: privateId,\n data\n }));\n },\n state: {},\n setState: (state: unknown) => {\n // Store client state in the shard's client map\n const clients = shardState.clients;\n const currentState = clients.get(privateId) || {};\n const mergedState = Object.assign({}, currentState, state as object);\n clients.set(privateId, mergedState);\n\n // Update our virtual connection's state reference\n virtualConnection.state = clients.get(privateId);\n return virtualConnection.state as any;\n },\n close: () => {\n // Send close command to the shard\n shardConnection.send(JSON.stringify({\n type: 'shard.closeClient',\n privateId\n }));\n\n // Clean up virtual connection\n if (shardState.clients) {\n shardState.clients.delete(privateId);\n }\n }\n };\n\n // Initialize the client's state in the shard state\n if (!shardState.clients.has(privateId)) {\n shardState.clients.set(privateId, {});\n }\n\n // Now handle this virtual connection as a regular client connection\n await this.onConnectClient(virtualConnection as Party.Connection, virtualContext);\n }\n\n /**\n * @method handleShardClientMessage\n * @private\n * @async\n * @param {Object} message - The client message from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Handles a message from a client via a shard.\n * @returns {Promise<void>}\n */\n private async handleShardClientMessage(message: any, shardConnection: Party.Connection) {\n const { privateId, publicId, payload } = message;\n const shardState = shardConnection.state as any;\n const clients = shardState.clients;\n\n // Get or create virtual connection for this client\n if (!clients.has(privateId)) {\n console.warn(`Received message from unknown client ${privateId}, creating virtual connection`);\n clients.set(privateId, { publicId });\n }\n\n // Create a virtual connection for the client\n const virtualConnection: Partial<Party.Connection> = {\n id: privateId,\n send: (data: string) => {\n // Forward to the actual client through the shard\n shardConnection.send(JSON.stringify({\n targetClientId: privateId,\n data\n }));\n },\n state: clients.get(privateId),\n setState: (state: unknown) => {\n const currentState = clients.get(privateId) || {};\n const mergedState = Object.assign({}, currentState, state as object);\n clients.set(privateId, mergedState);\n virtualConnection.state = clients.get(privateId);\n return virtualConnection.state as any;\n },\n close: () => {\n shardConnection.send(JSON.stringify({\n type: 'shard.closeClient',\n privateId\n }));\n\n if (shardState.clients) {\n shardState.clients.delete(privateId);\n }\n }\n };\n\n // Process the payload using the regular message handler\n const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);\n await this.onMessage(payloadString, virtualConnection as Party.Connection);\n }\n\n /**\n * @method handleShardClientDisconnect\n * @private\n * @async\n * @param {Object} message - The client disconnection message from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Handles a client disconnection via a shard.\n * @returns {Promise<void>}\n */\n private async handleShardClientDisconnect(message: any, shardConnection: Party.Connection) {\n const { privateId, publicId } = message;\n const shardState = shardConnection.state as any;\n const clients = shardState.clients;\n\n // Get client state\n const clientState = clients.get(privateId);\n if (!clientState) {\n console.warn(`Disconnection for unknown client ${privateId}`);\n return;\n }\n\n // Create a virtual connection for the client one last time\n const virtualConnection: Partial<Party.Connection> = {\n id: privateId,\n send: () => { }, // No-op since client is disconnecting\n state: clientState,\n setState: () => {\n // No-op since client is disconnecting\n return {} as any;\n },\n close: () => { }\n };\n\n // Handle disconnection with the regular onClose handler\n await this.onClose(virtualConnection as Party.Connection);\n\n // Clean up\n clients.delete(privateId);\n }\n\n /**\n * @method onClose\n * @async\n * @param {Party.Connection} conn - The connection object of the disconnecting user.\n * @description Handles user disconnection, removing them from the room and triggering the onLeave event..\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onClose = async (conn) => {\n * await server.onClose(conn);\n * console.log(\"User disconnected:\", conn.id);\n * };\n * ```\n */\n async onClose(conn: Party.Connection) {\n const subRoom = await this.getSubRoom()\n\n if (!subRoom) {\n return;\n }\n\n const signal = this.getUsersProperty(subRoom);\n\n if (!conn.state) {\n return;\n }\n\n const privateId = conn.id;\n const { publicId } = conn.state as any;\n const user = signal?.()[publicId];\n\n if (!user) return;\n\n // Mark session as disconnected instead of deleting it\n await this.updateSessionConnection(privateId, false);\n\n // Update user connection status in the signal\n const connectionUpdated = this.updateUserConnectionStatus(user, false);\n\n await awaitReturn(subRoom[\"onLeave\"]?.(user, conn));\n \n // Only broadcast disconnection if we couldn't update the connection signal\n if (!connectionUpdated) {\n // Broadcast user disconnection the old way\n this.broadcast({\n type: \"user_disconnected\",\n value: { publicId }\n }, subRoom);\n }\n }\n\n async onAlarm() {\n const subRoom = await this.getSubRoom()\n await awaitReturn(subRoom[\"onAlarm\"]?.(subRoom));\n }\n\n async onError(connection: Party.Connection, error: Error) {\n const subRoom = await this.getSubRoom()\n await awaitReturn(subRoom[\"onError\"]?.(connection, error));\n }\n\n /**\n * @method onRequest\n * @async\n * @param {Party.Request} req - The HTTP request to handle\n * @description Handles HTTP requests, either directly from clients or forwarded by shards\n * @returns {Promise<Response>} The response to return to the client\n */\n async onRequest(req: Party.Request) {\n // Check if the request is coming from a shard\n const isFromShard = req.headers.has('x-forwarded-by-shard');\n const shardId = req.headers.get('x-shard-id');\n \n // Create a response with proper CORS configuration\n const res = new ServerResponse([\n createCorsInterceptor()\n ]);\n\n if (req.method === 'OPTIONS') {\n // For OPTIONS requests, just return a 200 OK with CORS headers\n return res.status(200).send({});\n }\n\n if (isFromShard) {\n return this.handleShardRequest(req, res, shardId);\n }\n\n // Handle regular client request\n return this.handleDirectRequest(req, res);\n }\n\n\n /**\n * @method handleSessionRestore\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request for session restore\n * @param {ServerResponse} res - The response object\n * @description Handles session restoration from transfer data, creates session from privateId\n * @returns {Promise<Response>} The response to return to the client\n */\n private async handleSessionRestore(req: Party.Request, res: ServerResponse): Promise<Response> {\n try {\n const transferData = await req.json() as {\n privateId: string;\n userSnapshot?: any;\n sessionState?: any;\n publicId: string;\n };\n const { privateId, userSnapshot, sessionState, publicId } = transferData;\n\n if (!privateId || !publicId) {\n return res.badRequest('Missing privateId or publicId in transfer data');\n }\n\n const subRoom = await this.getSubRoom();\n if (!subRoom) {\n return res.serverError('Room not available');\n }\n\n // Create session from privateId\n await this.saveSession(privateId, {\n publicId,\n state: sessionState,\n created: Date.now(),\n connected: false // Will be set to true when user connects\n });\n\n // If userSnapshot exists, restore user data\n if (userSnapshot) {\n const signal = this.getUsersProperty(subRoom);\n const usersPropName = this.getUsersPropName(subRoom);\n \n if (signal && usersPropName) {\n const { classType } = signal.options;\n \n // Create new user instance\n const user = isClass(classType) ? new classType() : classType();\n \n // Load user data from snapshot\n load(user, userSnapshot, true);\n \n // Add user to signal\n signal()[publicId] = user;\n\n // Save user snapshot to storage\n await this.room.storage.put(`${usersPropName}.${publicId}`, userSnapshot);\n }\n }\n\n // Generate transfer token for the client to use when connecting\n const transferToken = generateShortUUID();\n await this.room.storage.put(`transfer:${transferToken}`, {\n privateId,\n publicId,\n restored: true\n });\n\n return res.success({ transferToken });\n } catch (error) {\n console.error('Error restoring session:', error);\n return res.serverError('Failed to restore session');\n }\n }\n\n /**\n * @method handleDirectRequest\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request received directly from a client\n * @description Processes requests received directly from clients\n * @returns {Promise<Response>} The response to return to the client\n */\n private async handleDirectRequest(req: Party.Request, res: ServerResponse): Promise<Response> {\n const subRoom = await this.getSubRoom();\n\n if (!subRoom) {\n return res.notFound();\n }\n\n const url = new URL(req.url);\n if (url.pathname.endsWith('/session-transfer') && req.method === 'POST') {\n return this.handleSessionRestore(req, res);\n }\n\n // First try to match using the registered @Request handlers\n const response = await this.tryMatchRequestHandler(req, res, subRoom);\n if (response) {\n return response;\n }\n\n // Fall back to the legacy onRequest method if no handler matched\n const legacyResponse = await awaitReturn(subRoom[\"onRequest\"]?.(req, res));\n if (!legacyResponse) {\n return res.notFound();\n }\n if (legacyResponse instanceof Response) {\n return legacyResponse;\n }\n return res.success(legacyResponse);\n }\n\n /**\n * @method tryMatchRequestHandler\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request to handle\n * @param {Object} subRoom - The room instance\n * @description Attempts to match the request to a registered @Request handler\n * @returns {Promise<Response | null>} The response or null if no handler matched\n */\n private async tryMatchRequestHandler(req: Party.Request, res: ServerResponse, subRoom: any): Promise<Response | null> {\n const requestHandlers = subRoom.constructor[\"_requestMetadata\"];\n if (!requestHandlers) {\n return null;\n }\n\n const url = new URL(req.url);\n const method = req.method;\n let pathname = url.pathname;\n\n pathname = '/' + pathname.split('/').slice(4).join('/');\n\n // Check each registered handler\n for (const [routeKey, handler] of requestHandlers.entries()) {\n const firstColonIndex = routeKey.indexOf(':');\n const handlerMethod = routeKey.substring(0, firstColonIndex);\n const handlerPath = routeKey.substring(firstColonIndex + 1);\n\n // Check if method matches\n if (handlerMethod !== method) {\n continue;\n }\n\n // Simple path matching (could be enhanced with path params)\n if (this.pathMatches(pathname, handlerPath)) {\n // Extract path params if any\n const params = this.extractPathParams(pathname, handlerPath);\n // Check request guards if they exist\n const guards = subRoom.constructor['_actionGuards']?.get(handler.key) || [];\n for (const guard of guards) {\n const isAuthorized = await guard(null, req, this.room);\n if (isAuthorized instanceof Response) {\n return isAuthorized;\n }\n if (!isAuthorized) {\n return res.notPermitted();\n }\n }\n\n // Validate request body if needed\n let bodyData = null;\n if (handler.bodyValidation && ['POST', 'PUT', 'PATCH'].includes(method)) {\n try {\n const contentType = req.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n const body = await req.json();\n const validation = handler.bodyValidation.safeParse(body);\n if (!validation.success) {\n return res.badRequest(\"Invalid request body\", {\n details: validation.error\n });\n }\n bodyData = validation.data;\n }\n } catch (error) {\n return res.badRequest(\"Failed to parse request body\");\n }\n }\n\n // Execute the handler method\n try {\n req['data'] = bodyData;\n req['params'] = params;\n const result = await awaitReturn(\n subRoom[handler.key](req, res)\n );\n\n if (result instanceof Response) {\n return result;\n }\n\n return res.success(result);\n } catch (error) {\n console.error('Error executing request handler:', error);\n return res.serverError();\n }\n }\n }\n\n return null;\n }\n\n /**\n * @method pathMatches\n * @private\n * @param {string} requestPath - The path from the request\n * @param {string} handlerPath - The path pattern from the handler\n * @description Checks if a request path matches a handler path pattern\n * @returns {boolean} True if the paths match\n */\n private pathMatches(requestPath: string, handlerPath: string): boolean {\n // Convert handler path pattern to regex\n // Replace :param with named capture groups\n const pathRegexString = handlerPath\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/:([^\\/]+)/g, '([^/]+)'); // Convert :params to capture groups\n\n const pathRegex = new RegExp(`^${pathRegexString}`);\n return pathRegex.test(requestPath);\n }\n\n /**\n * @method extractPathParams\n * @private\n * @param {string} requestPath - The path from the request\n * @param {string} handlerPath - The path pattern from the handler\n * @description Extracts path parameters from the request path based on the handler pattern\n * @returns {Object} An object containing the path parameters\n */\n private extractPathParams(requestPath: string, handlerPath: string): Record<string, string> {\n const params: Record<string, string> = {};\n\n // Extract parameter names from handler path\n const paramNames: string[] = [];\n handlerPath.split('/').forEach(segment => {\n if (segment.startsWith(':')) {\n paramNames.push(segment.substring(1));\n }\n });\n\n // Extract parameter values from request path\n const pathRegexString = handlerPath\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/:([^\\/]+)/g, '([^/]+)'); // Convert :params to capture groups\n\n const pathRegex = new RegExp(`^${pathRegexString}`);\n const matches = requestPath.match(pathRegex);\n\n if (matches && matches.length > 1) {\n // Skip the first match (the full string)\n for (let i = 0; i < paramNames.length; i++) {\n params[paramNames[i]] = matches[i + 1];\n }\n }\n\n return params;\n }\n\n /**\n * @method handleShardRequest\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request forwarded by a shard\n * @param {string | null} shardId - The ID of the shard that forwarded the request\n * @description Processes requests forwarded by shards, preserving client context\n * @returns {Promise<Response>} The response to return to the shard (which will forward it to the client)\n */\n private async handleShardRequest(req: Party.Request, res: ServerResponse, shardId: string | null): Promise<Response> {\n const subRoom = await this.getSubRoom();\n\n if (!subRoom) {\n return res.notFound();\n }\n\n // Create a context that preserves original client information\n const originalClientIp = req.headers.get('x-original-client-ip');\n const enhancedReq = this.createEnhancedRequest(req, originalClientIp);\n \n try {\n // First try to match using the registered @Request handlers\n const response = await this.tryMatchRequestHandler(enhancedReq, res, subRoom);\n if (response) {\n return response;\n }\n\n // Fall back to the legacy onRequest handler\n const legacyResponse = await awaitReturn(subRoom[\"onRequest\"]?.(enhancedReq, res));\n\n if (!legacyResponse) {\n return res.notFound();\n }\n\n if (legacyResponse instanceof Response) {\n return legacyResponse;\n }\n\n return res.success(legacyResponse);\n } catch (error) {\n console.error(`Error processing request from shard ${shardId}:`, error);\n return res.serverError();\n }\n }\n\n /**\n * @method createEnhancedRequest\n * @private\n * @param {Party.Request} originalReq - The original request received from the shard\n * @param {string | null} originalClientIp - The original client IP, if available\n * @description Creates an enhanced request object that preserves the original client context\n * @returns {Party.Request} The enhanced request object\n */\n private createEnhancedRequest(originalReq: Party.Request, originalClientIp: string | null): Party.Request {\n // Clone the original request to avoid mutating it\n const clonedReq = originalReq.clone();\n\n // Add a custom property to the request to indicate it came via a shard\n (clonedReq as any).viaShard = true;\n\n // If we have the original client IP, we can use it for things like rate limiting or geolocation\n if (originalClientIp) {\n (clonedReq as any).originalClientIp = originalClientIp;\n }\n\n return clonedReq;\n }\n}\n","import { dset } from \"dset\";\n\n/**\n * Checks if a value is a Promise.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - Returns true if the value is a Promise, otherwise false.\n *\n * @example\n * isPromise(Promise.resolve()); // true\n * isPromise(42); // false\n */\nexport function isPromise(value: unknown): value is Promise<any> {\n return value instanceof Promise;\n}\n\n/**\n * Awaits the given value if it is a Promise, otherwise returns the value directly.\n *\n * @param {unknown} val - The value to await or return.\n * @returns {Promise<any>} - Returns a Promise that resolves to the value.\n *\n * @example\n * awaitReturn(Promise.resolve(42)); // 42\n * awaitReturn(42); // 42\n */\nexport async function awaitReturn(val: unknown): Promise<any> {\n return isPromise(val) ? await val : val;\n}\n\n/**\n * Checks if a value is a class.\n *\n * @param {unknown} obj - The value to check.\n * @returns {boolean} - Returns true if the value is a class, otherwise false.\n *\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: unknown): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n\n/**\n * Creates a throttled function that only invokes the provided function at most once per every wait milliseconds.\n *\n * The throttled function comes with a cancel method to cancel delayed invocations.\n * If the throttled function is invoked more than once during the wait timeout,\n * it will call the provided function with the latest arguments.\n *\n * @template F - The type of the function to throttle.\n * @param {F} func - The function to throttle.\n * @param {number} wait - The number of milliseconds to throttle invocations to.\n * @returns {(...args: Parameters<F>) => void} - Returns the new throttled function.\n *\n * @example\n * const log = throttle((message) => console.log(message), 1000);\n * log(\"Hello\"); // Will log \"Hello\" immediately\n * log(\"World\"); // Will log \"World\" after 1 second, if no other calls to log() are made within the 1 second.\n */\nexport function throttle<F extends (...args: any[]) => any>(\n func: F,\n wait: number\n): (...args: Parameters<F>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: Parameters<F> | null = null;\n\n return function (...args: Parameters<F>) {\n if (!timeout) {\n func(...args);\n timeout = setTimeout(() => {\n if (lastArgs) {\n func(...lastArgs);\n lastArgs = null;\n }\n timeout = null;\n }, wait);\n } else {\n lastArgs = args;\n }\n };\n}\n\n/**\n * Extracts parameters from a given string based on a specified pattern.\n *\n * The pattern can include placeholders in the form of {paramName}, which will be\n * extracted from the input string if they match.\n *\n * @param {string} pattern - The pattern containing placeholders.\n * @param {string} str - The string to extract parameters from.\n * @returns {{ [key: string]: string } | null} - An object containing the extracted parameters,\n * or null if the string does not match the pattern.\n *\n * @example\n * // returns { id: '123' }\n * extractParams('game-{id}', 'game-123');\n *\n * @example\n * // returns { foo: 'abc', bar: 'xyz' }\n * extractParams('test-{foo}-{bar}', 'test-abc-xyz');\n *\n */\nexport function extractParams(\n pattern: string,\n str: string\n): { [key: string]: string } | null {\n // Replace placeholders in the pattern with named capture groups\n const regexPattern = pattern.replace(/{(\\w+)}/g, \"(?<$1>[\\\\w-]+)\");\n\n // Create a strict regular expression from the pattern\n const regex = new RegExp(`^${regexPattern}$`);\n const match = regex.exec(str);\n\n // If a match is found and groups are present, return the captured groups\n if (match && match.groups) {\n return match.groups;\n } else if (pattern === str) {\n // If the pattern exactly matches the string, return an empty object\n return {};\n } else {\n // Otherwise, return null\n return null;\n }\n}\n\n/**\n * Removes a property from an object based on a dot-separated key string or an array of keys.\n *\n * The function modifies the original object by deleting the specified property.\n * It safely handles dangerous keys like __proto__, constructor, and prototype.\n *\n * @param {Record<string, any>} obj - The object from which to remove the property.\n * @param {string | string[]} keys - The key(s) specifying the property to remove. Can be a dot-separated string or an array of strings.\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, 'a.b.c');\n * // obj is now { a: { b: {} } }\n *\n * @example\n * const obj = { a: 1, b: 2 };\n * dremove(obj, 'a');\n * // obj is now { b: 2 }\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, ['a', 'b', 'c']);\n * // obj is now { a: { b: {} } }\n */\nexport function dremove(\n obj: Record<string, any>,\n keys: string | string[]\n): void {\n // If keys is a string, convert it to an array using the \".\" separator\n if (typeof keys === \"string\") {\n keys = keys.split(\".\");\n }\n\n let i = 0;\n const l = keys.length;\n let t = obj;\n let k;\n\n while (i < l - 1) {\n k = keys[i++];\n if (k === \"__proto__\" || k === \"constructor\" || k === \"prototype\") return; // Avoid dangerous keys\n if (typeof t[k] !== \"object\" || t[k] === null) return; // If the object doesn't exist, stop\n t = t[k];\n }\n\n k = keys[i];\n if (\n t &&\n typeof t === \"object\" &&\n !(k === \"__proto__\" || k === \"constructor\" || k === \"prototype\")\n ) {\n delete t[k];\n }\n}\n\n/**\n * Builds an object from a map of values and updates the provided memory object.\n *\n * For each key-value pair in the map, this function sets the value at the given path in the `memoryObj`.\n * If the value is \"$delete\", it removes the corresponding path from `allMemory`.\n *\n * @param {Map<string, any>} valuesMap - A map where the keys are paths and the values are the values to set at those paths.\n * @param {Record<string, any>} allMemory - The object to update based on the values in the map.\n * @returns {Record<string, any>} - The built memory object with the applied values from the map.\n *\n * @example\n * const valuesMap = new Map();\n * valuesMap.set('a.b.c', 1);\n * valuesMap.set('x.y.z', '$delete');\n * const allMemory = { x: { y: { z: 2 } } };\n * const result = buildObject(valuesMap, allMemory);\n * // result is { a: { b: { c: 1 } }, x: { y: { z: '$delete' } } }\n * // allMemory is { a: { b: { c: 1 } }, x: { y: {} } }\n */\nexport function buildObject(valuesMap: Map<string, any>, allMemory: Record<string, any>): Record<string, any> {\n let memoryObj = {};\n for (let path of valuesMap.keys()) {\n const value = valuesMap.get(path);\n dset(memoryObj, path, value);\n if (value === \"$delete\") {\n dremove(allMemory, path);\n } else {\n dset(allMemory, path, value);\n }\n }\n return memoryObj;\n}\n\nexport function response(status: number, body: any): Response {\n return new Response(JSON.stringify(body), { \n status,\n headers: { 'Content-Type': 'application/json' }\n });\n}","export class ServerResponse {\n private interceptors: ((res: Response) => Promise<Response> | Response)[];\n private statusCode: number = 200;\n private responseBody: any = {};\n private responseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json'\n };\n\n /**\n * Creates a new ServerResponse instance\n * @param interceptors Array of interceptor functions that can modify the response\n */\n constructor(interceptors: ((res: Response) => Promise<Response> | Response)[] = []) {\n this.interceptors = interceptors;\n }\n\n /**\n * Sets the status code for the response\n * @param code HTTP status code\n * @returns this instance for chaining\n */\n status(code: number): ServerResponse {\n this.statusCode = code;\n return this;\n }\n\n /**\n * Sets the response body and returns this instance (chainable method)\n * \n * @param body Response body\n * @returns this instance for chaining\n */\n body(body: any): ServerResponse {\n this.responseBody = body;\n return this;\n }\n\n /**\n * Adds a header to the response\n * @param name Header name\n * @param value Header value\n * @returns this instance for chaining\n */\n header(name: string, value: string): ServerResponse {\n this.responseHeaders[name] = value;\n return this;\n }\n\n /**\n * Adds multiple headers to the response\n * @param headers Object containing headers\n * @returns this instance for chaining\n */\n setHeaders(headers: Record<string, string>): ServerResponse {\n this.responseHeaders = { ...this.responseHeaders, ...headers };\n return this;\n }\n\n /**\n * Add an interceptor to the chain\n * @param interceptor Function that takes a Response and returns a modified Response\n * @returns this instance for chaining\n */\n use(interceptor: (res: Response) => Promise<Response> | Response): ServerResponse {\n this.interceptors.push(interceptor);\n return this;\n }\n\n /**\n * Builds and returns the Response object after applying all interceptors\n * @returns Promise<Response> The final Response object\n * @private Internal method used by terminal methods\n */\n private async buildResponse(): Promise<Response> {\n // Create initial response\n let response = new Response(JSON.stringify(this.responseBody), {\n status: this.statusCode,\n headers: this.responseHeaders\n });\n\n // Apply all interceptors sequentially\n for (const interceptor of this.interceptors) {\n try {\n const interceptedResponse = interceptor(response);\n if (interceptedResponse instanceof Promise) {\n response = await interceptedResponse;\n } else {\n response = interceptedResponse;\n }\n } catch (error) {\n console.error('Error in interceptor:', error);\n // Continue with the current response if an interceptor fails\n }\n }\n\n return response;\n }\n\n /**\n * Sets the response body to the JSON-stringified version of the provided value\n * and sends the response (terminal method)\n * \n * @param body Response body to be JSON stringified\n * @returns Promise<Response> The final Response object\n */\n async json(body: any): Promise<Response> {\n this.responseBody = body;\n this.responseHeaders['Content-Type'] = 'application/json';\n return this.buildResponse();\n }\n\n /**\n * Sends the response with the current configuration (terminal method)\n * \n * @param body Optional body to set before sending\n * @returns Promise<Response> The final Response object\n */\n async send(body?: any): Promise<Response> {\n if (body !== undefined) {\n this.responseBody = body;\n }\n return this.buildResponse();\n }\n\n /**\n * Sends a plain text response (terminal method)\n * \n * @param text Text to send\n * @returns Promise<Response> The final Response object\n */\n async text(text: string): Promise<Response> {\n this.responseBody = text;\n this.responseHeaders['Content-Type'] = 'text/plain';\n \n // Create a text response without JSON stringifying the body\n let response = new Response(text, {\n status: this.statusCode,\n headers: this.responseHeaders\n });\n \n // Apply interceptors\n for (const interceptor of this.interceptors) {\n try {\n const interceptedResponse = interceptor(response);\n if (interceptedResponse instanceof Promise) {\n response = await interceptedResponse;\n } else {\n response = interceptedResponse;\n }\n } catch (error) {\n console.error('Error in interceptor:', error);\n }\n }\n \n return response;\n }\n\n /**\n * Redirects to the specified URL (terminal method)\n * \n * @param url URL to redirect to\n * @param statusCode HTTP status code (default: 302)\n * @returns Promise<Response> The final Response object\n */\n async redirect(url: string, statusCode: number = 302): Promise<Response> {\n this.statusCode = statusCode;\n this.responseHeaders['Location'] = url;\n return this.buildResponse();\n }\n\n /**\n * Creates a success response with status 200\n * @param body Response body\n * @returns Promise<Response> The final Response object\n */\n async success(body: any = {}): Promise<Response> {\n return this.status(200).json(body);\n }\n\n /**\n * Creates an error response with status 400\n * @param message Error message\n * @param details Additional error details\n * @returns Promise<Response> The final Response object\n */\n async badRequest(message: string, details: any = {}): Promise<Response> {\n return this.status(400).json({\n error: message,\n ...details\n });\n }\n\n /**\n * Creates an error response with status 403\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async notPermitted(message: string = \"Not permitted\"): Promise<Response> {\n return this.status(403).json({ error: message });\n }\n\n /**\n * Creates an error response with status 401\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async unauthorized(message: string = \"Unauthorized\"): Promise<Response> {\n return this.status(401).json({ error: message });\n }\n\n /**\n * Creates an error response with status 404\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async notFound(message: string = \"Not found\"): Promise<Response> {\n return this.status(404).json({ error: message });\n }\n\n /**\n * Creates an error response with status 500\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async serverError(message: string = \"Internal Server Error\"): Promise<Response> {\n return this.status(500).json({ error: message });\n }\n}","export function cors(res: Response, options: CorsOptions = {}) {\n const newHeaders = new Headers(res.headers);\n \n // Set default CORS headers\n const requestOrigin = options.origin || '*';\n newHeaders.set('Access-Control-Allow-Origin', requestOrigin);\n \n if (options.credentials) {\n newHeaders.set('Access-Control-Allow-Credentials', 'true');\n }\n \n if (options.exposedHeaders && options.exposedHeaders.length) {\n newHeaders.set('Access-Control-Expose-Headers', options.exposedHeaders.join(', '));\n }\n \n // Handle preflight requests\n if (options.methods && options.methods.length) {\n newHeaders.set('Access-Control-Allow-Methods', options.methods.join(', '));\n } else {\n newHeaders.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');\n }\n \n if (options.allowedHeaders && options.allowedHeaders.length) {\n newHeaders.set('Access-Control-Allow-Headers', options.allowedHeaders.join(', '));\n } else {\n newHeaders.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');\n }\n \n if (options.maxAge) {\n newHeaders.set('Access-Control-Max-Age', options.maxAge.toString());\n } else {\n // Default max-age to 86400 seconds (24 hours)\n newHeaders.set('Access-Control-Max-Age', '86400');\n }\n \n return new Response(res.body, {\n status: res.status,\n headers: newHeaders\n });\n}\n\nexport interface CorsOptions {\n origin?: string;\n methods?: string[];\n allowedHeaders?: string[];\n exposedHeaders?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\n/**\n * Creates a CORS interceptor with the specified options\n * @param options CORS configuration options\n * @returns An interceptor function that can be used with ServerResponse\n */\nexport function createCorsInterceptor(options: CorsOptions = {}): (res: Response) => Response {\n return (res: Response) => cors(res, options);\n}","import type * as Party from \"./types/party\";\nimport { response } from \"./utils\";\n\ninterface PartyWebSocket {\n send: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void;\n addEventListener: (type: string, listener: (event: any) => void) => void;\n close: () => void;\n}\n\nexport interface ShardOptions {\n worldUrl?: string;\n worldId?: string;\n statsInterval?: number;\n}\n\nexport class Shard {\n ws: PartyWebSocket;\n connectionMap = new Map<string, Party.Connection>(); // Map privateId -> connection\n mainServerStub: any;\n worldUrl: string | null = null;\n worldId: string = 'default';\n lastReportedConnections: number = 0;\n statsInterval: number = 30000; \n statsIntervalId: any = null;\n\n constructor(private room: Party.Room) {}\n\n async onStart() {\n const roomId = this.room.id.split(':')[0];\n const roomStub = this.room.context.parties.main.get(roomId);\n if (!roomStub) {\n console.warn('No room room stub found in main party context');\n return;\n }\n \n this.mainServerStub = roomStub;\n this.ws = await roomStub.socket({\n headers: {\n 'x-shard-id': this.room.id\n }\n }) as unknown as PartyWebSocket;\n \n // Handle messages from the main server\n this.ws.addEventListener(\"message\", (event) => {\n try {\n const message = JSON.parse(event.data);\n\n // If the message is directed to a specific client, forward it\n if (message.targetClientId) {\n const clientConn = this.connectionMap.get(message.targetClientId);\n if (clientConn) {\n // Remove the routing information before forwarding\n delete message.targetClientId;\n clientConn.send(message.data);\n }\n } else {\n // Broadcast to all clients if no specific target\n this.room.broadcast(event.data);\n }\n } catch (error) {\n console.error(\"Error processing message from main server:\", error);\n }\n });\n\n await this.updateWorldStats();\n this.startPeriodicStatsUpdates();\n }\n\n private startPeriodicStatsUpdates() {\n if (!this.worldUrl) {\n return;\n }\n\n if (this.statsIntervalId) {\n clearInterval(this.statsIntervalId);\n }\n\n this.statsIntervalId = setInterval(() => {\n this.updateWorldStats().catch(error => {\n console.error('Error in periodic stats update:', error);\n });\n }, this.statsInterval);\n }\n\n private stopPeriodicStatsUpdates() {\n if (this.statsIntervalId) {\n clearInterval(this.statsIntervalId);\n this.statsIntervalId = null;\n }\n }\n\n onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {\n // Store connection mapping\n this.connectionMap.set(conn.id, conn);\n \n // Capture all headers and request information\n const headers: Record<string, string> = {};\n if (ctx.request?.headers) {\n ctx.request.headers.forEach((value, key) => {\n headers[key] = value;\n });\n }\n\n // Prepare connection context information\n const requestInfo = ctx.request ? {\n headers,\n url: ctx.request.url,\n method: ctx.request.method\n } : null;\n\n // Notify the main server about the new connection with complete connection metadata\n this.ws.send(JSON.stringify({\n type: 'shard.clientConnected',\n privateId: conn.id,\n requestInfo\n }));\n\n this.updateWorldStats();\n }\n\n onMessage(message: string | ArrayBuffer | ArrayBufferView, sender: Party.Connection) {\n try {\n // Parse the message if it's a string\n const parsedMessage = typeof message === 'string' ? JSON.parse(message) : message;\n \n // Wrap the original message with sender information\n const wrappedMessage = JSON.stringify({\n type: 'shard.clientMessage',\n privateId: sender.id,\n publicId: (sender.state as any)?.publicId,\n payload: parsedMessage\n });\n \n // Forward to main server\n this.ws.send(wrappedMessage);\n } catch (error) {\n console.error(\"Error forwarding message to main server:\", error);\n }\n }\n\n onClose(conn: Party.Connection) {\n // Remove connection from the map\n this.connectionMap.delete(conn.id);\n \n // Notify main server about disconnection\n this.ws.send(JSON.stringify({\n type: 'shard.clientDisconnected',\n privateId: conn.id,\n publicId: (conn.state as any)?.publicId\n }));\n\n this.updateWorldStats();\n }\n\n async updateWorldStats(): Promise<boolean> {\n const currentConnections = this.connectionMap.size;\n \n if (currentConnections === this.lastReportedConnections) {\n return true;\n }\n\n try {\n const worldRoom = this.room.context.parties.world.get('world-default');\n const response = await worldRoom.fetch('/update-shard', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-access-shard': this.room.env.SHARD_SECRET as string\n },\n body: JSON.stringify({\n shardId: this.room.id,\n connections: currentConnections\n })\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));\n console.error(`Failed to update World stats: ${response.status} - ${errorData.error || 'Unknown error'}`);\n return false;\n }\n\n // Mettre à jour le dernier nombre rapporté\n this.lastReportedConnections = currentConnections;\n return true;\n } catch (error) {\n console.error('Error updating World stats:', error);\n return false;\n }\n }\n\n /**\n * @method onRequest\n * @async\n * @param {Party.Request} req - The HTTP request to handle\n * @description Forwards HTTP requests to the main server, preserving client context\n * @returns {Promise<Response>} The response from the main server\n */\n async onRequest(req: Party.Request): Promise<Response> {\n if (!this.mainServerStub) {\n return response(503, { error: 'Shard not connected to main server' });\n }\n\n try {\n // Extract necessary information from the request\n const url = new URL(req.url);\n const path = url.pathname;\n const method = req.method;\n let body: string | null = null;\n \n if (method !== 'GET' && method !== 'HEAD') {\n body = await req.text();\n }\n \n // Convert original headers to a new Headers object\n const headers = new Headers();\n req.headers.forEach((value, key) => {\n headers.set(key, value);\n });\n \n // Add shard identification\n headers.set('x-shard-id', this.room.id);\n headers.set('x-forwarded-by-shard', 'true');\n \n // Client IP tracking for the main server\n const clientIp = req.headers.get('x-forwarded-for') || 'unknown';\n if (clientIp) {\n headers.set('x-original-client-ip', clientIp);\n }\n \n // Prepare request options\n const requestInit: RequestInit = {\n method,\n headers,\n body\n };\n // Forward the request to the main server\n const response = await this.mainServerStub.fetch(path, requestInit);\n return response;\n } catch (error) {\n return response(500, { error: 'Error forwarding request' });\n }\n }\n \n /**\n * @method onAlarm\n * @async\n * @description Executed periodically, used to perform maintenance tasks\n */\n async onAlarm() {\n await this.updateWorldStats();\n }\n}\n\nShard satisfies Party.Worker;","import { ServerIo } from \"./mock\"\nimport { Server } from \"./server\"\nimport { Shard } from \"./shard\"\n\n/**\n * @description Test the room with a mock server and client\n * @param Room - The room class to test\n * @param options - The options for the room\n * @param options.hibernate - Whether to hibernate the server. If hybernate, room is null\n * @example\n * ```ts\n * const { createClient, room, server } = await testRoom(GameRoom)\n * const client1 = await createClient()\n * const client2 = await createClient()\n * \n * client1.addEventListener('message', (data) => {\n * console.log(data)\n * })\n * client2.addEventListener('message', (data) => {\n * console.log(data)\n * })\n * \n * await client1.send({\n * action: 'increment'\n * })\n * \n * ```\n * @returns The server, room, and createClient function\n */\nexport async function testRoom(Room, options: {\n hibernate?: boolean,\n shard?: boolean,\n env?: Record<string, string>,\n parties?: Record<string, (io: any) => any>,\n partyFn?: (io: any) => any\n} = {}) {\n\n const createServer = (io: any) => {\n const server = new Server(io)\n server.rooms = [Room]\n return server\n }\n\n const isShard = options.shard || false\n const io = new ServerIo(Room.path, isShard ? {\n parties: {\n game: createServer,\n ...(options.parties || {})\n },\n partyFn: options.partyFn,\n env: options.env\n } : {\n parties: options.parties,\n partyFn: options.partyFn,\n env: options.env\n })\n Room.prototype.throttleSync = 0\n Room.prototype.throttleStorage = 0\n Room.prototype.options = options\n \n let server: Server | Shard;\n if (options.shard) {\n const shardServer = new Shard(io as any);\n // Add subRoom property to Shard for compatibility with Server\n (shardServer as any).subRoom = null;\n server = shardServer;\n // In shard mode, parties.main is a Map of lobbies; ensure their servers are started\n if (io.context.parties.main instanceof Map) {\n for (const lobby of io.context.parties.main.values()) {\n await lobby.server.onStart();\n }\n }\n } else {\n server = await createServer(io as any);\n // If extra parties are provided in non-shard mode, start them too\n if (io.context.parties.main instanceof Map) {\n for (const lobby of io.context.parties.main.values()) {\n if (lobby.server && lobby.server !== server) {\n await lobby.server.onStart();\n }\n }\n }\n }\n \n await server.onStart()\n \n return {\n server,\n room: (server as any).subRoom,\n createClient: async (id?: string, opts?: { query?: Record<string, string>, headers?: Record<string, string> }) => {\n const client = await io.connection(server as Server, id, opts)\n return client\n }\n }\n}\n\nexport async function request(room: Server | Shard, path: string, options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n body?: any,\n headers?: Record<string, string>\n} = {\n method: 'GET',\n}) {\n const url = new URL('http://localhost' + path)\n const request = new Request(url.toString(), options)\n const response = await room.onRequest(request as any)\n return response\n}","import { generateShortUUID } from \"../../sync/src/utils\";\nimport { Server } from \"./server\";\nimport { Storage } from \"./storage\";\nimport { request } from \"./testing\";\n\nexport class MockPartyClient {\n private events: Map<string, Function[]> = new Map();\n id : string\n conn: MockConnection;\n\n constructor(public server: Server, id?: string) {\n this.id = id || generateShortUUID()\n this.conn = new MockConnection(this)\n }\n \n addEventListener(event, cb) {\n if (!this.events.has(event)) {\n this.events.set(event, []);\n }\n this.events.get(event).push(cb);\n }\n\n removeEventListener(event, cb) {\n if (!this.events.has(event)) return;\n const callbacks = this.events.get(event);\n const index = callbacks.indexOf(cb);\n if (index !== -1) {\n callbacks.splice(index, 1);\n }\n if (callbacks.length === 0) {\n this.events.delete(event);\n }\n }\n\n _trigger(event, data) {\n const callbacks = this.events.get(event);\n if (callbacks) {\n for (const cb of callbacks) {\n cb(data);\n }\n }\n }\n\n send(data) {\n return this.server.onMessage(JSON.stringify(data), this.conn as any)\n }\n}\n\nclass MockLobby {\n constructor(public server: Server, public lobbyId: string) {}\n\n socket(_init?: any) {\n return new MockPartyClient(this.server)\n }\n\n async connection(idOrOptions?: string | { id?: string, query?: Record<string, string>, headers?: Record<string, string> }, maybeOptions?: { query?: Record<string, string>, headers?: Record<string, string> }) {\n const id = typeof idOrOptions === 'string' ? idOrOptions : idOrOptions?.id;\n const options = (typeof idOrOptions === 'string' ? maybeOptions : idOrOptions) || {};\n return (this.server.room as any).connection(this.server, id, options as any);\n }\n\n fetch(url: string, options: any) {\n const baseUrl = url.includes('shard') ? '' :( '/parties/main/' + this.lobbyId )\n return request(this.server, baseUrl + url, options)\n }\n}\n\ninterface MockContextOptions {\n parties?: any;\n partyFn?: (roomId: string) => any;\n}\n\nclass MockContext {\n parties: {\n main: any\n } = {\n main: new Map()\n }\n\n constructor(public room: MockPartyRoom, options: MockContextOptions = {}) {\n const parties = options.parties || {}\n if (options.partyFn) {\n const serverCache = new Map<string, MockLobby>();\n this.parties.main = {\n get: async (lobbyId: string) => {\n if (!serverCache.has(lobbyId)) {\n const server = await options.partyFn(lobbyId);\n serverCache.set(lobbyId, new MockLobby(server, lobbyId));\n }\n return serverCache.get(lobbyId)!;\n }\n }\n }\n else {\n for (let lobbyId in parties) {\n const server = parties[lobbyId](room)\n ;(this.parties.main as Map<string, any>).set(lobbyId, new MockLobby(server, lobbyId))\n }\n }\n }\n}\n\nclass MockPartyRoom {\n clients: Map<string, MockPartyClient> = new Map();\n storage = new Storage();\n context: MockContext;\n env = {}\n\n constructor(public id?: string, options: any = {}) {\n this.id = id || generateShortUUID()\n this.context = new MockContext(this, {\n parties: options.parties,\n partyFn: options.partyFn\n })\n this.env = options.env || {}\n }\n\n async connection(server: Server, id?: string, opts?: { query?: Record<string, string>, headers?: Record<string, string> }) {\n const socket = new MockPartyClient(server, id);\n const url = new URL('http://localhost')\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n url.searchParams.set(key, String(value))\n }\n }\n const request = new Request(url.toString(), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...(opts?.headers || {})\n }\n })\n await server.onConnect(socket.conn as any, { request } as any);\n this.clients.set(socket.id, socket);\n return socket\n }\n\n broadcast(data: any) {\n this.clients.forEach((client) => {\n client._trigger('message', data);\n });\n }\n\n getConnection(id: string) {\n return this.clients.get(id)\n }\n\n getConnections() {\n return Array.from(this.clients.values()).map((client) => client.conn); \n }\n\n clear() {\n this.clients.clear();\n }\n}\n\nexport class MockConnection {\n server: Server;\n id: string;\n\n constructor(public client: MockPartyClient) {\n this.server = client.server\n this.id = client.id\n }\n\n state: any = {};\n\n setState(value: any) {\n this.state = value;\n }\n\n send(data: any) {\n this.client._trigger('message', data)\n }\n\n close() {\n this.server.onClose(this as any)\n }\n}\n\nexport const ServerIo = MockPartyRoom;\nexport const ClientIo = MockPartyClient;\n","import { signal } from \"@signe/reactive\";\nimport { Room, Action, Guard, Request } from \"./decorators\";\nimport { sync, id, persist } from \"@signe/sync\";\nimport { z } from \"zod\";\nimport * as Party from \"./types/party\";\nimport { guardManageWorld } from \"./world.guard\";\nimport { response } from \"./utils\";\nimport { RoomInterceptorPacket, RoomOnJoin } from \"./interfaces\";\nimport { ServerResponse } from \"./request/response\";\n\n// Types definitions\ntype BalancingStrategy = 'round-robin' | 'least-connections' | 'random';\ntype ShardStatus = 'active' | 'maintenance' | 'draining';\n\nconst MAX_PLAYERS_PER_SHARD = 75;\n\n// Schema validations\nconst RoomConfigSchema = z.object({\n name: z.string(),\n balancingStrategy: z.enum(['round-robin', 'least-connections', 'random']),\n public: z.boolean(),\n maxPlayersPerShard: z.number().int().positive(),\n minShards: z.number().int().min(0),\n maxShards: z.number().int().positive().optional(),\n});\n\nconst RegisterShardSchema = z.object({\n shardId: z.string(),\n roomId: z.string(),\n url: z.string().url(),\n maxConnections: z.number().int().positive(),\n});\n\nconst UpdateShardStatsSchema = z.object({\n connections: z.number().int().min(0),\n status: z.enum(['active', 'maintenance', 'draining']).optional(),\n});\n\nconst ScaleRoomSchema = z.object({\n roomId: z.string(),\n targetShardCount: z.number().int().positive(),\n shardTemplate: z.object({\n urlTemplate: z.string(),\n maxConnections: z.number().int().positive(),\n }).optional(),\n});\n\n// Model classes\nclass RoomConfig {\n @id() id: string;\n @sync() name = signal(\"\");\n @sync() balancingStrategy = signal<BalancingStrategy>(\"round-robin\");\n @sync() public = signal(true);\n @sync() maxPlayersPerShard = signal(MAX_PLAYERS_PER_SHARD);\n @sync() minShards = signal(1);\n @sync() maxShards = signal<number | undefined>(undefined);\n}\n\nclass ShardInfo {\n @id() id: string;\n @sync() roomId = signal(\"\");\n @sync() url = signal(\"\");\n @sync({\n persist: false\n }) currentConnections = signal(0);\n @sync() maxConnections = signal(MAX_PLAYERS_PER_SHARD);\n @sync() status = signal<ShardStatus>(\"active\");\n @sync() lastHeartbeat = signal(0);\n}\n\n// World room implementation\n@Room({\n path: \"world-{worldId}\",\n maxUsers: 100, // Limit for admin connections\n throttleStorage: 2000, // Throttle storage updates (ms)\n throttleSync: 500, // Throttle sync updates (ms)\n})\nexport class WorldRoom implements RoomInterceptorPacket, RoomOnJoin {\n // Synchronized state\n @sync(RoomConfig) rooms = signal<Record<string, RoomConfig>>({});\n @sync(ShardInfo) shards = signal<Record<string, ShardInfo>>({});\n \n // Only persisted state (not synced to clients)\n @persist() rrCounters = signal<Record<string, number>>({});\n \n // Configuration\n defaultShardUrlTemplate = signal(\"{shardId}\");\n defaultMaxConnectionsPerShard = signal(MAX_PLAYERS_PER_SHARD);\n\n constructor(private room: Party.Room) {\n const { AUTH_JWT_SECRET, SHARD_SECRET } = this.room.env;\n if (!AUTH_JWT_SECRET) {\n throw new Error(\"AUTH_JWT_SECRET env variable is not set\");\n }\n if (!SHARD_SECRET) {\n throw new Error(\"SHARD_SECRET env variable is not set\");\n }\n // TODO\n //setTimeout(() => this.cleanupInactiveShards(), 60000);\n }\n\n async onJoin(user: any, conn: Party.Connection, ctx: Party.ConnectionContext) {\n const canConnect = await guardManageWorld(user, ctx.request, this.room);\n conn.setState({\n ...conn.state,\n isAdmin: canConnect\n })\n }\n\n interceptorPacket(_, obj: any, conn: Party.Connection) {\n if (!conn.state['isAdmin']) {\n return null;\n }\n return obj;\n }\n \n // Helper methods\n private cleanupInactiveShards() {\n const now = Date.now();\n const timeout = 5 * 60 * 1000; // 5 minutes timeout\n const shardsValue = this.shards();\n \n let hasChanges = false;\n Object.values(shardsValue).forEach(shard => {\n if (now - shard.lastHeartbeat() > timeout) {\n delete this.shards()[shard.id];\n \n hasChanges = true;\n }\n });\n \n // Schedule next cleanup\n setTimeout(() => this.cleanupInactiveShards(), 60000);\n }\n \n // Actions\n @Request({\n path: 'register-room',\n method: 'POST',\n })\n @Guard([guardManageWorld])\n async registerRoom(req: Party.Request) {\n const roomConfig: z.infer<typeof RoomConfigSchema> = await req.json();\n const roomId = roomConfig.name;\n \n if (!this.rooms()[roomId]) {\n const newRoom = new RoomConfig();\n newRoom.id = roomId;\n newRoom.name.set(roomConfig.name);\n newRoom.balancingStrategy.set(roomConfig.balancingStrategy);\n newRoom.public.set(roomConfig.public);\n newRoom.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);\n newRoom.minShards.set(roomConfig.minShards);\n newRoom.maxShards.set(roomConfig.maxShards);\n \n this.rooms()[roomId] = newRoom;\n \n // Ensure minimum shards are created\n if (roomConfig.minShards > 0) {\n for (let i = 0; i < roomConfig.minShards; i++) {\n await this.createShard(roomId);\n }\n }\n } else {\n // Update existing room\n const room = this.rooms()[roomId];\n room.balancingStrategy.set(roomConfig.balancingStrategy);\n room.public.set(roomConfig.public);\n room.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);\n room.minShards.set(roomConfig.minShards);\n room.maxShards.set(roomConfig.maxShards);\n }\n }\n \n @Request({\n path: 'update-shard',\n method: 'POST',\n })\n @Guard([guardManageWorld])\n async updateShardStats(req: Party.Request, res: ServerResponse) {\n const body: { shardId: string; connections: number; status: ShardStatus } = await req.json();\n const { shardId, connections, status } = body;\n const shard = this.shards()[shardId];\n\n if (!shard) {\n return res.notFound(`Shard ${shardId} not found`);\n }\n \n shard.currentConnections.set(connections);\n if (status) {\n shard.status.set(status);\n }\n shard.lastHeartbeat.set(Date.now());\n }\n \n @Request({\n path: 'scale-room',\n method: 'POST',\n })\n @Guard([guardManageWorld])\n async scaleRoom(req: Party.Request, res: ServerResponse) {\n const data: z.infer<typeof ScaleRoomSchema> = await req.json();\n const { targetShardCount, shardTemplate, roomId } = data;\n \n // Validate room exists\n const room = this.rooms()[roomId];\n if (!room) {\n return res.notFound(`Room ${roomId} does not exist`);\n }\n \n const roomShards = Object.values(this.shards())\n .filter(shard => shard.roomId() === roomId);\n \n const previousShardCount = roomShards.length;\n \n // Check max shards constraint\n if (room.maxShards() !== undefined && targetShardCount > room.maxShards()!) {\n return res.badRequest(`Cannot scale beyond maximum allowed shards (${room.maxShards()})`, {\n roomId,\n currentShardCount: previousShardCount\n });\n }\n \n // Handle scaling down\n if (targetShardCount < previousShardCount) {\n // Find candidates for removal (prioritize draining or low-connection shards)\n const shardsToRemove = [...roomShards]\n .sort((a, b) => {\n // Prioritize draining status\n if (a.status() === 'draining' && b.status() !== 'draining') return -1;\n if (a.status() !== 'draining' && b.status() === 'draining') return 1;\n \n // Then by connection count (ascending)\n return a.currentConnections() - b.currentConnections();\n })\n .slice(0, previousShardCount - targetShardCount);\n \n // Remove the selected shards\n const shardsToKeep = roomShards.filter(\n shard => !shardsToRemove.some(s => s.id === shard.id)\n );\n \n // Update shards\n for (const shard of shardsToRemove) {\n delete this.shards()[shard.id];\n }\n \n return;\n }\n \n // Handle scaling up\n if (targetShardCount > previousShardCount) {\n const newShards = [];\n \n // Create new shards\n for (let i = 0; i < targetShardCount - previousShardCount; i++) {\n const newShard = await this.createShard(\n roomId,\n shardTemplate?.urlTemplate,\n shardTemplate?.maxConnections\n );\n \n if (newShard) {\n newShards.push(newShard);\n }\n }\n }\n }\n\n @Request({\n path: 'connect',\n method: 'POST',\n })\n async connect(req: Party.Request, res: ServerResponse) {\n try {\n // Extract request data\n let data: { roomId: string; autoCreate?: boolean };\n \n try {\n // Handle potential empty body or malformed JSON\n const body = await req.text();\n if (!body || body.trim() === '') {\n return res.badRequest(\"Request body is empty\");\n }\n \n data = JSON.parse(body);\n } catch (parseError) {\n return res.badRequest(\"Invalid JSON in request body\");\n }\n \n // Verify roomId is provided\n if (!data.roomId) {\n return res.badRequest(\"roomId parameter is required\");\n }\n \n // Determine if auto-creation is enabled (default to true)\n const autoCreate = data.autoCreate !== undefined ? data.autoCreate : true;\n \n // Find optimal shard\n const result = await this.findOptimalShard(data.roomId, autoCreate);\n \n // Check for errors\n if ('error' in result) {\n return res.notFound(result.error);\n }\n \n // Return shard information to the client\n return res.success({\n success: true,\n shardId: result.shardId,\n url: result.url\n });\n } catch (error) {\n console.error('Error connecting to shard:', error);\n return res.serverError();\n }\n }\n \n private async findOptimalShard(\n roomId: string, \n autoCreate: boolean = true\n ): Promise<{ shardId: string; url: string } | { error: string }> {\n // Ensure room exists\n let room = this.rooms()[roomId];\n if (!room) {\n if (autoCreate) {\n const mockRequest = {\n json: async () => ({\n name: roomId,\n balancingStrategy: 'round-robin',\n public: true,\n maxPlayersPerShard: this.defaultMaxConnectionsPerShard(),\n minShards: 1,\n maxShards: undefined\n })\n } as Party.Request;\n \n await this.registerRoom(mockRequest);\n \n room = this.rooms()[roomId];\n \n if (!room) {\n return { error: `Failed to create room ${roomId}` };\n }\n } else {\n return { error: `Room ${roomId} does not exist` };\n }\n }\n \n // Get shards for this room\n const roomShards = Object.values(this.shards())\n .filter(shard => shard.roomId() === roomId);\n \n if (roomShards.length === 0) {\n if (autoCreate) {\n // Auto-create a shard\n const newShard = await this.createShard(roomId);\n if (newShard) {\n return {\n shardId: newShard.id,\n url: newShard.url()\n };\n } else {\n return { error: `Failed to create shard for room ${roomId}` };\n }\n } else {\n return { error: `No shards available for room ${roomId}` };\n }\n }\n \n // Get active shards\n const activeShards = roomShards\n .filter(shard => shard && shard.status() === 'active');\n \n if (activeShards.length === 0) {\n return { error: `No active shards available for room ${roomId}` };\n }\n \n // Apply balancing strategy\n const balancingStrategy = room.balancingStrategy();\n let selectedShard: ShardInfo;\n \n switch (balancingStrategy) {\n case 'least-connections':\n // Choose shard with fewest connections\n selectedShard = activeShards.reduce(\n (min, shard) => \n shard.currentConnections() < min.currentConnections() ? shard : min,\n activeShards[0]\n );\n break;\n \n case 'random':\n // Choose random shard\n selectedShard = activeShards[Math.floor(Math.random() * activeShards.length)];\n break;\n \n case 'round-robin':\n default:\n // Round-robin selection\n const counter = this.rrCounters()[roomId] || 0;\n const nextCounter = (counter + 1) % activeShards.length;\n this.rrCounters()[roomId] = nextCounter;\n \n selectedShard = activeShards[counter];\n break;\n }\n \n return {\n shardId: selectedShard.id,\n url: selectedShard.url()\n };\n }\n \n // Private methods\n private async createShard(\n roomId: string,\n urlTemplate?: string,\n maxConnections?: number\n ): Promise<ShardInfo | null> {\n const room = this.rooms()[roomId];\n if (!room) {\n console.error(`Cannot create shard for non-existent room: ${roomId}`);\n return null;\n }\n \n // Generate shard ID\n const shardId = `${roomId}:${Date.now()}-${Math.floor(Math.random() * 10000)}`;\n \n // Generate URL from template\n const template = urlTemplate || this.defaultShardUrlTemplate();\n const url = template.replace('{shardId}', shardId).replace('{roomId}', roomId);\n\n // Set max connections\n const max = maxConnections || room.maxPlayersPerShard();\n \n // Create the shard\n const newShard = new ShardInfo();\n newShard.id = shardId;\n newShard.roomId.set(roomId);\n newShard.url.set(url);\n newShard.maxConnections.set(max);\n newShard.currentConnections.set(0);\n newShard.status.set(\"active\");\n newShard.lastHeartbeat.set(Date.now());\n \n // Update shards collection\n this.shards()[shardId] = newShard;\n return newShard;\n }\n}","import type {\n AnalyticsEngineDataset,\n ExecutionContext as CFExecutionContext,\n Request as CFRequest,\n DurableObjectState,\n DurableObjectStorage,\n KVNamespace,\n R2Bucket,\n ScheduledController,\n VectorizeIndex,\n WebSocket\n} from \"@cloudflare/workers-types\";\n \n export type StaticAssetsManifestType = {\n devServer: string;\n browserTTL: number | null | undefined;\n edgeTTL: number | null | undefined;\n singlePageApp: boolean | undefined;\n assets: Record<string, string>;\n assetInfo?: Record<\n string,\n {\n fileSize: number;\n fileHash: string;\n fileName: string;\n }\n >;\n };\n \n type AssetFetcher = {\n fetch(path: string): Promise<Response | null>;\n };\n \n type StandardRequest = globalThis.Request;\n \n // Types with PartyKit* prefix are used in module workers, i.e.\n // `export default {} satisfies PartyKitServer;`\n \n // Types with Party.* prefix are used in class workers, i.e.\n // `export default class Room implements Party.Server {}`\n \n // Extend type so that when language server (e.g. vscode) autocompletes,\n // it will import this type instead of the underlying type directly.\n export interface Request extends CFRequest {}\n \n // Because when you construct a `new Request()` in a user script,\n // it's assumed to be a standards-based Fetch API Response, unless overridden.\n // This is fine by us, let user return whichever request type\n type ReturnRequest = StandardRequest | CFRequest;\n \n /** Per-party key-value storage */\n export interface Storage extends DurableObjectStorage {}\n \n /** Connection metadata only available when the connection is made */\n export type ConnectionContext = { request: CFRequest };\n \n export type Stub = {\n /** @deprecated Use `await socket()` instead */\n connect: () => WebSocket;\n socket(pathOrInit?: string | RequestInit): Promise<WebSocket>;\n socket(path: string, init?: RequestInit): Promise<WebSocket>;\n fetch(pathOrInit?: string | RequestInit | ReturnRequest): Promise<Response>;\n fetch(path: string, init?: RequestInit | ReturnRequest): Promise<Response>;\n };\n \n /** Additional information about other resources in the current project */\n export type Context = {\n /** Access other parties in this project */\n parties: Record<\n string,\n {\n get(id: string): Stub;\n }\n >;\n /**\n * A binding to the Cloudflare AI service.\n */\n ai: AI;\n /**\n * A binding to the Cloudflare Vectorize service.\n */\n vectorize: Record<string, VectorizeIndex>;\n \n /**\n * A binding to fetch static assets\n */\n assets: AssetFetcher;\n \n /**\n * Custom bindings\n */\n bindings: CustomBindings;\n };\n \n export type AI = Record<string, never>;\n \n export type FetchLobby = {\n env: Record<string, unknown>;\n ai: AI;\n parties: Context[\"parties\"];\n vectorize: Context[\"vectorize\"];\n analytics: AnalyticsEngineDataset;\n assets: AssetFetcher;\n bindings: CustomBindings;\n };\n \n export type CronLobby = {\n env: Record<string, unknown>;\n ai: AI;\n parties: Context[\"parties\"];\n vectorize: Context[\"vectorize\"];\n analytics: AnalyticsEngineDataset;\n assets: AssetFetcher;\n bindings: CustomBindings;\n };\n \n export type Lobby = {\n id: string;\n env: Record<string, unknown>;\n ai: AI;\n parties: Context[\"parties\"];\n vectorize: Context[\"vectorize\"];\n analytics: AnalyticsEngineDataset;\n assets: AssetFetcher;\n bindings: CustomBindings;\n };\n \n export type ExecutionContext = CFExecutionContext;\n \n // https://stackoverflow.com/a/58993872\n type ImmutablePrimitive = undefined | null | boolean | string | number;\n type Immutable<T> = T extends ImmutablePrimitive\n ? T\n : T extends Array<infer U>\n ? ImmutableArray<U>\n : T extends Map<infer K, infer V>\n ? ImmutableMap<K, V>\n : T extends Set<infer M>\n ? ImmutableSet<M>\n : ImmutableObject<T>;\n type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;\n type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;\n type ImmutableSet<T> = ReadonlySet<Immutable<T>>;\n type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };\n \n export type ConnectionState<T> = ImmutableObject<T> | null;\n export type ConnectionSetStateFn<T> = (prevState: ConnectionState<T>) => T;\n \n /** A WebSocket connected to the Room */\n export type Connection<TState = unknown> = WebSocket & {\n /** Connection identifier */\n id: string;\n \n /** @deprecated You can access the socket properties directly on the connection*/\n socket: WebSocket;\n // We would have been able to use Websocket::url\n // but it's not available in the Workers runtime\n // (rather, url is `null` when using WebSocketPair)\n // It's also set as readonly, so we can't set it ourselves.\n // Instead, we'll use the `uri` property.\n uri: string;\n \n /**\n * Arbitrary state associated with this connection.\n * Read-only, use Connection.setState to update the state.\n */\n state: ConnectionState<TState>;\n \n setState(\n state: TState | ConnectionSetStateFn<TState> | null\n ): ConnectionState<TState>;\n \n /** @deprecated use Connection.setState instead */\n serializeAttachment<T = unknown>(attachment: T): void;\n \n /** @deprecated use Connection.state instead */\n deserializeAttachment<T = unknown>(): T | null;\n };\n \n type CustomBindings = {\n r2: Record<string, R2Bucket>;\n kv: Record<string, KVNamespace>;\n };\n \n /** Room represents a single, self-contained, long-lived session. */\n export type Room = {\n /** Room ID defined in the Party URL, e.g. /parties/:name/:id */\n id: string;\n \n /** Internal ID assigned by the platform. Use Party.id instead. */\n internalID: string;\n \n /** Party name defined in the Party URL, e.g. /parties/:name/:id */\n name: string;\n \n /** Environment variables (--var, partykit.json#vars, or .env) */\n env: Record<string, unknown>;\n \n /** A per-room key-value storage */\n storage: Storage;\n \n /** `blockConcurrencyWhile()` ensures no requests are delivered until */\n blockConcurrencyWhile: DurableObjectState[\"blockConcurrencyWhile\"];\n \n /** Additional information about other resources in the current project */\n context: Context;\n \n /** @deprecated Use `room.getConnections` instead */\n connections: Map<string, Connection>;\n \n /** @deprecated Use `room.context.parties` instead */\n parties: Context[\"parties\"];\n \n /** Send a message to all connected clients, except connection ids listed `without` */\n broadcast: (\n msg: string | ArrayBuffer | ArrayBufferView,\n without?: string[] | undefined\n ) => void;\n \n /** Get a connection by connection id */\n getConnection<TState = unknown>(id: string): Connection<TState> | undefined;\n \n /**\n * Get all connections. Optionally, you can provide a tag to filter returned connections.\n * Use `Party.Server#getConnectionTags` to tag the connection on connect.\n */\n getConnections<TState = unknown>(tag?: string): Iterable<Connection<TState>>;\n \n /**\n * Cloudflare Analytics Engine dataset. Use this to log custom events and metrics.\n */\n analytics: AnalyticsEngineDataset;\n };\n \n /**\n * Interface defining the lifecycle hooks available in a Room\n */\n export interface RoomHooks<TUser = any> {\n /**\n * Called when a user joins the room\n * @param user The user object that joined\n * @param conn The connection object\n * @param ctx The connection context\n */\n onJoin?(user: TUser, conn: Connection, ctx: ConnectionContext): void | Promise<void>;\n \n /**\n * Called when a user leaves the room\n * @param user The user object that left\n * @param conn The connection object\n */\n onLeave?(user: TUser, conn: Connection): void | Promise<void>;\n }\n \n /** @deprecated Use `Party.Room` instead */\n export type Party = Room;\n \n /* Party.Server defines what happens when someone connects to and sends messages or HTTP requests to your party\n *\n * @example\n * export default class Room implements Party.Server {\n * constructor(readonly room: Party) {}\n * onConnect(connection: Party.Connection) {\n * this.room.broadcast(\"Someone connected with id \" + connection.id);\n * }\n * }\n */\n export type Server = {\n /**\n * You can define an `options` field to customise the Party.Server behaviour.\n */\n readonly options?: ServerOptions;\n \n /**\n * You can tag a connection to filter them in Party#getConnections.\n * Each connection supports up to 9 tags, each tag max length is 256 characters.\n */\n getConnectionTags?(\n connection: Connection,\n context: ConnectionContext\n ): string[] | Promise<string[]>;\n \n /**\n * Called when the server is started, before first `onConnect` or `onRequest`.\n * Useful for loading data from storage.\n *\n * You can use this to load data from storage and perform other asynchronous\n * initialization, such as retrieving data or configuration from other\n * services or databases.\n */\n onStart?(): void | Promise<void>;\n \n /**\n * Called when a new incoming WebSocket connection is opened.\n */\n onConnect?(\n connection: Connection,\n ctx: ConnectionContext\n ): void | Promise<void>;\n \n /**\n * Called when a WebSocket connection receives a message from a client, or another connected party.\n */\n onMessage?(\n message: string | ArrayBuffer | ArrayBufferView,\n sender: Connection\n ): void | Promise<void>;\n \n /**\n * Called when a WebSocket connection is closed by the client.\n */\n onClose?(connection: Connection): void | Promise<void>;\n \n /**\n * Called when a WebSocket connection is closed due to a connection error.\n */\n onError?(connection: Connection, error: Error): void | Promise<void>;\n \n /**\n * Called when a HTTP request is made to the room URL.\n */\n onRequest?(req: Request): Response | Promise<Response>;\n \n /**\n * Called when an alarm is triggered. Use Party.storage.setAlarm to set an alarm.\n *\n * Alarms have access to most Party resources such as storage, but not Party.id\n * and Party.context.parties properties. Attempting to access them will result in a\n * runtime error.\n */\n onAlarm?(): void | Promise<void>;\n };\n \n export type FetchSocket = WebSocket & {\n request: Request;\n };\n \n export type Cron = ScheduledController & {\n name: string;\n };\n \n type ServerConstructor = {\n new (room: Room): Server;\n };\n \n /**\n * Party.Worker allows you to customise the behaviour of the Edge worker that routes\n * connections to your party.\n *\n * The Party.Worker methods can be defined as static methods on the Party.Server constructor.\n * @example\n * export default class Room implements Party.Server {\n * static onBeforeConnect(req: Party.Request) {\n * return new Response(\"Access denied\", { status: 403 })\n * }\n * constructor(readonly room: Room) {}\n * }\n *\n * Room satisfies Party.Worker;\n */\n \n export type Worker = ServerConstructor & {\n /**\n * Runs on any HTTP request that does not match a Party URL or a static asset.\n * Useful for running lightweight HTTP endpoints that don't need access to the Party\n * state.\n **/\n onFetch?(\n req: Request,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ): Response | undefined | null | Promise<Response | null | undefined>;\n \n /**\n * Runs on any WebSocket connection that does not match a Party URL or a static asset.\n * Useful for running lightweight WebSocket endpoints that don't need access to the Party\n * state.\n */\n onSocket?(\n socket: FetchSocket,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ): void | Promise<void>;\n \n /**\n * Runs before any HTTP request is made to the party. You can modify the request\n * before it is forwarded to the party, or return a Response to short-circuit it.\n */\n onBeforeRequest?(\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ): Request | Response | Promise<Request | Response>;\n \n /**\n * Runs before any WebSocket connection is made to the party. You can modify the request\n * before opening a connection, or return a Response to prevent the connection.\n */\n onBeforeConnect?(\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ): Request | Response | Promise<Request | Response>;\n \n /**\n * Runs on a schedule. You can use this to perform periodic tasks, such as\n * sending a heartbeat to a third-party service.\n */\n onCron?(\n controller: Cron,\n lobby: CronLobby,\n ctx: ExecutionContext\n ): Response | Promise<Response>;\n };\n \n /**\n * PartyKitServer is allows you to customise the behaviour of your Party.\n *\n * @note If you're starting a new project, we recommend using the newer\n * Party.Server API instead.\n *\n * @example\n * export default {\n * onConnect(connection, room) {\n * room.broadcast(\"Someone connected with id \" + connection.id);\n * }\n * }\n */\n export type PartyKitServer = {\n /** @deprecated Use `onFetch` instead */\n unstable_onFetch?: (\n req: Request,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ) => Response | null | undefined | Promise<Response | null | undefined>;\n onFetch?: (\n req: Request,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ) => Response | null | undefined | Promise<Response | null | undefined>;\n onSocket?(\n socket: FetchSocket,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ): void | Promise<void>;\n onBeforeRequest?: (\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ) => ReturnRequest | Response | Promise<ReturnRequest | Response>;\n \n onCron?: (\n controller: Cron,\n lobby: CronLobby,\n ctx: ExecutionContext\n ) => void | Promise<void>;\n \n onRequest?: (req: Request, room: Room) => Response | Promise<Response>;\n onAlarm?: (room: Omit<Room, \"id\" | \"parties\">) => void | Promise<void>;\n onConnect?: (\n connection: Connection,\n room: Room,\n ctx: ConnectionContext\n ) => void | Promise<void>;\n onBeforeConnect?: (\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ) => ReturnRequest | Response | Promise<ReturnRequest | Response>;\n \n /**\n * PartyKitServer may opt into being hibernated between WebSocket\n * messages, which enables a single server to handle more connections.\n */\n onMessage?: (\n message: string | ArrayBuffer | ArrayBufferView,\n connection: Connection,\n room: Room\n ) => void | Promise<void>;\n onClose?: (connection: Connection, room: Room) => void | Promise<void>;\n onError?: (\n connection: Connection,\n err: Error,\n room: Room\n ) => void | Promise<void>;\n };\n \n export type ServerOptions = {\n /**\n * Whether the PartyKit platform should remove the server from memory\n * between HTTP requests and WebSocket messages.\n *\n * The default value is `false`.\n */\n hibernate?: boolean;\n };\n \n //\n // ---\n // DEPRECATIONS\n // ---\n //\n \n /** @deprecated Use Party.Request instead */\n export type PartyRequest = Request;\n \n /** @deprecated Use Party.Storage instead */\n export type PartyStorage = Storage;\n \n /** @deprecated Use Party.Storage instead */\n export type PartyKitStorage = Storage;\n \n /** @deprecated Use Party.ConnectionContext instead */\n export type PartyConnectionContext = ConnectionContext;\n \n /** @deprecated Use Party.ConnectionContext instead */\n export type PartyKitContext = ConnectionContext;\n \n /** @deprecated Use Party.Stub instead */\n export type PartyStub = Stub;\n \n /** Additional information about other resources in the current project */\n /** @deprecated Use Party.Context instead */\n export type PartyContext = Context;\n \n /** @deprecated Use Party.FetchLobby instead */\n export type PartyFetchLobby = FetchLobby;\n \n /** @deprecated Use Party.Lobby instead */\n export type PartyLobby = Lobby;\n \n /** @deprecated Use Party.ExecutionContext instead */\n export type PartyExecutionContext = ExecutionContext;\n \n /** @deprecated Use Party.Connection instead */\n export type PartyConnection = Connection;\n \n /** @deprecated Use Party.Server instead */\n export type PartyServer = Server;\n \n /** @deprecated Use Party.Worker instead */\n export type PartyWorker = Worker;\n \n /** @deprecated Use `Room` instead */\n export type PartyKitRoom = Room;\n \n /** @deprecated Use `Party.Connection` instead */\n export type PartyKitConnection = Connection;\n \n /** @deprecated Use `Party.ServerOptions` instead */\n export type PartyServerOptions = ServerOptions;","/**\n * JWT Authentication Class for Cloudflare Workers\n * Uses Web Crypto API for JWT signing and verification\n */\n\ninterface JWTOptions {\n expiresIn?: string | number;\n}\n\ninterface JWTHeader {\n alg: string;\n typ: string;\n}\n\ninterface JWTPayload {\n [key: string]: unknown;\n iat?: number;\n exp?: number;\n}\n\nexport class JWTAuth {\n private secret: string;\n private encoder: TextEncoder;\n private decoder: TextDecoder;\n\n /**\n * Constructor for the JWTAuth class\n * @param {string} secret - The secret key used for signing and verifying tokens\n */\n constructor(secret: string) {\n if (!secret || typeof secret !== 'string') {\n throw new Error('Secret is required and must be a string');\n }\n this.secret = secret;\n this.encoder = new TextEncoder();\n this.decoder = new TextDecoder();\n }\n\n /**\n * Convert the secret to a CryptoKey for HMAC operations\n * @returns {Promise<CryptoKey>} - The CryptoKey for HMAC operations\n */\n private async getSecretKey(): Promise<CryptoKey> {\n const keyData: Uint8Array = this.encoder.encode(this.secret);\n return await crypto.subtle.importKey(\n 'raw', // format\n keyData, // key data\n {\n name: 'HMAC',\n hash: { name: 'SHA-256' },\n },\n false, // extractable\n ['sign', 'verify'] // key usages\n );\n }\n\n /**\n * Base64Url encode a buffer\n * @param {ArrayBuffer} buffer - The buffer to encode\n * @returns {string} - The base64url encoded string\n */\n private base64UrlEncode(buffer: ArrayBuffer): string {\n const base64: string = btoa(String.fromCharCode(...new Uint8Array(buffer)));\n return base64.replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\n }\n\n /**\n * Base64Url decode a string\n * @param {string} base64Url - The base64url encoded string\n * @returns {ArrayBuffer} - The decoded buffer\n */\n private base64UrlDecode(base64Url: string) {\n const padding: string = '='.repeat((4 - (base64Url.length % 4)) % 4);\n const base64: string = base64Url.replace(/-/g, '+').replace(/_/g, '/') + padding;\n const rawData: string = atob(base64);\n const buffer: Uint8Array = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; i++) {\n buffer[i] = rawData.charCodeAt(i);\n }\n\n return buffer.buffer;\n }\n\n /**\n * Sign a payload and create a JWT token\n * @param {JWTPayload} payload - The payload to include in the token\n * @param {JWTOptions} [options={}] - Options for the token\n * @param {string | number} [options.expiresIn='1h'] - Token expiration time\n * @returns {Promise<string>} - The JWT token\n */\n public async sign(payload: JWTPayload, options: JWTOptions = {}): Promise<string> {\n if (!payload || typeof payload !== 'object') {\n throw new Error('Payload must be an object');\n }\n\n // Set default expiration if not provided\n const expiresIn: string | number = options.expiresIn || '1h';\n\n // Calculate expiration time\n let exp: number | undefined;\n if (typeof expiresIn === 'number') {\n exp = Math.floor(Date.now() / 1000) + expiresIn;\n } else if (typeof expiresIn === 'string') {\n const match: RegExpMatchArray | null = expiresIn.match(/^(\\d+)([smhd])$/);\n if (match) {\n const value: number = parseInt(match[1]);\n const unit: string = match[2];\n const seconds: number = {\n 's': value,\n 'm': value * 60,\n 'h': value * 60 * 60,\n 'd': value * 60 * 60 * 24\n }[unit]!;\n exp = Math.floor(Date.now() / 1000) + seconds;\n } else {\n throw new Error('Invalid expiresIn format. Use a number (seconds) or a string like \"1h\", \"30m\", etc.');\n }\n }\n\n // Create full payload with claims\n const fullPayload: JWTPayload = {\n ...payload,\n iat: Math.floor(Date.now() / 1000),\n exp\n };\n\n // Create header\n const header: JWTHeader = {\n alg: 'HS256',\n typ: 'JWT'\n };\n\n // Encode header and payload\n // @ts-expect-error - TS doesn't have a built-in TextEncoder\n const encodedHeader: string = this.base64UrlEncode(this.encoder.encode(JSON.stringify(header)));\n // @ts-expect-error - TS doesn't have a built-in TextEncoder\n const encodedPayload: string = this.base64UrlEncode(this.encoder.encode(JSON.stringify(fullPayload)));\n\n // Create signature base\n const signatureBase: string = `${encodedHeader}.${encodedPayload}`;\n\n // Get key and sign\n const key: CryptoKey = await this.getSecretKey();\n const signature: ArrayBuffer = await crypto.subtle.sign(\n { name: 'HMAC' },\n key,\n this.encoder.encode(signatureBase)\n );\n\n // Encode signature and create token\n const encodedSignature: string = this.base64UrlEncode(signature);\n return `${signatureBase}.${encodedSignature}`;\n }\n\n /**\n * Verify a JWT token and return the decoded payload\n * @param {string} token - The JWT token to verify\n * @returns {Promise<JWTPayload>} - The decoded payload if verification succeeds\n * @throws {Error} - If verification fails\n */\n public async verify(token: string): Promise<JWTPayload> {\n if (!token || typeof token !== 'string') {\n throw new Error('Token is required and must be a string');\n }\n\n // Split token into parts\n const parts: string[] = token.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid token format');\n }\n\n const [encodedHeader, encodedPayload, encodedSignature] = parts;\n\n // Decode header and payload\n try {\n // @ts-expect-error - TS doesn't have a built-in TextDecoder\n const header: JWTHeader = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedHeader)));\n // @ts-expect-error - TS doesn't have a built-in TextDecoder\n const payload: JWTPayload = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedPayload)));\n\n // Check algorithm\n if (header.alg !== 'HS256') {\n throw new Error(`Unsupported algorithm: ${header.alg}`);\n }\n\n // Check expiration\n const now: number = Math.floor(Date.now() / 1000);\n if (payload.exp && payload.exp < now) {\n throw new Error('Token has expired');\n }\n\n // Verify signature\n const key: CryptoKey = await this.getSecretKey();\n const signatureBase: string = `${encodedHeader}.${encodedPayload}`;\n const signature: ArrayBuffer = this.base64UrlDecode(encodedSignature) as ArrayBuffer;\n\n const isValid: boolean = await crypto.subtle.verify(\n { name: 'HMAC' },\n key,\n signature,\n this.encoder.encode(signatureBase)\n );\n\n if (!isValid) {\n throw new Error('Invalid signature');\n }\n\n return payload;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Token verification failed: ${error.message}`);\n }\n throw new Error('Token verification failed: Unknown error');\n }\n }\n}","import * as Party from \"./types/party\";\nimport { JWTAuth } from \"./jwt\";\nimport { response } from \"./utils\";\n\nexport const guardManageWorld = async (_, req: Party.Request, room: Party.Room): Promise<boolean> => {\n const tokenShard = req.headers.get(\"x-access-shard\");\n if (tokenShard) {\n if (tokenShard !== room.env.SHARD_SECRET) {\n return false\n }\n return true\n }\n const url = new URL(req.url);\n const token = req.headers.get(\"Authorization\") ?? url.searchParams.get(\"world-auth-token\");\n if (!token) {\n return false;\n }\n const jwt = new JWTAuth(room.env.AUTH_JWT_SECRET as string);\n try {\n const payload = await jwt.verify(token);\n if (!payload) {\n return false;\n }\n } catch (error) {\n return false;\n }\n return true;\n}","import type * as Party from \"./types/party\";\n\n/**\n * @description Factory function that creates a session guard with access to room storage\n * @param {Party.Storage} storage - The room storage instance\n * @returns {Function} - The guard function\n * \n * @example\n * ```typescript\n * import { createRequireSessionGuard } from \"./session.guard\";\n * \n * export class GameRoom {\n * constructor(private room: Party.Room) {}\n * \n * @Action(\"sendMessage\")\n * @Guard([createRequireSessionGuard(this.room.storage)])\n * async sendMessage(user: User, message: string, conn: Party.Connection) {\n * // This action will only execute if the user has a valid session\n * this.$broadcast({ type: \"message\", user, message });\n * }\n * }\n * ```\n */\nexport function createRequireSessionGuard(storage: Party.Storage) {\n return async (sender: Party.Connection, value: any): Promise<boolean> => {\n if (!sender || !sender.id) {\n return false;\n }\n\n try {\n // Check if session exists in storage\n const session = await storage.get(`session:${sender.id}`);\n \n // Return false if no session found\n if (!session) {\n return false;\n }\n\n // Verify session has required properties\n const typedSession = session as { publicId: string, created?: number, connected?: boolean };\n if (!typedSession.publicId) {\n return false;\n }\n\n // Session exists and is valid\n return true;\n } catch (error) {\n // If there's an error accessing storage, deny access\n console.error('Error checking session in requireSession guard:', error);\n return false;\n }\n };\n}\n\n/**\n * @description Guard function that verifies if a user session exists (for room and request guards)\n * @param {Party.Connection} sender - The connection object of the sender\n * @param {any} value - The value/payload sent with the action or request\n * @param {Party.Room} room - The room instance\n * @returns {Promise<boolean>} - Returns true if session exists, false otherwise\n * \n * @example\n * ```typescript\n * import { requireSession } from \"./session.guard\";\n * \n * // For room guards\n * @Room({\n * path: \"game-{id}\",\n * guards: [requireSession]\n * })\n * export class GameRoom {\n * // Room implementation\n * }\n * \n * // For request guards\n * @Request({ path: '/api/data', method: 'GET' })\n * @Guard([requireSession])\n * async getData(req: Party.Request, res: ServerResponse) {\n * // This request will only execute if the user has a valid session\n * return res.success({ data: \"protected data\" });\n * }\n * ```\n */\nexport const requireSession = async (sender: Party.Connection, value: any, room: Party.Room): Promise<boolean> => {\n if (!sender || !sender.id) {\n return false;\n }\n\n try {\n // Check if session exists in storage\n const session = await room.storage.get(`session:${sender.id}`);\n \n // Return false if no session found\n if (!session) {\n return false;\n }\n\n // Verify session has required properties\n const typedSession = session as { publicId: string, created?: number, connected?: boolean };\n if (!typedSession.publicId) {\n return false;\n }\n\n // Session exists and is valid\n return true;\n } catch (error) {\n // If there's an error accessing storage, deny access\n console.error('Error checking session in requireSession guard:', error);\n return false;\n }\n};\n"],"mappings":"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","names":["Action","name","bodyValidation","target","propertyKey","constructor","_actionMetadata","Map","set","key","Request","options","_requestMetadata","path","startsWith","method","routeKey","Room","prototype","maxUsers","throttleStorage","throttleSync","sessionExpiryTime","guards","RoomGuard","Guard","descriptor","Array","isArray","generateShortUUID","chars","uuid","i","randomIndex","Math","floor","random","length","Storage","memory","Map","put","key","value","set","get","delete","list","dset","z","createStatesSnapshot","getByPath","load","syncClass","DELETE_TOKEN","generateShortUUID","dset","isPromise","value","Promise","awaitReturn","val","isClass","obj","prototype","constructor","throttle","func","wait","timeout","lastArgs","args","setTimeout","extractParams","pattern","str","regexPattern","replace","regex","RegExp","match","exec","groups","dremove","keys","split","i","l","length","t","k","buildObject","valuesMap","allMemory","memoryObj","path","get","dset","response","status","body","Response","JSON","stringify","headers","ServerResponse","interceptors","statusCode","responseBody","responseHeaders","constructor","status","code","body","header","name","value","setHeaders","headers","use","interceptor","push","buildResponse","response","Response","JSON","stringify","interceptedResponse","Promise","error","console","json","send","undefined","text","redirect","url","success","badRequest","message","details","notPermitted","unauthorized","notFound","serverError","cors","res","options","newHeaders","Headers","headers","requestOrigin","origin","set","credentials","exposedHeaders","length","join","methods","allowedHeaders","maxAge","toString","Response","body","status","createCorsInterceptor","Message","z","object","action","string","value","any","Server","subRoom","rooms","constructor","room","isHibernate","hibernate","roomStorage","storage","send","conn","obj","structuredClone","interceptorPacket","signal","getUsersProperty","publicId","state","user","awaitReturn","JSON","stringify","broadcast","getConnections","onStart","createRoom","runGarbageCollector","garbageCollector","sessionExpiryTime","options","getSubRoom","activeConnections","activePrivateIds","Set","map","id","sessions","list","users","usersPropName","getUsersPropName","validPublicIds","expiredPublicIds","SESSION_EXPIRY_TIME","now","Date","key","session","startsWith","privateId","replace","typedSession","has","connected","created","deleteSession","add","currentUsers","delete","error","console","instance","init","initPersist","params","extractParams","path","loadMemory","root","get","memory","tmpObject","dset","load","$memoryAll","$send","$broadcast","$sessionTransfer","targetRoomId","userSession","userSnapshot","createStatesSnapshot","transferData","sessionState","targetRoomParty","context","parties","main","response","fetch","method","body","headers","ok","Error","text","transferToken","json","syncCb","values","getMemoryAll","buildObject","packet","type","clear","persistCb","_instance","getByPath","itemValue","DELETE_TOKEN","put","syncClass","onSync","throttle","onPersist","meta","propId","metadata","_propertyMetadata","getUserConnectionProperty","connectedPropName","updateUserConnectionStatus","isConnected","connectionSignal","set","getSession","e","saveSession","data","sessionData","undefined","updateSessionConnection","onConnectClient","ctx","close","roomGuards","guard","isAuthorized","request","url","URL","searchParams","existingSession","generateShortUUID","classType","restored","isClass","snapshot","sessionPrivateId","setState","pId","onConnect","onConnectShard","shardId","shard","clients","Map","onMessage","message","sender","handleShardMessage","parse","result","safeParse","success","warn","actions","actionName","guards","bodyValidation","bodyResult","shardConnection","parsedMessage","shardState","handleShardClientConnect","handleShardClientMessage","handleShardClientDisconnect","requestInfo","virtualContext","Headers","virtualConnection","targetClientId","currentState","mergedState","Object","assign","payload","payloadString","clientState","onClose","connectionUpdated","onAlarm","onError","connection","onRequest","req","isFromShard","res","ServerResponse","createCorsInterceptor","status","handleShardRequest","handleDirectRequest","handleSessionRestore","badRequest","serverError","notFound","pathname","endsWith","tryMatchRequestHandler","legacyResponse","Response","requestHandlers","split","slice","join","routeKey","handler","entries","firstColonIndex","indexOf","handlerMethod","substring","handlerPath","pathMatches","extractPathParams","notPermitted","bodyData","includes","contentType","validation","details","requestPath","pathRegexString","pathRegex","RegExp","test","paramNames","forEach","segment","push","matches","match","length","i","originalClientIp","enhancedReq","createEnhancedRequest","originalReq","clonedReq","clone","viaShard","Shard","ws","connectionMap","mainServerStub","worldUrl","worldId","lastReportedConnections","statsInterval","statsIntervalId","constructor","room","Map","onStart","roomId","id","split","roomStub","context","parties","main","get","console","warn","socket","headers","addEventListener","event","message","JSON","parse","data","targetClientId","clientConn","send","broadcast","error","updateWorldStats","startPeriodicStatsUpdates","clearInterval","setInterval","catch","stopPeriodicStatsUpdates","onConnect","conn","ctx","set","request","forEach","value","key","requestInfo","url","method","stringify","type","privateId","onMessage","sender","parsedMessage","wrappedMessage","publicId","state","payload","onClose","delete","currentConnections","size","worldRoom","world","response","fetch","env","SHARD_SECRET","body","shardId","connections","ok","errorData","json","status","onRequest","req","URL","path","pathname","text","Headers","clientIp","requestInit","onAlarm","testRoom","Room","options","createServer","io","server","Server","rooms","isShard","shard","ServerIo","path","parties","game","partyFn","env","prototype","throttleSync","throttleStorage","shardServer","Shard","subRoom","context","main","Map","lobby","values","onStart","room","createClient","id","opts","client","connection","request","method","url","URL","Request","toString","response","onRequest","MockPartyClient","events","id","conn","constructor","server","Map","generateShortUUID","MockConnection","addEventListener","event","cb","has","set","get","push","removeEventListener","callbacks","index","indexOf","splice","length","delete","_trigger","data","send","onMessage","JSON","stringify","MockLobby","lobbyId","socket","_init","connection","idOrOptions","maybeOptions","options","room","fetch","url","baseUrl","includes","request","MockContext","parties","main","partyFn","serverCache","MockPartyRoom","clients","storage","context","env","Storage","opts","URL","query","key","value","Object","entries","searchParams","String","Request","toString","method","headers","onConnect","broadcast","forEach","client","getConnection","getConnections","Array","from","values","map","clear","state","setState","close","onClose","ServerIo","ClientIo","signal","sync","id","persist","z","JWTAuth","secret","encoder","decoder","constructor","Error","TextEncoder","TextDecoder","getSecretKey","keyData","encode","crypto","subtle","importKey","name","hash","base64UrlEncode","buffer","base64","btoa","String","fromCharCode","Uint8Array","replace","base64UrlDecode","base64Url","padding","repeat","length","rawData","atob","i","charCodeAt","sign","payload","options","expiresIn","exp","Math","floor","Date","now","match","value","parseInt","unit","seconds","fullPayload","iat","header","alg","typ","encodedHeader","JSON","stringify","encodedPayload","signatureBase","key","signature","encodedSignature","verify","token","parts","split","parse","decode","isValid","error","message","guardManageWorld","_","req","room","tokenShard","headers","get","env","SHARD_SECRET","url","URL","token","searchParams","jwt","JWTAuth","AUTH_JWT_SECRET","payload","verify","error","MAX_PLAYERS_PER_SHARD","RoomConfigSchema","z","object","name","string","balancingStrategy","enum","public","boolean","maxPlayersPerShard","number","int","positive","minShards","min","maxShards","optional","RegisterShardSchema","shardId","roomId","url","maxConnections","UpdateShardStatsSchema","connections","status","ScaleRoomSchema","targetShardCount","shardTemplate","urlTemplate","RoomConfig","id","signal","undefined","ShardInfo","currentConnections","lastHeartbeat","persist","WorldRoom","rooms","shards","rrCounters","defaultShardUrlTemplate","defaultMaxConnectionsPerShard","constructor","room","AUTH_JWT_SECRET","SHARD_SECRET","env","Error","onJoin","user","conn","ctx","canConnect","guardManageWorld","request","setState","state","isAdmin","interceptorPacket","_","obj","cleanupInactiveShards","now","Date","timeout","shardsValue","hasChanges","Object","values","forEach","shard","setTimeout","registerRoom","req","roomConfig","json","newRoom","set","i","createShard","updateShardStats","res","body","notFound","scaleRoom","data","roomShards","filter","previousShardCount","length","badRequest","currentShardCount","shardsToRemove","sort","a","b","slice","shardsToKeep","some","s","newShards","newShard","push","connect","text","trim","JSON","parse","parseError","autoCreate","result","findOptimalShard","error","success","console","serverError","mockRequest","activeShards","selectedShard","reduce","Math","floor","random","counter","nextCounter","template","replace","max","path","method","maxUsers","throttleStorage","throttleSync","createRequireSessionGuard","storage","sender","value","id","session","get","typedSession","publicId","error","console","requireSession","room"]}
|
|
1
|
+
{"version":3,"sources":["../src/decorators.ts","../../sync/src/utils.ts","../src/storage.ts","../src/server.ts","../src/utils.ts","../src/request/response.ts","../src/request/cors.ts","../src/shard.ts","../src/testing.ts","../src/mock.ts","../src/world.ts","../src/types/party.ts","../src/jwt.ts","../src/world.guard.ts","../src/session.guard.ts"],"sourcesContent":["import type * as Party from \"./types/party\"\nimport type { z } from \"zod\"\ntype GuardFn = (sender: Party.Connection, value: any | Party.Request, room: Party.Room) => boolean | Promise<boolean | Response>;\ntype RoomGuardFn = (conn: Party.Connection, ctx: Party.ConnectionContext, room: Party.Room) => boolean | Promise<boolean | Response>;\n\nexport function Action(name: string, bodyValidation?: z.ZodSchema) {\n return function (target: any, propertyKey: string) {\n if (!target.constructor._actionMetadata) {\n target.constructor._actionMetadata = new Map();\n }\n target.constructor._actionMetadata.set(name, {\n key: propertyKey,\n bodyValidation,\n });\n };\n}\n\n/**\n * Request decorator for handling HTTP requests with path and method routing\n * @param options Configuration for the HTTP request handler\n * @param bodyValidation Optional Zod schema for request body validation\n */\nexport interface RequestOptions {\n path: string;\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';\n}\n\nexport function Request(options: RequestOptions, bodyValidation?: z.ZodSchema) {\n return function (target: any, propertyKey: string) {\n if (!target.constructor._requestMetadata) {\n target.constructor._requestMetadata = new Map();\n }\n \n // Format the path to ensure it starts with a slash\n const path = options.path.startsWith('/') ? options.path : `/${options.path}`;\n const method = options.method || 'GET';\n \n // Create a unique key for this route using method and path\n const routeKey = `${method}:${path}`;\n \n target.constructor._requestMetadata.set(routeKey, {\n key: propertyKey,\n path,\n method,\n bodyValidation,\n });\n };\n}\n\nexport interface RoomOptions {\n path: string;\n maxUsers?: number;\n throttleStorage?: number;\n throttleSync?: number;\n hibernate?: boolean;\n guards?: RoomGuardFn[];\n sessionExpiryTime?: number;\n}\n\nexport function Room(options: RoomOptions) {\n return function (target: any) {\n target.path = options.path;\n target.prototype.maxUsers = options.maxUsers;\n target.prototype.throttleStorage = options.throttleStorage;\n target.prototype.throttleSync = options.throttleSync;\n target.prototype.sessionExpiryTime = options.sessionExpiryTime ?? 5 * 60 * 1000;\n if (options.guards) {\n target['_roomGuards'] = options.guards;\n }\n };\n}\n\n/**\n * Room guard decorator\n * @param guards Array of guard functions to check on connection\n */\nexport function RoomGuard(guards: RoomGuardFn[]) {\n return function (target: any) {\n target['_roomGuards'] = guards;\n };\n}\n\n/**\n * Action guard decorator\n * @param guards Array of guard functions to check before action execution\n */\nexport function Guard(guards: GuardFn[]) {\n return function (\n target: any,\n propertyKey: string,\n descriptor: PropertyDescriptor\n ) {\n if (!target.constructor['_actionGuards']) {\n target.constructor['_actionGuards'] = new Map();\n }\n if (!Array.isArray(guards)) {\n guards = [guards]\n }\n target.constructor['_actionGuards'].set(propertyKey, guards);\n };\n}","/**\n * Checks if the given value is a function.\n *\n * @param {unknown} val - The value to check.\n * @returns {boolean} - True if the value is a function, false otherwise.\n * @example\n * isFunction(function() {}); // true\n * isFunction(() => {}); // true\n * isFunction(123); // false\n */\nexport function isFunction(val: unknown): boolean {\n return {}.toString.call(val) === \"[object Function]\";\n}\n\n/**\n * Checks if the given object is a class.\n *\n * @param {any} obj - The object to check.\n * @returns {boolean} - True if the object is a class, false otherwise.\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: any): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n/**\n * Checks if the given item is an object.\n *\n * @param {any} item - The item to check.\n * @returns {boolean} - True if the item is an object, false otherwise.\n * @example\n * isObject({}); // true\n * isObject(null); // false\n * isObject([]); // false\n */\nexport const isObject = (item: any): boolean =>\n item && typeof item === \"object\" && !Array.isArray(item) && item !== null;\n\n/**\n * Checks if the given value is an instance of a class.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - True if the value is an instance of a class, false otherwise.\n * @example\n * class MyClass {}\n * const instance = new MyClass();\n * isInstanceOfClass(instance); // true\n * isInstanceOfClass({}); // false\n */\nexport function isInstanceOfClass(value: unknown): boolean {\n if (\n value === null ||\n typeof value !== \"object\" ||\n value === undefined ||\n Array.isArray(value)\n ) {\n return false;\n }\n return Object.getPrototypeOf(value) !== Object.prototype;\n}\n\nexport function generateShortUUID(): string {\n const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n let uuid = '';\n for (let i = 0; i < 8; i++) {\n const randomIndex = Math.floor(Math.random() * chars.length);\n uuid += chars[randomIndex];\n }\n return uuid;\n}","export class Storage {\n private memory = new Map();\n async put(key, value) {\n this.memory.set(key, value);\n }\n async get(key) {\n return this.memory.get(key);\n }\n async delete(key) {\n this.memory.delete(key);\n }\n async list() {\n return this.memory;\n }\n}\n","import { dset } from \"dset\";\nimport z from \"zod\";\nimport {\n createStatesSnapshot,\n getByPath,\n load,\n syncClass,\n DELETE_TOKEN,\n generateShortUUID\n} from \"@signe/sync\";\nimport type * as Party from \"./types/party\";\nimport {\n awaitReturn,\n buildObject,\n extractParams,\n isClass,\n throttle,\n} from \"./utils\";\nimport { ServerResponse } from \"./request/response\";\nimport { createCorsInterceptor } from \"./request/cors\";\nimport { Signal, WritableSignal } from \"@signe/reactive\";\n\nconst Message = z.object({\n action: z.string(),\n value: z.any(),\n});\n\ntype CreateRoomOptions = {\n getMemoryAll?: boolean;\n sessionExpiryTime?: number;\n throttleSync?: number;\n throttleStorage?: number;\n};\n\n/**\n * @class Server\n * @implements {Party.Server}\n * @description Represents a server that manages rooms and connections for a multiplayer game or application.\n * \n * @example\n * ```typescript\n * import { Room, Server, ServerIo } from \"@yourpackage/room\";\n * \n * @Room({ path: \"game\" })\n * class GameRoom {\n * // Room implementation\n * }\n * \n * class MyServer extends Server {\n * rooms = [GameRoom];\n * }\n * \n * const server = new MyServer(new ServerIo(\"game\"));\n * server.onStart();\n * ```\n */\nexport class Server implements Party.Server {\n subRoom = null;\n rooms: any[] = [];\n\n /**\n * @constructor\n * @param {Party.Room} room - The room object representing the current game or application instance.\n * \n * @example\n * ```typescript\n * const server = new MyServer(new ServerIo(\"game\"));\n * ```\n */\n constructor(readonly room: Party.Room) { }\n\n /**\n * @readonly\n * @property {boolean} isHibernate - Indicates whether the server is in hibernate mode.\n * \n * @example\n * ```typescript\n * if (!server.isHibernate) {\n * console.log(\"Server is active\");\n * }\n * ```\n */\n get isHibernate(): boolean {\n return !!this[\"options\"]?.hibernate;\n }\n\n get roomStorage(): Party.Storage {\n return this.room.storage\n }\n\n async send(conn: Party.Connection, obj: any, subRoom: any) {\n obj = structuredClone(obj);\n if (subRoom.interceptorPacket) {\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = conn.state as any;\n const user = signal?.()[publicId];\n obj = await awaitReturn(subRoom[\"interceptorPacket\"]?.(user, obj, conn));\n if (obj === null) return;\n }\n conn.send(JSON.stringify(obj));\n }\n\n broadcast(obj: any, subRoom: any) {\n for (let conn of this.room.getConnections()) {\n this.send(conn, obj, subRoom);\n }\n }\n\n /**\n * @method onStart\n * @async\n * @description Initializes the server and creates the initial room if not in hibernate mode.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * async function initServer() {\n * await server.onStart();\n * console.log(\"Server started\");\n * }\n * ```\n */\n\n async onStart() {\n // Only create a room if not in hibernate mode\n // This prevents unnecessary resource allocation for inactive rooms\n if (!this.isHibernate) {\n this.subRoom = await this.createRoom();\n }\n }\n\n async runGarbageCollector() {\n await this.garbageCollector({ sessionExpiryTime: -1 });\n }\n\n private async garbageCollector(options: { sessionExpiryTime: number }) {\n const subRoom = await this.getSubRoom();\n if (!subRoom) return;\n\n // Get active connections\n const activeConnections = [...this.room.getConnections()];\n const activePrivateIds = new Set(activeConnections.map(conn => conn.id));\n\n try {\n // Get all sessions from storage\n const sessions = await this.room.storage.list();\n const users = this.getUsersProperty(subRoom);\n const usersPropName = this.getUsersPropName(subRoom);\n\n // Store valid publicIds from sessions\n const validPublicIds = new Set<string>();\n const expiredPublicIds = new Set<string>();\n const SESSION_EXPIRY_TIME = options.sessionExpiryTime\n const now = Date.now();\n\n for (const [key, session] of sessions) {\n // Only process session entries\n if (!key.startsWith('session:')) continue;\n\n const privateId = key.replace('session:', '');\n const typedSession = session as { publicId: string, created: number, connected: boolean };\n\n // Check if session should be deleted based on:\n // 1. Connection is not active\n // 2. Session is marked as disconnected\n // 3. Session is older than expiry time\n if (!activePrivateIds.has(privateId) &&\n !typedSession.connected &&\n (now - typedSession.created) > SESSION_EXPIRY_TIME) {\n // Delete expired session\n await this.deleteSession(privateId);\n expiredPublicIds.add(typedSession.publicId);\n } else if (typedSession && typedSession.publicId) {\n // Keep track of valid publicIds from active or recent sessions\n validPublicIds.add(typedSession.publicId);\n }\n }\n\n // Clean up users only if ALL their sessions are expired\n if (users && usersPropName) {\n const currentUsers = users();\n for (const publicId in currentUsers) {\n // Only delete user if they have an expired session and no valid sessions\n if (expiredPublicIds.has(publicId) && !validPublicIds.has(publicId)) {\n delete currentUsers[publicId];\n await this.room.storage.delete(`${usersPropName}.${publicId}`);\n }\n }\n }\n\n } catch (error) {\n console.error('Error in garbage collector:', error);\n }\n }\n\n /**\n * @method createRoom\n * @private\n * @async\n * @param {CreateRoomOptions} [options={}] - Options for creating the room.\n * @returns {Promise<Object>} The created room instance.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalCreateRoom() {\n * const room = await this.createRoom({ getMemoryAll: true });\n * console.log(\"Room created:\", room);\n * }\n * ```\n */\n private async createRoom(options: CreateRoomOptions = {}) {\n let instance\n let init = true\n let initPersist = true\n\n // Find the appropriate room based on the current room ID\n for (let room of this.rooms) {\n const params = extractParams(room.path, this.room.id);\n if (params) {\n instance = new room(this.room, params);\n break;\n }\n }\n\n if (!instance) {\n return null;\n }\n\n // Load the room's memory from storage\n // This ensures persistence across server restarts\n const loadMemory = async () => {\n const root = await this.room.storage.get(\".\");\n const memory = await this.room.storage.list();\n const tmpObject: any = root || {};\n for (let [key, value] of memory) {\n if (key.startsWith('session:')) {\n continue;\n }\n if (key == \".\") {\n continue;\n }\n dset(tmpObject, key, value);\n }\n load(instance, tmpObject, true);\n };\n\n instance.$memoryAll = {}\n instance.$send = (conn: Party.Connection, obj: any) => {\n return this.send(conn, obj, instance)\n }\n instance.$broadcast = (obj: any) => {\n return this.broadcast(obj, instance)\n }\n instance.$sessionTransfer = async (conn: Party.Connection, targetRoomId: string) => {\n let user: any;\n \n const signal = this.getUsersProperty(instance);\n \n if (!signal) {\n console.error('[sessionTransfer] `users` property not defined in the room.');\n return null;\n }\n \n const { publicId } = conn.state as any;\n user = signal()[publicId];\n\n if (!user) {\n console.error(`[sessionTransfer] User with publicId ${publicId} not found.`);\n return null;\n }\n\n const sessions = await this.room.storage.list();\n let userSession: any = null;\n let privateId: string | null = null;\n\n for (const [key, session] of sessions) {\n if (key.startsWith('session:') && (session as any).publicId === publicId) {\n userSession = session;\n privateId = key.replace('session:', '');\n break;\n }\n }\n\n if (!userSession || !privateId) {\n console.error(`[sessionTransfer] Session for publicId ${publicId} not found.`);\n return null;\n }\n\n const usersPropName = this.getUsersPropName(instance);\n if (!usersPropName) {\n console.error('[sessionTransfer] `users` property not defined in the room.');\n return null;\n }\n\n // Create a snapshot of the user state\n const userSnapshot = createStatesSnapshot(user);\n\n const transferData = {\n privateId,\n userSnapshot,\n sessionState: userSession.state,\n publicId\n };\n\n try {\n const targetRoomParty = await this.room.context.parties.main.get(targetRoomId);\n const response = await targetRoomParty.fetch('/session-transfer', {\n method: 'POST',\n body: JSON.stringify(transferData),\n headers: {\n 'Content-Type': 'application/json'\n }\n });\n\n if (!response.ok) {\n throw new Error(`Transfer request failed: ${await response.text()}`);\n }\n\n const { transferToken } = await response.json();\n \n return transferToken;\n } catch (error) {\n console.error(`[sessionTransfer] Failed to transfer session to room ${targetRoomId}:`, error);\n return null;\n }\n };\n\n // Sync callback: Broadcast changes to all clients\n const syncCb = (values) => {\n if (options.getMemoryAll) {\n buildObject(values, instance.$memoryAll);\n }\n if (init && this.isHibernate) {\n init = false;\n return;\n }\n const packet = buildObject(values, instance.$memoryAll);\n this.broadcast(\n {\n type: \"sync\",\n value: packet,\n },\n instance\n );\n values.clear();\n }\n\n // Persist callback: Save changes to storage\n const persistCb = async (values: Map<string, any>) => {\n if (initPersist) {\n values.clear();\n return;\n }\n for (let [path, value] of values) {\n const _instance =\n path == \".\" ? instance : getByPath(instance, path);\n const itemValue = createStatesSnapshot(_instance);\n if (value == DELETE_TOKEN) {\n await this.room.storage.delete(path);\n } else {\n await this.room.storage.put(path, itemValue);\n }\n }\n values.clear();\n }\n\n // Set up syncing and persistence with throttling to optimize performance\n syncClass(instance, {\n onSync: throttle(syncCb, instance[\"throttleSync\"] ?? 500),\n onPersist: throttle(persistCb, instance[\"throttleStorage\"] ?? 2000),\n });\n\n await loadMemory();\n\n initPersist = false\n\n return instance\n }\n\n /**\n * @method getSubRoom\n * @private\n * @async\n * @param {Object} [options={}] - Options for getting the sub-room.\n * @returns {Promise<Object>} The sub-room instance.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * async function internalGetSubRoom() {\n * const subRoom = await this.getSubRoom();\n * console.log(\"Sub-room retrieved:\", subRoom);\n * }\n * ```\n */\n private async getSubRoom(options = {}): Promise<any | null> {\n let subRoom // instance of the room or null\n if (this.isHibernate) {\n subRoom = await this.createRoom(options)\n }\n else {\n subRoom = this.subRoom\n }\n return subRoom\n }\n\n /**\n * @method getUsersProperty\n * @private\n * @param {Object} subRoom - The sub-room instance.\n * @returns {Object|null} The users property of the sub-room, or null if not found.\n * \n * @example\n * ```typescript\n * // This method is private and called internally\n * function internalGetUsers(subRoom) {\n * const users = this.getUsersProperty(subRoom);\n * console.log(\"Users:\", users);\n * }\n * ```\n */\n\n private getUsersProperty(subRoom) {\n const meta = subRoom.constructor[\"_propertyMetadata\"];\n const propId = meta?.get(\"users\");\n if (propId) {\n return subRoom[propId];\n }\n return null;\n }\n\n private getUsersPropName(subRoom) {\n if (!subRoom) return null;\n const metadata = subRoom.constructor._propertyMetadata;\n if (!metadata) return null;\n return metadata.get(\"users\");\n }\n\n /**\n * Retrieves the connection status property from a user object.\n * \n * @param {any} user - The user object to get the connection property from.\n * @returns {Function|null} - The connection property signal function or null if not found.\n * @private\n */\n private getUserConnectionProperty(user: any): WritableSignal<boolean> | null {\n if (!user) return null;\n \n const metadata = user.constructor._propertyMetadata;\n if (!metadata) return null;\n \n const connectedPropName = metadata.get(\"connected\");\n if (!connectedPropName) return null;\n\n return user[connectedPropName];\n }\n \n /**\n * Updates a user's connection status in the signal.\n * \n * @param {any} user - The user object to update.\n * @param {boolean} isConnected - The new connection status.\n * @returns {boolean} - Whether the update was successful.\n * @private\n */\n private updateUserConnectionStatus(user: any, isConnected: boolean): boolean {\n const connectionSignal = this.getUserConnectionProperty(user);\n\n if (connectionSignal) {\n connectionSignal.set(isConnected);\n return true;\n }\n \n return false;\n }\n\n /**\n * @method getSession\n * @private\n * @param {string} privateId - The private ID of the session.\n * @returns {Promise<Object|null>} The session object, or null if not found.\n * \n * @example\n * ```typescript\n * const session = await server.getSession(\"privateId\");\n * console.log(session);\n * ```\n */\n async getSession(privateId: string): Promise<{ publicId: string, state?: any, created?: number, connected?: boolean } | null> {\n if (!privateId) return null;\n try {\n const session = await this.room.storage.get(`session:${privateId}`);\n return session as { publicId: string, state?: any, created: number, connected: boolean } | null;\n } catch (e) {\n return null;\n }\n }\n\n private async saveSession(privateId: string, data: { publicId: string, state?: any, created?: number, connected?: boolean }) {\n const sessionData = {\n ...data,\n created: data.created || Date.now(),\n connected: data.connected !== undefined ? data.connected : true\n };\n await this.room.storage.put(`session:${privateId}`, sessionData);\n }\n\n private async updateSessionConnection(privateId: string, connected: boolean) {\n const session = await this.getSession(privateId);\n if (session) {\n await this.saveSession(privateId, { ...session, connected });\n }\n }\n\n /**\n * @method deleteSession\n * @private\n * @param {string} privateId - The private ID of the session to delete.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * await server.deleteSession(\"privateId\");\n * ```\n */\n async deleteSession(privateId: string) {\n await this.room.storage.delete(`session:${privateId}`);\n }\n\n async onConnectClient(conn: Party.Connection, ctx: Party.ConnectionContext) {\n const subRoom = await this.getSubRoom({\n getMemoryAll: true,\n })\n\n if (!subRoom) {\n conn.close();\n return;\n }\n\n const sessionExpiryTime = subRoom.constructor.sessionExpiryTime;\n await this.garbageCollector({ sessionExpiryTime });\n\n // Check room guards\n const roomGuards = subRoom.constructor['_roomGuards'] || [];\n for (const guard of roomGuards) {\n const isAuthorized = await guard(conn, ctx, this.room);\n if (!isAuthorized) {\n conn.close();\n return;\n }\n }\n\n // Handle session transfer\n let transferToken = null;\n if (ctx.request?.url) {\n const url = new URL(ctx.request.url);\n transferToken = url.searchParams.get('transferToken');\n }\n let transferData: any = null;\n if (transferToken) {\n transferData = await this.room.storage.get(`transfer:${transferToken}`);\n if (transferData) {\n await this.room.storage.delete(`transfer:${transferToken}`);\n }\n }\n\n // Check for existing session\n const existingSession = await this.getSession(conn.id)\n\n // Generate IDs\n const publicId = existingSession?.publicId || transferData?.publicId || generateShortUUID();\n\n let user = null;\n const signal = this.getUsersProperty(subRoom);\n const usersPropName = this.getUsersPropName(subRoom);\n\n if (signal) {\n const { classType } = signal.options;\n\n // Restore state if exists\n if (!existingSession?.publicId) {\n // Check if we have a transferred user already restored\n if (transferData?.restored && signal()[publicId]) {\n user = signal()[publicId];\n } else {\n user = isClass(classType) ? new classType() : classType(conn, ctx);\n signal()[publicId] = user;\n const snapshot = createStatesSnapshot(user);\n this.room.storage.put(`${usersPropName}.${publicId}`, snapshot);\n }\n }\n else {\n user = signal()[existingSession.publicId];\n }\n\n // Only store new session if it doesn't exist\n if (!existingSession) {\n // Use the transferred privateId if available, otherwise use connection id\n const sessionPrivateId = transferData?.privateId || conn.id;\n await this.saveSession(sessionPrivateId, {\n publicId\n });\n }\n else {\n await this.updateSessionConnection(conn.id, true);\n }\n }\n // Update user connection status if applicable\n this.updateUserConnectionStatus(user, true);\n\n // Store both IDs in connection state\n conn.setState({\n ...conn.state,\n publicId\n });\n\n // Call the room's onJoin method if it exists\n await awaitReturn(subRoom[\"onJoin\"]?.(user, conn, ctx));\n\n // Send initial sync data with both IDs to the new connection\n this.send(conn, {\n type: \"sync\",\n value: {\n pId: publicId,\n ...subRoom.$memoryAll,\n },\n }, subRoom)\n }\n\n /**\n * @method onConnect\n * @async\n * @param {Party.Connection} conn - The connection object for the new user.\n * @param {Party.ConnectionContext} ctx - The context of the connection.\n * @description Handles a new user connection, creates a user object, and sends initial sync data.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onConnect = async (conn, ctx) => {\n * await server.onConnect(conn, ctx);\n * console.log(\"New user connected:\", conn.id);\n * };\n * ```\n */\n async onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {\n if (ctx.request?.headers.has('x-shard-id')) {\n this.onConnectShard(conn, ctx);\n }\n else {\n await this.onConnectClient(conn, ctx);\n }\n }\n\n /**\n * @method onConnectShard\n * @private\n * @param {Party.Connection} conn - The connection object for the new shard.\n * @param {Party.ConnectionContext} ctx - The context of the shard connection.\n * @description Handles a new shard connection, setting up the necessary state.\n * @returns {void}\n */\n onConnectShard(conn: Party.Connection, ctx: Party.ConnectionContext) {\n // Set shard metadata in connection state\n const shardId = ctx.request?.headers.get('x-shard-id') || 'unknown-shard';\n conn.setState({\n shard: true,\n shardId,\n clients: new Map() // Track clients connected through this shard\n });\n }\n\n /**\n * @method onMessage\n * @async\n * @param {string} message - The message received from a user or shard.\n * @param {Party.Connection} sender - The connection object of the sender.\n * @description Processes incoming messages, handling differently based on if sender is shard or client.\n * @returns {Promise<void>}\n */\n async onMessage(message: string, sender: Party.Connection) {\n // Check if message is from a shard\n if (sender.state && (sender.state as any).shard) {\n await this.handleShardMessage(message, sender);\n return;\n }\n\n // Regular client message handling\n let json\n try {\n json = JSON.parse(message)\n }\n catch (e) {\n return;\n }\n\n // Validate incoming messages\n const result = Message.safeParse(json);\n if (!result.success) {\n return;\n }\n\n const subRoom = await this.getSubRoom()\n\n if (!subRoom) {\n console.warn(\"Room not found\");\n return;\n }\n\n // Check room guards\n const roomGuards = subRoom.constructor['_roomGuards'] || [];\n for (const guard of roomGuards) {\n const isAuthorized = await guard(sender, result.data.value, this.room);\n if (!isAuthorized) {\n return;\n }\n }\n\n const actions = subRoom.constructor[\"_actionMetadata\"];\n if (actions) {\n const signal = this.getUsersProperty(subRoom);\n const { publicId } = sender.state as any;\n const user = signal?.()[publicId];\n const actionName = actions.get(result.data.action);\n if (actionName) {\n\n // Check all guards if they exist\n const guards = subRoom.constructor['_actionGuards']?.get(actionName.key) || [];\n for (const guard of guards) {\n const isAuthorized = await guard(sender, result.data.value);\n if (!isAuthorized) {\n return;\n }\n }\n\n // Validate action body if a validation schema is defined\n if (actionName.bodyValidation) {\n const bodyResult = actionName.bodyValidation.safeParse(\n result.data.value\n );\n if (!bodyResult.success) {\n return;\n }\n }\n // Execute the action\n await awaitReturn(\n subRoom[actionName.key](user, result.data.value, sender)\n );\n }\n }\n }\n\n /**\n * @method handleShardMessage\n * @private\n * @async\n * @param {string} message - The message received from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Processes messages from shards, extracting client information.\n * @returns {Promise<void>}\n */\n private async handleShardMessage(message: string, shardConnection: Party.Connection) {\n let parsedMessage;\n try {\n parsedMessage = JSON.parse(message);\n } catch (e) {\n console.error(\"Error parsing shard message:\", e);\n return;\n }\n\n const shardState = shardConnection.state as any;\n const clients = shardState.clients;\n\n switch (parsedMessage.type) {\n case 'shard.clientConnected':\n // Handle new client connection through shard\n await this.handleShardClientConnect(parsedMessage, shardConnection);\n break;\n\n case 'shard.clientMessage':\n // Handle message from a client through shard\n await this.handleShardClientMessage(parsedMessage, shardConnection);\n break;\n\n case 'shard.clientDisconnected':\n // Handle client disconnection through shard\n await this.handleShardClientDisconnect(parsedMessage, shardConnection);\n break;\n\n default:\n console.warn(`Unknown shard message type: ${parsedMessage.type}`);\n }\n }\n\n /**\n * @method handleShardClientConnect\n * @private\n * @async\n * @param {Object} message - The client connection message from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Handles a new client connection via a shard.\n * @returns {Promise<void>}\n */\n private async handleShardClientConnect(message: any, shardConnection: Party.Connection) {\n const { privateId, requestInfo } = message;\n const shardState = shardConnection.state as any;\n\n // Create a virtual connection context for the client\n const virtualContext: Party.ConnectionContext = {\n request: requestInfo ? {\n headers: new Headers(requestInfo.headers),\n method: requestInfo.method,\n url: requestInfo.url\n } as unknown as Party.Request : undefined\n };\n\n // Create a virtual connection for the client\n const virtualConnection: Partial<Party.Connection> = {\n id: privateId,\n send: (data: string) => {\n // Forward to the actual client through the shard\n shardConnection.send(JSON.stringify({\n targetClientId: privateId,\n data\n }));\n },\n state: {},\n setState: (state: unknown) => {\n // Store client state in the shard's client map\n const clients = shardState.clients;\n const currentState = clients.get(privateId) || {};\n const mergedState = Object.assign({}, currentState, state as object);\n clients.set(privateId, mergedState);\n\n // Update our virtual connection's state reference\n virtualConnection.state = clients.get(privateId);\n return virtualConnection.state as any;\n },\n close: () => {\n // Send close command to the shard\n shardConnection.send(JSON.stringify({\n type: 'shard.closeClient',\n privateId\n }));\n\n // Clean up virtual connection\n if (shardState.clients) {\n shardState.clients.delete(privateId);\n }\n }\n };\n\n // Initialize the client's state in the shard state\n if (!shardState.clients.has(privateId)) {\n shardState.clients.set(privateId, {});\n }\n\n // Now handle this virtual connection as a regular client connection\n await this.onConnectClient(virtualConnection as Party.Connection, virtualContext);\n }\n\n /**\n * @method handleShardClientMessage\n * @private\n * @async\n * @param {Object} message - The client message from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Handles a message from a client via a shard.\n * @returns {Promise<void>}\n */\n private async handleShardClientMessage(message: any, shardConnection: Party.Connection) {\n const { privateId, publicId, payload } = message;\n const shardState = shardConnection.state as any;\n const clients = shardState.clients;\n\n // Get or create virtual connection for this client\n if (!clients.has(privateId)) {\n console.warn(`Received message from unknown client ${privateId}, creating virtual connection`);\n clients.set(privateId, { publicId });\n }\n\n // Create a virtual connection for the client\n const virtualConnection: Partial<Party.Connection> = {\n id: privateId,\n send: (data: string) => {\n // Forward to the actual client through the shard\n shardConnection.send(JSON.stringify({\n targetClientId: privateId,\n data\n }));\n },\n state: clients.get(privateId),\n setState: (state: unknown) => {\n const currentState = clients.get(privateId) || {};\n const mergedState = Object.assign({}, currentState, state as object);\n clients.set(privateId, mergedState);\n virtualConnection.state = clients.get(privateId);\n return virtualConnection.state as any;\n },\n close: () => {\n shardConnection.send(JSON.stringify({\n type: 'shard.closeClient',\n privateId\n }));\n\n if (shardState.clients) {\n shardState.clients.delete(privateId);\n }\n }\n };\n\n // Process the payload using the regular message handler\n const payloadString = typeof payload === 'string' ? payload : JSON.stringify(payload);\n await this.onMessage(payloadString, virtualConnection as Party.Connection);\n }\n\n /**\n * @method handleShardClientDisconnect\n * @private\n * @async\n * @param {Object} message - The client disconnection message from a shard.\n * @param {Party.Connection} shardConnection - The connection object of the shard.\n * @description Handles a client disconnection via a shard.\n * @returns {Promise<void>}\n */\n private async handleShardClientDisconnect(message: any, shardConnection: Party.Connection) {\n const { privateId, publicId } = message;\n const shardState = shardConnection.state as any;\n const clients = shardState.clients;\n\n // Get client state\n const clientState = clients.get(privateId);\n if (!clientState) {\n console.warn(`Disconnection for unknown client ${privateId}`);\n return;\n }\n\n // Create a virtual connection for the client one last time\n const virtualConnection: Partial<Party.Connection> = {\n id: privateId,\n send: () => { }, // No-op since client is disconnecting\n state: clientState,\n setState: () => {\n // No-op since client is disconnecting\n return {} as any;\n },\n close: () => { }\n };\n\n // Handle disconnection with the regular onClose handler\n await this.onClose(virtualConnection as Party.Connection);\n\n // Clean up\n clients.delete(privateId);\n }\n\n /**\n * @method onClose\n * @async\n * @param {Party.Connection} conn - The connection object of the disconnecting user.\n * @description Handles user disconnection, removing them from the room and triggering the onLeave event..\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onClose = async (conn) => {\n * await server.onClose(conn);\n * console.log(\"User disconnected:\", conn.id);\n * };\n * ```\n */\n async onClose(conn: Party.Connection) {\n const subRoom = await this.getSubRoom()\n\n if (!subRoom) {\n return;\n }\n\n const signal = this.getUsersProperty(subRoom);\n\n if (!conn.state) {\n return;\n }\n\n const privateId = conn.id;\n const { publicId } = conn.state as any;\n const user = signal?.()[publicId];\n\n if (!user) return;\n\n // Mark session as disconnected instead of deleting it\n await this.updateSessionConnection(privateId, false);\n\n // Update user connection status in the signal\n const connectionUpdated = this.updateUserConnectionStatus(user, false);\n\n await awaitReturn(subRoom[\"onLeave\"]?.(user, conn));\n \n // Only broadcast disconnection if we couldn't update the connection signal\n if (!connectionUpdated) {\n // Broadcast user disconnection the old way\n this.broadcast({\n type: \"user_disconnected\",\n value: { publicId }\n }, subRoom);\n }\n }\n\n async onAlarm() {\n const subRoom = await this.getSubRoom()\n await awaitReturn(subRoom[\"onAlarm\"]?.(subRoom));\n }\n\n async onError(connection: Party.Connection, error: Error) {\n const subRoom = await this.getSubRoom()\n await awaitReturn(subRoom[\"onError\"]?.(connection, error));\n }\n\n /**\n * @method onRequest\n * @async\n * @param {Party.Request} req - The HTTP request to handle\n * @description Handles HTTP requests, either directly from clients or forwarded by shards\n * @returns {Promise<Response>} The response to return to the client\n */\n async onRequest(req: Party.Request) {\n // Check if the request is coming from a shard\n const isFromShard = req.headers.has('x-forwarded-by-shard');\n const shardId = req.headers.get('x-shard-id');\n \n // Create a response with proper CORS configuration\n const res = new ServerResponse([\n createCorsInterceptor()\n ]);\n\n if (req.method === 'OPTIONS') {\n // For OPTIONS requests, just return a 200 OK with CORS headers\n return res.status(200).send({});\n }\n\n if (isFromShard) {\n return this.handleShardRequest(req, res, shardId);\n }\n\n // Handle regular client request\n return this.handleDirectRequest(req, res);\n }\n\n\n /**\n * @method handleSessionRestore\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request for session restore\n * @param {ServerResponse} res - The response object\n * @description Handles session restoration from transfer data, creates session from privateId\n * @returns {Promise<Response>} The response to return to the client\n */\n private async handleSessionRestore(req: Party.Request, res: ServerResponse): Promise<Response> {\n try {\n const transferData = await req.json() as {\n privateId: string;\n userSnapshot?: any;\n sessionState?: any;\n publicId: string;\n };\n const { privateId, userSnapshot, sessionState, publicId } = transferData;\n\n if (!privateId || !publicId) {\n return res.badRequest('Missing privateId or publicId in transfer data');\n }\n\n const subRoom = await this.getSubRoom();\n if (!subRoom) {\n return res.serverError('Room not available');\n }\n\n // Create session from privateId\n await this.saveSession(privateId, {\n publicId,\n state: sessionState,\n created: Date.now(),\n connected: false // Will be set to true when user connects\n });\n\n // If userSnapshot exists, restore user data\n if (userSnapshot) {\n const signal = this.getUsersProperty(subRoom);\n const usersPropName = this.getUsersPropName(subRoom);\n \n if (signal && usersPropName) {\n const { classType } = signal.options;\n \n // Create new user instance\n const user = isClass(classType) ? new classType() : classType();\n \n // Load user data from snapshot\n load(user, userSnapshot, true);\n \n // Add user to signal\n signal()[publicId] = user;\n\n // Save user snapshot to storage\n await this.room.storage.put(`${usersPropName}.${publicId}`, userSnapshot);\n }\n }\n\n // Generate transfer token for the client to use when connecting\n const transferToken = generateShortUUID();\n await this.room.storage.put(`transfer:${transferToken}`, {\n privateId,\n publicId,\n restored: true\n });\n\n return res.success({ transferToken });\n } catch (error) {\n console.error('Error restoring session:', error);\n return res.serverError('Failed to restore session');\n }\n }\n\n /**\n * @method handleDirectRequest\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request received directly from a client\n * @description Processes requests received directly from clients\n * @returns {Promise<Response>} The response to return to the client\n */\n private async handleDirectRequest(req: Party.Request, res: ServerResponse): Promise<Response> {\n const subRoom = await this.getSubRoom();\n\n if (!subRoom) {\n return res.notFound();\n }\n\n const url = new URL(req.url);\n if (url.pathname.endsWith('/session-transfer') && req.method === 'POST') {\n return this.handleSessionRestore(req, res);\n }\n\n // First try to match using the registered @Request handlers\n const response = await this.tryMatchRequestHandler(req, res, subRoom);\n if (response) {\n return response;\n }\n\n // Fall back to the legacy onRequest method if no handler matched\n const legacyResponse = await awaitReturn(subRoom[\"onRequest\"]?.(req, res));\n if (!legacyResponse) {\n return res.notFound();\n }\n if (legacyResponse instanceof Response) {\n return legacyResponse;\n }\n return res.success(legacyResponse);\n }\n\n /**\n * @method tryMatchRequestHandler\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request to handle\n * @param {Object} subRoom - The room instance\n * @description Attempts to match the request to a registered @Request handler\n * @returns {Promise<Response | null>} The response or null if no handler matched\n */\n private async tryMatchRequestHandler(req: Party.Request, res: ServerResponse, subRoom: any): Promise<Response | null> {\n const requestHandlers = subRoom.constructor[\"_requestMetadata\"];\n if (!requestHandlers) {\n return null;\n }\n\n const url = new URL(req.url);\n const method = req.method;\n let pathname = url.pathname;\n\n pathname = '/' + pathname.split('/').slice(4).join('/');\n\n // Check each registered handler\n for (const [routeKey, handler] of requestHandlers.entries()) {\n const firstColonIndex = routeKey.indexOf(':');\n const handlerMethod = routeKey.substring(0, firstColonIndex);\n const handlerPath = routeKey.substring(firstColonIndex + 1);\n\n // Check if method matches\n if (handlerMethod !== method) {\n continue;\n }\n\n // Simple path matching (could be enhanced with path params)\n if (this.pathMatches(pathname, handlerPath)) {\n // Extract path params if any\n const params = this.extractPathParams(pathname, handlerPath);\n // Check request guards if they exist\n const guards = subRoom.constructor['_actionGuards']?.get(handler.key) || [];\n for (const guard of guards) {\n const isAuthorized = await guard(null, req, this.room);\n if (isAuthorized instanceof Response) {\n return isAuthorized;\n }\n if (!isAuthorized) {\n return res.notPermitted();\n }\n }\n\n // Validate request body if needed\n let bodyData = null;\n if (handler.bodyValidation && ['POST', 'PUT', 'PATCH'].includes(method)) {\n try {\n const contentType = req.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n const body = await req.json();\n const validation = handler.bodyValidation.safeParse(body);\n if (!validation.success) {\n return res.badRequest(\"Invalid request body\", {\n details: validation.error\n });\n }\n bodyData = validation.data;\n }\n } catch (error) {\n return res.badRequest(\"Failed to parse request body\");\n }\n }\n\n // Execute the handler method\n try {\n req['data'] = bodyData;\n req['params'] = params;\n const result = await awaitReturn(\n subRoom[handler.key](req, res)\n );\n\n if (result instanceof Response) {\n return result;\n }\n\n return res.success(result);\n } catch (error) {\n console.error('Error executing request handler:', error);\n return res.serverError();\n }\n }\n }\n\n return null;\n }\n\n /**\n * @method pathMatches\n * @private\n * @param {string} requestPath - The path from the request\n * @param {string} handlerPath - The path pattern from the handler\n * @description Checks if a request path matches a handler path pattern\n * @returns {boolean} True if the paths match\n */\n private pathMatches(requestPath: string, handlerPath: string): boolean {\n // Convert handler path pattern to regex\n // Replace :param with named capture groups\n const pathRegexString = handlerPath\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/:([^\\/]+)/g, '([^/]+)'); // Convert :params to capture groups\n\n const pathRegex = new RegExp(`^${pathRegexString}`);\n return pathRegex.test(requestPath);\n }\n\n /**\n * @method extractPathParams\n * @private\n * @param {string} requestPath - The path from the request\n * @param {string} handlerPath - The path pattern from the handler\n * @description Extracts path parameters from the request path based on the handler pattern\n * @returns {Object} An object containing the path parameters\n */\n private extractPathParams(requestPath: string, handlerPath: string): Record<string, string> {\n const params: Record<string, string> = {};\n\n // Extract parameter names from handler path\n const paramNames: string[] = [];\n handlerPath.split('/').forEach(segment => {\n if (segment.startsWith(':')) {\n paramNames.push(segment.substring(1));\n }\n });\n\n // Extract parameter values from request path\n const pathRegexString = handlerPath\n .replace(/\\//g, '\\\\/') // Escape slashes\n .replace(/:([^\\/]+)/g, '([^/]+)'); // Convert :params to capture groups\n\n const pathRegex = new RegExp(`^${pathRegexString}`);\n const matches = requestPath.match(pathRegex);\n\n if (matches && matches.length > 1) {\n // Skip the first match (the full string)\n for (let i = 0; i < paramNames.length; i++) {\n params[paramNames[i]] = matches[i + 1];\n }\n }\n\n return params;\n }\n\n /**\n * @method handleShardRequest\n * @private\n * @async\n * @param {Party.Request} req - The HTTP request forwarded by a shard\n * @param {string | null} shardId - The ID of the shard that forwarded the request\n * @description Processes requests forwarded by shards, preserving client context\n * @returns {Promise<Response>} The response to return to the shard (which will forward it to the client)\n */\n private async handleShardRequest(req: Party.Request, res: ServerResponse, shardId: string | null): Promise<Response> {\n const subRoom = await this.getSubRoom();\n\n if (!subRoom) {\n return res.notFound();\n }\n\n // Create a context that preserves original client information\n const originalClientIp = req.headers.get('x-original-client-ip');\n const enhancedReq = this.createEnhancedRequest(req, originalClientIp);\n \n try {\n // First try to match using the registered @Request handlers\n const response = await this.tryMatchRequestHandler(enhancedReq, res, subRoom);\n if (response) {\n return response;\n }\n\n // Fall back to the legacy onRequest handler\n const legacyResponse = await awaitReturn(subRoom[\"onRequest\"]?.(enhancedReq, res));\n\n if (!legacyResponse) {\n return res.notFound();\n }\n\n if (legacyResponse instanceof Response) {\n return legacyResponse;\n }\n\n return res.success(legacyResponse);\n } catch (error) {\n console.error(`Error processing request from shard ${shardId}:`, error);\n return res.serverError();\n }\n }\n\n /**\n * @method createEnhancedRequest\n * @private\n * @param {Party.Request} originalReq - The original request received from the shard\n * @param {string | null} originalClientIp - The original client IP, if available\n * @description Creates an enhanced request object that preserves the original client context\n * @returns {Party.Request} The enhanced request object\n */\n private createEnhancedRequest(originalReq: Party.Request, originalClientIp: string | null): Party.Request {\n // Clone the original request to avoid mutating it\n const clonedReq = originalReq.clone();\n\n // Add a custom property to the request to indicate it came via a shard\n (clonedReq as any).viaShard = true;\n\n // If we have the original client IP, we can use it for things like rate limiting or geolocation\n if (originalClientIp) {\n (clonedReq as any).originalClientIp = originalClientIp;\n }\n\n return clonedReq;\n }\n}\n","import { dset } from \"dset\";\n\n/**\n * Checks if a value is a Promise.\n *\n * @param {unknown} value - The value to check.\n * @returns {boolean} - Returns true if the value is a Promise, otherwise false.\n *\n * @example\n * isPromise(Promise.resolve()); // true\n * isPromise(42); // false\n */\nexport function isPromise(value: unknown): value is Promise<any> {\n return value instanceof Promise;\n}\n\n/**\n * Awaits the given value if it is a Promise, otherwise returns the value directly.\n *\n * @param {unknown} val - The value to await or return.\n * @returns {Promise<any>} - Returns a Promise that resolves to the value.\n *\n * @example\n * awaitReturn(Promise.resolve(42)); // 42\n * awaitReturn(42); // 42\n */\nexport async function awaitReturn(val: unknown): Promise<any> {\n return isPromise(val) ? await val : val;\n}\n\n/**\n * Checks if a value is a class.\n *\n * @param {unknown} obj - The value to check.\n * @returns {boolean} - Returns true if the value is a class, otherwise false.\n *\n * @example\n * class MyClass {}\n * isClass(MyClass); // true\n * isClass(() => {}); // false\n */\nexport function isClass(obj: unknown): boolean {\n return (\n typeof obj === \"function\" &&\n obj.prototype &&\n obj.prototype.constructor === obj\n );\n}\n\n\n/**\n * Creates a throttled function that only invokes the provided function at most once per every wait milliseconds.\n *\n * The throttled function comes with a cancel method to cancel delayed invocations.\n * If the throttled function is invoked more than once during the wait timeout,\n * it will call the provided function with the latest arguments.\n *\n * @template F - The type of the function to throttle.\n * @param {F} func - The function to throttle.\n * @param {number} wait - The number of milliseconds to throttle invocations to.\n * @returns {(...args: Parameters<F>) => void} - Returns the new throttled function.\n *\n * @example\n * const log = throttle((message) => console.log(message), 1000);\n * log(\"Hello\"); // Will log \"Hello\" immediately\n * log(\"World\"); // Will log \"World\" after 1 second, if no other calls to log() are made within the 1 second.\n */\nexport function throttle<F extends (...args: any[]) => any>(\n func: F,\n wait: number\n): (...args: Parameters<F>) => void {\n let timeout: ReturnType<typeof setTimeout> | null = null;\n let lastArgs: Parameters<F> | null = null;\n\n return function (...args: Parameters<F>) {\n if (!timeout) {\n func(...args);\n timeout = setTimeout(() => {\n if (lastArgs) {\n func(...lastArgs);\n lastArgs = null;\n }\n timeout = null;\n }, wait);\n } else {\n lastArgs = args;\n }\n };\n}\n\n/**\n * Extracts parameters from a given string based on a specified pattern.\n *\n * The pattern can include placeholders in the form of {paramName}, which will be\n * extracted from the input string if they match.\n *\n * @param {string} pattern - The pattern containing placeholders.\n * @param {string} str - The string to extract parameters from.\n * @returns {{ [key: string]: string } | null} - An object containing the extracted parameters,\n * or null if the string does not match the pattern.\n *\n * @example\n * // returns { id: '123' }\n * extractParams('game-{id}', 'game-123');\n *\n * @example\n * // returns { foo: 'abc', bar: 'xyz' }\n * extractParams('test-{foo}-{bar}', 'test-abc-xyz');\n *\n */\nexport function extractParams(\n pattern: string,\n str: string\n): { [key: string]: string } | null {\n // Replace placeholders in the pattern with named capture groups\n const regexPattern = pattern.replace(/{(\\w+)}/g, \"(?<$1>[\\\\w-]+)\");\n\n // Create a strict regular expression from the pattern\n const regex = new RegExp(`^${regexPattern}$`);\n const match = regex.exec(str);\n\n // If a match is found and groups are present, return the captured groups\n if (match && match.groups) {\n return match.groups;\n } else if (pattern === str) {\n // If the pattern exactly matches the string, return an empty object\n return {};\n } else {\n // Otherwise, return null\n return null;\n }\n}\n\n/**\n * Removes a property from an object based on a dot-separated key string or an array of keys.\n *\n * The function modifies the original object by deleting the specified property.\n * It safely handles dangerous keys like __proto__, constructor, and prototype.\n *\n * @param {Record<string, any>} obj - The object from which to remove the property.\n * @param {string | string[]} keys - The key(s) specifying the property to remove. Can be a dot-separated string or an array of strings.\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, 'a.b.c');\n * // obj is now { a: { b: {} } }\n *\n * @example\n * const obj = { a: 1, b: 2 };\n * dremove(obj, 'a');\n * // obj is now { b: 2 }\n *\n * @example\n * const obj = { a: { b: { c: 3 } } };\n * dremove(obj, ['a', 'b', 'c']);\n * // obj is now { a: { b: {} } }\n */\nexport function dremove(\n obj: Record<string, any>,\n keys: string | string[]\n): void {\n // If keys is a string, convert it to an array using the \".\" separator\n if (typeof keys === \"string\") {\n keys = keys.split(\".\");\n }\n\n let i = 0;\n const l = keys.length;\n let t = obj;\n let k;\n\n while (i < l - 1) {\n k = keys[i++];\n if (k === \"__proto__\" || k === \"constructor\" || k === \"prototype\") return; // Avoid dangerous keys\n if (typeof t[k] !== \"object\" || t[k] === null) return; // If the object doesn't exist, stop\n t = t[k];\n }\n\n k = keys[i];\n if (\n t &&\n typeof t === \"object\" &&\n !(k === \"__proto__\" || k === \"constructor\" || k === \"prototype\")\n ) {\n delete t[k];\n }\n}\n\n/**\n * Builds an object from a map of values and updates the provided memory object.\n *\n * For each key-value pair in the map, this function sets the value at the given path in the `memoryObj`.\n * If the value is \"$delete\", it removes the corresponding path from `allMemory`.\n *\n * @param {Map<string, any>} valuesMap - A map where the keys are paths and the values are the values to set at those paths.\n * @param {Record<string, any>} allMemory - The object to update based on the values in the map.\n * @returns {Record<string, any>} - The built memory object with the applied values from the map.\n *\n * @example\n * const valuesMap = new Map();\n * valuesMap.set('a.b.c', 1);\n * valuesMap.set('x.y.z', '$delete');\n * const allMemory = { x: { y: { z: 2 } } };\n * const result = buildObject(valuesMap, allMemory);\n * // result is { a: { b: { c: 1 } }, x: { y: { z: '$delete' } } }\n * // allMemory is { a: { b: { c: 1 } }, x: { y: {} } }\n */\nexport function buildObject(valuesMap: Map<string, any>, allMemory: Record<string, any>): Record<string, any> {\n let memoryObj = {};\n for (let path of valuesMap.keys()) {\n const value = valuesMap.get(path);\n dset(memoryObj, path, value);\n if (value === \"$delete\") {\n dremove(allMemory, path);\n } else {\n dset(allMemory, path, value);\n }\n }\n return memoryObj;\n}\n\nexport function response(status: number, body: any): Response {\n return new Response(JSON.stringify(body), { \n status,\n headers: { 'Content-Type': 'application/json' }\n });\n}","export class ServerResponse {\n private interceptors: ((res: Response) => Promise<Response> | Response)[];\n private statusCode: number = 200;\n private responseBody: any = {};\n private responseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json'\n };\n\n /**\n * Creates a new ServerResponse instance\n * @param interceptors Array of interceptor functions that can modify the response\n */\n constructor(interceptors: ((res: Response) => Promise<Response> | Response)[] = []) {\n this.interceptors = interceptors;\n }\n\n /**\n * Sets the status code for the response\n * @param code HTTP status code\n * @returns this instance for chaining\n */\n status(code: number): ServerResponse {\n this.statusCode = code;\n return this;\n }\n\n /**\n * Sets the response body and returns this instance (chainable method)\n * \n * @param body Response body\n * @returns this instance for chaining\n */\n body(body: any): ServerResponse {\n this.responseBody = body;\n return this;\n }\n\n /**\n * Adds a header to the response\n * @param name Header name\n * @param value Header value\n * @returns this instance for chaining\n */\n header(name: string, value: string): ServerResponse {\n this.responseHeaders[name] = value;\n return this;\n }\n\n /**\n * Adds multiple headers to the response\n * @param headers Object containing headers\n * @returns this instance for chaining\n */\n setHeaders(headers: Record<string, string>): ServerResponse {\n this.responseHeaders = { ...this.responseHeaders, ...headers };\n return this;\n }\n\n /**\n * Add an interceptor to the chain\n * @param interceptor Function that takes a Response and returns a modified Response\n * @returns this instance for chaining\n */\n use(interceptor: (res: Response) => Promise<Response> | Response): ServerResponse {\n this.interceptors.push(interceptor);\n return this;\n }\n\n /**\n * Builds and returns the Response object after applying all interceptors\n * @returns Promise<Response> The final Response object\n * @private Internal method used by terminal methods\n */\n private async buildResponse(): Promise<Response> {\n // Create initial response\n let response = new Response(JSON.stringify(this.responseBody), {\n status: this.statusCode,\n headers: this.responseHeaders\n });\n\n // Apply all interceptors sequentially\n for (const interceptor of this.interceptors) {\n try {\n const interceptedResponse = interceptor(response);\n if (interceptedResponse instanceof Promise) {\n response = await interceptedResponse;\n } else {\n response = interceptedResponse;\n }\n } catch (error) {\n console.error('Error in interceptor:', error);\n // Continue with the current response if an interceptor fails\n }\n }\n\n return response;\n }\n\n /**\n * Sets the response body to the JSON-stringified version of the provided value\n * and sends the response (terminal method)\n * \n * @param body Response body to be JSON stringified\n * @returns Promise<Response> The final Response object\n */\n async json(body: any): Promise<Response> {\n this.responseBody = body;\n this.responseHeaders['Content-Type'] = 'application/json';\n return this.buildResponse();\n }\n\n /**\n * Sends the response with the current configuration (terminal method)\n * \n * @param body Optional body to set before sending\n * @returns Promise<Response> The final Response object\n */\n async send(body?: any): Promise<Response> {\n if (body !== undefined) {\n this.responseBody = body;\n }\n return this.buildResponse();\n }\n\n /**\n * Sends a plain text response (terminal method)\n * \n * @param text Text to send\n * @returns Promise<Response> The final Response object\n */\n async text(text: string): Promise<Response> {\n this.responseBody = text;\n this.responseHeaders['Content-Type'] = 'text/plain';\n \n // Create a text response without JSON stringifying the body\n let response = new Response(text, {\n status: this.statusCode,\n headers: this.responseHeaders\n });\n \n // Apply interceptors\n for (const interceptor of this.interceptors) {\n try {\n const interceptedResponse = interceptor(response);\n if (interceptedResponse instanceof Promise) {\n response = await interceptedResponse;\n } else {\n response = interceptedResponse;\n }\n } catch (error) {\n console.error('Error in interceptor:', error);\n }\n }\n \n return response;\n }\n\n /**\n * Redirects to the specified URL (terminal method)\n * \n * @param url URL to redirect to\n * @param statusCode HTTP status code (default: 302)\n * @returns Promise<Response> The final Response object\n */\n async redirect(url: string, statusCode: number = 302): Promise<Response> {\n this.statusCode = statusCode;\n this.responseHeaders['Location'] = url;\n return this.buildResponse();\n }\n\n /**\n * Creates a success response with status 200\n * @param body Response body\n * @returns Promise<Response> The final Response object\n */\n async success(body: any = {}): Promise<Response> {\n return this.status(200).json(body);\n }\n\n /**\n * Creates an error response with status 400\n * @param message Error message\n * @param details Additional error details\n * @returns Promise<Response> The final Response object\n */\n async badRequest(message: string, details: any = {}): Promise<Response> {\n return this.status(400).json({\n error: message,\n ...details\n });\n }\n\n /**\n * Creates an error response with status 403\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async notPermitted(message: string = \"Not permitted\"): Promise<Response> {\n return this.status(403).json({ error: message });\n }\n\n /**\n * Creates an error response with status 401\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async unauthorized(message: string = \"Unauthorized\"): Promise<Response> {\n return this.status(401).json({ error: message });\n }\n\n /**\n * Creates an error response with status 404\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async notFound(message: string = \"Not found\"): Promise<Response> {\n return this.status(404).json({ error: message });\n }\n\n /**\n * Creates an error response with status 500\n * @param message Error message\n * @returns Promise<Response> The final Response object\n */\n async serverError(message: string = \"Internal Server Error\"): Promise<Response> {\n return this.status(500).json({ error: message });\n }\n}","export function cors(res: Response, options: CorsOptions = {}) {\n const newHeaders = new Headers(res.headers);\n \n // Set default CORS headers\n const requestOrigin = options.origin || '*';\n newHeaders.set('Access-Control-Allow-Origin', requestOrigin);\n \n if (options.credentials) {\n newHeaders.set('Access-Control-Allow-Credentials', 'true');\n }\n \n if (options.exposedHeaders && options.exposedHeaders.length) {\n newHeaders.set('Access-Control-Expose-Headers', options.exposedHeaders.join(', '));\n }\n \n // Handle preflight requests\n if (options.methods && options.methods.length) {\n newHeaders.set('Access-Control-Allow-Methods', options.methods.join(', '));\n } else {\n newHeaders.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');\n }\n \n if (options.allowedHeaders && options.allowedHeaders.length) {\n newHeaders.set('Access-Control-Allow-Headers', options.allowedHeaders.join(', '));\n } else {\n newHeaders.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');\n }\n \n if (options.maxAge) {\n newHeaders.set('Access-Control-Max-Age', options.maxAge.toString());\n } else {\n // Default max-age to 86400 seconds (24 hours)\n newHeaders.set('Access-Control-Max-Age', '86400');\n }\n \n return new Response(res.body, {\n status: res.status,\n headers: newHeaders\n });\n}\n\nexport interface CorsOptions {\n origin?: string;\n methods?: string[];\n allowedHeaders?: string[];\n exposedHeaders?: string[];\n credentials?: boolean;\n maxAge?: number;\n}\n\n/**\n * Creates a CORS interceptor with the specified options\n * @param options CORS configuration options\n * @returns An interceptor function that can be used with ServerResponse\n */\nexport function createCorsInterceptor(options: CorsOptions = {}): (res: Response) => Response {\n return (res: Response) => cors(res, options);\n}","import type * as Party from \"./types/party\";\nimport { response } from \"./utils\";\n\ninterface PartyWebSocket {\n send: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => void;\n addEventListener: (type: string, listener: (event: any) => void) => void;\n close: () => void;\n}\n\nexport interface ShardOptions {\n worldUrl?: string;\n worldId?: string;\n statsInterval?: number;\n}\n\nexport class Shard {\n ws: PartyWebSocket;\n connectionMap = new Map<string, Party.Connection>(); // Map privateId -> connection\n mainServerStub: any;\n worldUrl: string | null = null;\n worldId: string = 'default';\n lastReportedConnections: number = 0;\n statsInterval: number = 30000; \n statsIntervalId: any = null;\n\n constructor(private room: Party.Room) {}\n\n async onStart() {\n const roomId = this.room.id.split(':')[0];\n const roomStub = this.room.context.parties.main.get(roomId);\n if (!roomStub) {\n console.warn('No room room stub found in main party context');\n return;\n }\n \n this.mainServerStub = roomStub;\n this.ws = await roomStub.socket({\n headers: {\n 'x-shard-id': this.room.id\n }\n }) as unknown as PartyWebSocket;\n \n // Handle messages from the main server\n this.ws.addEventListener(\"message\", (event) => {\n try {\n const message = JSON.parse(event.data);\n\n // If the message is directed to a specific client, forward it\n if (message.targetClientId) {\n const clientConn = this.connectionMap.get(message.targetClientId);\n if (clientConn) {\n // Remove the routing information before forwarding\n delete message.targetClientId;\n clientConn.send(message.data);\n }\n } else {\n // Broadcast to all clients if no specific target\n this.room.broadcast(event.data);\n }\n } catch (error) {\n console.error(\"Error processing message from main server:\", error);\n }\n });\n\n await this.updateWorldStats();\n this.startPeriodicStatsUpdates();\n }\n\n private startPeriodicStatsUpdates() {\n if (!this.worldUrl) {\n return;\n }\n\n if (this.statsIntervalId) {\n clearInterval(this.statsIntervalId);\n }\n\n this.statsIntervalId = setInterval(() => {\n this.updateWorldStats().catch(error => {\n console.error('Error in periodic stats update:', error);\n });\n }, this.statsInterval);\n }\n\n private stopPeriodicStatsUpdates() {\n if (this.statsIntervalId) {\n clearInterval(this.statsIntervalId);\n this.statsIntervalId = null;\n }\n }\n\n onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {\n // Store connection mapping\n this.connectionMap.set(conn.id, conn);\n \n // Capture all headers and request information\n const headers: Record<string, string> = {};\n if (ctx.request?.headers) {\n ctx.request.headers.forEach((value, key) => {\n headers[key] = value;\n });\n }\n\n // Prepare connection context information\n const requestInfo = ctx.request ? {\n headers,\n url: ctx.request.url,\n method: ctx.request.method\n } : null;\n\n // Notify the main server about the new connection with complete connection metadata\n this.ws.send(JSON.stringify({\n type: 'shard.clientConnected',\n privateId: conn.id,\n requestInfo\n }));\n\n this.updateWorldStats();\n }\n\n onMessage(message: string | ArrayBuffer | ArrayBufferView, sender: Party.Connection) {\n try {\n // Parse the message if it's a string\n const parsedMessage = typeof message === 'string' ? JSON.parse(message) : message;\n \n // Wrap the original message with sender information\n const wrappedMessage = JSON.stringify({\n type: 'shard.clientMessage',\n privateId: sender.id,\n publicId: (sender.state as any)?.publicId,\n payload: parsedMessage\n });\n \n // Forward to main server\n this.ws.send(wrappedMessage);\n } catch (error) {\n console.error(\"Error forwarding message to main server:\", error);\n }\n }\n\n onClose(conn: Party.Connection) {\n // Remove connection from the map\n this.connectionMap.delete(conn.id);\n \n // Notify main server about disconnection\n this.ws.send(JSON.stringify({\n type: 'shard.clientDisconnected',\n privateId: conn.id,\n publicId: (conn.state as any)?.publicId\n }));\n\n this.updateWorldStats();\n }\n\n async updateWorldStats(): Promise<boolean> {\n const currentConnections = this.connectionMap.size;\n \n if (currentConnections === this.lastReportedConnections) {\n return true;\n }\n\n try {\n const worldRoom = this.room.context.parties.world.get('world-default');\n const response = await worldRoom.fetch('/update-shard', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-access-shard': this.room.env.SHARD_SECRET as string\n },\n body: JSON.stringify({\n shardId: this.room.id,\n connections: currentConnections\n })\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));\n console.error(`Failed to update World stats: ${response.status} - ${errorData.error || 'Unknown error'}`);\n return false;\n }\n\n // Mettre à jour le dernier nombre rapporté\n this.lastReportedConnections = currentConnections;\n return true;\n } catch (error) {\n console.error('Error updating World stats:', error);\n return false;\n }\n }\n\n /**\n * @method onRequest\n * @async\n * @param {Party.Request} req - The HTTP request to handle\n * @description Forwards HTTP requests to the main server, preserving client context\n * @returns {Promise<Response>} The response from the main server\n */\n async onRequest(req: Party.Request): Promise<Response> {\n if (!this.mainServerStub) {\n return response(503, { error: 'Shard not connected to main server' });\n }\n\n try {\n // Extract necessary information from the request\n const url = new URL(req.url);\n const path = url.pathname;\n const method = req.method;\n let body: string | null = null;\n \n if (method !== 'GET' && method !== 'HEAD') {\n body = await req.text();\n }\n \n // Convert original headers to a new Headers object\n const headers = new Headers();\n req.headers.forEach((value, key) => {\n headers.set(key, value);\n });\n \n // Add shard identification\n headers.set('x-shard-id', this.room.id);\n headers.set('x-forwarded-by-shard', 'true');\n \n // Client IP tracking for the main server\n const clientIp = req.headers.get('x-forwarded-for') || 'unknown';\n if (clientIp) {\n headers.set('x-original-client-ip', clientIp);\n }\n \n // Prepare request options\n const requestInit: RequestInit = {\n method,\n headers,\n body\n };\n // Forward the request to the main server\n const response = await this.mainServerStub.fetch(path, requestInit);\n return response;\n } catch (error) {\n return response(500, { error: 'Error forwarding request' });\n }\n }\n \n /**\n * @method onAlarm\n * @async\n * @description Executed periodically, used to perform maintenance tasks\n */\n async onAlarm() {\n await this.updateWorldStats();\n }\n}\n\nShard satisfies Party.Worker;","import { ServerIo } from \"./mock\"\nimport { Server } from \"./server\"\nimport { Shard } from \"./shard\"\n\n/**\n * @description Test the room with a mock server and client\n * @param Room - The room class to test\n * @param options - The options for the room\n * @param options.hibernate - Whether to hibernate the server. If hybernate, room is null\n * @example\n * ```ts\n * const { createClient, room, server } = await testRoom(GameRoom)\n * const client1 = await createClient()\n * const client2 = await createClient()\n * \n * client1.addEventListener('message', (data) => {\n * console.log(data)\n * })\n * client2.addEventListener('message', (data) => {\n * console.log(data)\n * })\n * \n * await client1.send({\n * action: 'increment'\n * })\n * \n * ```\n * @returns The server, room, and createClient function\n */\nexport async function testRoom(Room, options: {\n hibernate?: boolean,\n shard?: boolean,\n env?: Record<string, string>,\n parties?: Record<string, (io: any) => any>,\n partyFn?: (io: any) => any\n} = {}) {\n\n const createServer = (io: any) => {\n const server = new Server(io)\n server.rooms = [Room]\n return server\n }\n\n const isShard = options.shard || false\n const io = new ServerIo(Room.path, isShard ? {\n parties: {\n game: createServer,\n ...(options.parties || {})\n },\n partyFn: options.partyFn,\n env: options.env\n } : {\n parties: options.parties,\n partyFn: options.partyFn,\n env: options.env\n })\n Room.prototype.throttleSync = 0\n Room.prototype.throttleStorage = 0\n Room.prototype.options = options\n \n let server: Server | Shard;\n if (options.shard) {\n const shardServer = new Shard(io as any);\n // Add subRoom property to Shard for compatibility with Server\n (shardServer as any).subRoom = null;\n server = shardServer;\n // In shard mode, parties.main is a Map of lobbies; ensure their servers are started\n if (io.context.parties.main instanceof Map) {\n for (const lobby of io.context.parties.main.values()) {\n await lobby.server.onStart();\n }\n }\n } else {\n server = await createServer(io as any);\n // If extra parties are provided in non-shard mode, start them too\n if (io.context.parties.main instanceof Map) {\n for (const lobby of io.context.parties.main.values()) {\n if (lobby.server && lobby.server !== server) {\n await lobby.server.onStart();\n }\n }\n }\n }\n \n await server.onStart()\n \n return {\n server,\n room: (server as any).subRoom,\n createClient: async (id?: string, opts?: { query?: Record<string, string>, headers?: Record<string, string> }) => {\n const client = await io.connection(server as Server, id, opts)\n return client\n },\n getServerUser: async (client: any, prop = 'users') => {\n const privateId = client.conn.id;\n const session = await (server as Server).getSession(privateId);\n return (server as any).subRoom[prop]()[session?.publicId];\n }\n }\n}\n\nexport async function request(room: Server | Shard, path: string, options: {\n method: 'GET' | 'POST' | 'PUT' | 'DELETE',\n body?: any,\n headers?: Record<string, string>\n} = {\n method: 'GET',\n}) {\n const url = new URL('http://localhost' + path)\n const request = new Request(url.toString(), options)\n const response = await room.onRequest(request as any)\n return response\n}\n\nexport function tick(ms: number = 0) {\n return new Promise(resolve => setTimeout(resolve, ms))\n}","import { generateShortUUID } from \"../../sync/src/utils\";\nimport { Server } from \"./server\";\nimport { Storage } from \"./storage\";\nimport { request } from \"./testing\";\n\nexport class MockPartyClient {\n private events: Map<string, Function[]> = new Map();\n id : string\n conn: MockConnection;\n\n constructor(public server: Server, id?: string) {\n this.id = id || generateShortUUID()\n this.conn = new MockConnection(this)\n }\n \n addEventListener(event, cb) {\n if (!this.events.has(event)) {\n this.events.set(event, []);\n }\n this.events.get(event).push(cb);\n }\n\n removeEventListener(event, cb) {\n if (!this.events.has(event)) return;\n const callbacks = this.events.get(event);\n const index = callbacks.indexOf(cb);\n if (index !== -1) {\n callbacks.splice(index, 1);\n }\n if (callbacks.length === 0) {\n this.events.delete(event);\n }\n }\n\n _trigger(event, data) {\n const callbacks = this.events.get(event);\n if (callbacks) {\n for (const cb of callbacks) {\n cb(data);\n }\n }\n }\n\n send(data) {\n return this.server.onMessage(JSON.stringify(data), this.conn as any)\n }\n}\n\nclass MockLobby {\n constructor(public server: Server, public lobbyId: string) {}\n\n socket(_init?: any) {\n return new MockPartyClient(this.server)\n }\n\n async connection(idOrOptions?: string | { id?: string, query?: Record<string, string>, headers?: Record<string, string> }, maybeOptions?: { query?: Record<string, string>, headers?: Record<string, string> }) {\n const id = typeof idOrOptions === 'string' ? idOrOptions : idOrOptions?.id;\n const options = (typeof idOrOptions === 'string' ? maybeOptions : idOrOptions) || {};\n return (this.server.room as any).connection(this.server, id, options as any);\n }\n\n fetch(url: string, options: any) {\n const baseUrl = url.includes('shard') ? '' :( '/parties/main/' + this.lobbyId )\n return request(this.server, baseUrl + url, options)\n }\n}\n\ninterface MockContextOptions {\n parties?: any;\n partyFn?: (roomId: string) => any;\n}\n\nclass MockContext {\n parties: {\n main: any\n } = {\n main: new Map()\n }\n\n constructor(public room: MockPartyRoom, options: MockContextOptions = {}) {\n const parties = options.parties || {}\n if (options.partyFn) {\n const serverCache = new Map<string, MockLobby>();\n this.parties.main = {\n get: async (lobbyId: string) => {\n if (!serverCache.has(lobbyId)) {\n const server = await options.partyFn(lobbyId);\n serverCache.set(lobbyId, new MockLobby(server, lobbyId));\n }\n return serverCache.get(lobbyId)!;\n }\n }\n }\n else {\n for (let lobbyId in parties) {\n const server = parties[lobbyId](room)\n ;(this.parties.main as Map<string, any>).set(lobbyId, new MockLobby(server, lobbyId))\n }\n }\n }\n}\n\nclass MockPartyRoom {\n clients: Map<string, MockPartyClient> = new Map();\n storage = new Storage();\n context: MockContext;\n env = {}\n\n constructor(public id?: string, options: any = {}) {\n this.id = id || generateShortUUID()\n this.context = new MockContext(this, {\n parties: options.parties,\n partyFn: options.partyFn\n })\n this.env = options.env || {}\n }\n\n async connection(server: Server, id?: string, opts?: { query?: Record<string, string>, headers?: Record<string, string> }) {\n const socket = new MockPartyClient(server, id);\n const url = new URL('http://localhost')\n if (opts?.query) {\n for (const [key, value] of Object.entries(opts.query)) {\n url.searchParams.set(key, String(value))\n }\n }\n const request = new Request(url.toString(), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n ...(opts?.headers || {})\n }\n })\n await server.onConnect(socket.conn as any, { request } as any);\n this.clients.set(socket.id, socket);\n return socket\n }\n\n broadcast(data: any) {\n this.clients.forEach((client) => {\n client._trigger('message', data);\n });\n }\n\n getConnection(id: string) {\n return this.clients.get(id)\n }\n\n getConnections() {\n return Array.from(this.clients.values()).map((client) => client.conn); \n }\n\n clear() {\n this.clients.clear();\n }\n}\n\nexport class MockConnection {\n server: Server;\n id: string;\n\n constructor(public client: MockPartyClient) {\n this.server = client.server\n this.id = client.id\n }\n\n state: any = {};\n\n setState(value: any) {\n this.state = value;\n }\n\n send(data: any) {\n this.client._trigger('message', data)\n }\n\n close() {\n this.server.onClose(this as any)\n }\n}\n\nexport const ServerIo = MockPartyRoom;\nexport const ClientIo = MockPartyClient;\n","import { signal } from \"@signe/reactive\";\nimport { Room, Action, Guard, Request } from \"./decorators\";\nimport { sync, id, persist } from \"@signe/sync\";\nimport { z } from \"zod\";\nimport * as Party from \"./types/party\";\nimport { guardManageWorld } from \"./world.guard\";\nimport { response } from \"./utils\";\nimport { RoomInterceptorPacket, RoomOnJoin } from \"./interfaces\";\nimport { ServerResponse } from \"./request/response\";\n\n// Types definitions\ntype BalancingStrategy = 'round-robin' | 'least-connections' | 'random';\ntype ShardStatus = 'active' | 'maintenance' | 'draining';\n\nconst MAX_PLAYERS_PER_SHARD = 75;\n\n// Schema validations\nconst RoomConfigSchema = z.object({\n name: z.string(),\n balancingStrategy: z.enum(['round-robin', 'least-connections', 'random']),\n public: z.boolean(),\n maxPlayersPerShard: z.number().int().positive(),\n minShards: z.number().int().min(0),\n maxShards: z.number().int().positive().optional(),\n});\n\nconst RegisterShardSchema = z.object({\n shardId: z.string(),\n roomId: z.string(),\n url: z.string().url(),\n maxConnections: z.number().int().positive(),\n});\n\nconst UpdateShardStatsSchema = z.object({\n connections: z.number().int().min(0),\n status: z.enum(['active', 'maintenance', 'draining']).optional(),\n});\n\nconst ScaleRoomSchema = z.object({\n roomId: z.string(),\n targetShardCount: z.number().int().positive(),\n shardTemplate: z.object({\n urlTemplate: z.string(),\n maxConnections: z.number().int().positive(),\n }).optional(),\n});\n\n// Model classes\nclass RoomConfig {\n @id() id: string;\n @sync() name = signal(\"\");\n @sync() balancingStrategy = signal<BalancingStrategy>(\"round-robin\");\n @sync() public = signal(true);\n @sync() maxPlayersPerShard = signal(MAX_PLAYERS_PER_SHARD);\n @sync() minShards = signal(1);\n @sync() maxShards = signal<number | undefined>(undefined);\n}\n\nclass ShardInfo {\n @id() id: string;\n @sync() roomId = signal(\"\");\n @sync() url = signal(\"\");\n @sync({\n persist: false\n }) currentConnections = signal(0);\n @sync() maxConnections = signal(MAX_PLAYERS_PER_SHARD);\n @sync() status = signal<ShardStatus>(\"active\");\n @sync() lastHeartbeat = signal(0);\n}\n\n// World room implementation\n@Room({\n path: \"world-{worldId}\",\n maxUsers: 100, // Limit for admin connections\n throttleStorage: 2000, // Throttle storage updates (ms)\n throttleSync: 500, // Throttle sync updates (ms)\n})\nexport class WorldRoom implements RoomInterceptorPacket, RoomOnJoin {\n // Synchronized state\n @sync(RoomConfig) rooms = signal<Record<string, RoomConfig>>({});\n @sync(ShardInfo) shards = signal<Record<string, ShardInfo>>({});\n \n // Only persisted state (not synced to clients)\n @persist() rrCounters = signal<Record<string, number>>({});\n \n // Configuration\n defaultShardUrlTemplate = signal(\"{shardId}\");\n defaultMaxConnectionsPerShard = signal(MAX_PLAYERS_PER_SHARD);\n\n constructor(private room: Party.Room) {\n const { AUTH_JWT_SECRET, SHARD_SECRET } = this.room.env;\n if (!AUTH_JWT_SECRET) {\n throw new Error(\"AUTH_JWT_SECRET env variable is not set\");\n }\n if (!SHARD_SECRET) {\n throw new Error(\"SHARD_SECRET env variable is not set\");\n }\n // TODO\n //setTimeout(() => this.cleanupInactiveShards(), 60000);\n }\n\n async onJoin(user: any, conn: Party.Connection, ctx: Party.ConnectionContext) {\n const canConnect = await guardManageWorld(user, ctx.request, this.room);\n conn.setState({\n ...conn.state,\n isAdmin: canConnect\n })\n }\n\n interceptorPacket(_, obj: any, conn: Party.Connection) {\n if (!conn.state['isAdmin']) {\n return null;\n }\n return obj;\n }\n \n // Helper methods\n private cleanupInactiveShards() {\n const now = Date.now();\n const timeout = 5 * 60 * 1000; // 5 minutes timeout\n const shardsValue = this.shards();\n \n let hasChanges = false;\n Object.values(shardsValue).forEach(shard => {\n if (now - shard.lastHeartbeat() > timeout) {\n delete this.shards()[shard.id];\n \n hasChanges = true;\n }\n });\n \n // Schedule next cleanup\n setTimeout(() => this.cleanupInactiveShards(), 60000);\n }\n \n // Actions\n @Request({\n path: 'register-room',\n method: 'POST',\n })\n @Guard([guardManageWorld])\n async registerRoom(req: Party.Request) {\n const roomConfig: z.infer<typeof RoomConfigSchema> = await req.json();\n const roomId = roomConfig.name;\n \n if (!this.rooms()[roomId]) {\n const newRoom = new RoomConfig();\n newRoom.id = roomId;\n newRoom.name.set(roomConfig.name);\n newRoom.balancingStrategy.set(roomConfig.balancingStrategy);\n newRoom.public.set(roomConfig.public);\n newRoom.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);\n newRoom.minShards.set(roomConfig.minShards);\n newRoom.maxShards.set(roomConfig.maxShards);\n \n this.rooms()[roomId] = newRoom;\n \n // Ensure minimum shards are created\n if (roomConfig.minShards > 0) {\n for (let i = 0; i < roomConfig.minShards; i++) {\n await this.createShard(roomId);\n }\n }\n } else {\n // Update existing room\n const room = this.rooms()[roomId];\n room.balancingStrategy.set(roomConfig.balancingStrategy);\n room.public.set(roomConfig.public);\n room.maxPlayersPerShard.set(roomConfig.maxPlayersPerShard);\n room.minShards.set(roomConfig.minShards);\n room.maxShards.set(roomConfig.maxShards);\n }\n }\n \n @Request({\n path: 'update-shard',\n method: 'POST',\n })\n @Guard([guardManageWorld])\n async updateShardStats(req: Party.Request, res: ServerResponse) {\n const body: { shardId: string; connections: number; status: ShardStatus } = await req.json();\n const { shardId, connections, status } = body;\n const shard = this.shards()[shardId];\n\n if (!shard) {\n return res.notFound(`Shard ${shardId} not found`);\n }\n \n shard.currentConnections.set(connections);\n if (status) {\n shard.status.set(status);\n }\n shard.lastHeartbeat.set(Date.now());\n }\n \n @Request({\n path: 'scale-room',\n method: 'POST',\n })\n @Guard([guardManageWorld])\n async scaleRoom(req: Party.Request, res: ServerResponse) {\n const data: z.infer<typeof ScaleRoomSchema> = await req.json();\n const { targetShardCount, shardTemplate, roomId } = data;\n \n // Validate room exists\n const room = this.rooms()[roomId];\n if (!room) {\n return res.notFound(`Room ${roomId} does not exist`);\n }\n \n const roomShards = Object.values(this.shards())\n .filter(shard => shard.roomId() === roomId);\n \n const previousShardCount = roomShards.length;\n \n // Check max shards constraint\n if (room.maxShards() !== undefined && targetShardCount > room.maxShards()!) {\n return res.badRequest(`Cannot scale beyond maximum allowed shards (${room.maxShards()})`, {\n roomId,\n currentShardCount: previousShardCount\n });\n }\n \n // Handle scaling down\n if (targetShardCount < previousShardCount) {\n // Find candidates for removal (prioritize draining or low-connection shards)\n const shardsToRemove = [...roomShards]\n .sort((a, b) => {\n // Prioritize draining status\n if (a.status() === 'draining' && b.status() !== 'draining') return -1;\n if (a.status() !== 'draining' && b.status() === 'draining') return 1;\n \n // Then by connection count (ascending)\n return a.currentConnections() - b.currentConnections();\n })\n .slice(0, previousShardCount - targetShardCount);\n \n // Remove the selected shards\n const shardsToKeep = roomShards.filter(\n shard => !shardsToRemove.some(s => s.id === shard.id)\n );\n \n // Update shards\n for (const shard of shardsToRemove) {\n delete this.shards()[shard.id];\n }\n \n return;\n }\n \n // Handle scaling up\n if (targetShardCount > previousShardCount) {\n const newShards = [];\n \n // Create new shards\n for (let i = 0; i < targetShardCount - previousShardCount; i++) {\n const newShard = await this.createShard(\n roomId,\n shardTemplate?.urlTemplate,\n shardTemplate?.maxConnections\n );\n \n if (newShard) {\n newShards.push(newShard);\n }\n }\n }\n }\n\n @Request({\n path: 'connect',\n method: 'POST',\n })\n async connect(req: Party.Request, res: ServerResponse) {\n try {\n // Extract request data\n let data: { roomId: string; autoCreate?: boolean };\n \n try {\n // Handle potential empty body or malformed JSON\n const body = await req.text();\n if (!body || body.trim() === '') {\n return res.badRequest(\"Request body is empty\");\n }\n \n data = JSON.parse(body);\n } catch (parseError) {\n return res.badRequest(\"Invalid JSON in request body\");\n }\n \n // Verify roomId is provided\n if (!data.roomId) {\n return res.badRequest(\"roomId parameter is required\");\n }\n \n // Determine if auto-creation is enabled (default to true)\n const autoCreate = data.autoCreate !== undefined ? data.autoCreate : true;\n \n // Find optimal shard\n const result = await this.findOptimalShard(data.roomId, autoCreate);\n \n // Check for errors\n if ('error' in result) {\n return res.notFound(result.error);\n }\n \n // Return shard information to the client\n return res.success({\n success: true,\n shardId: result.shardId,\n url: result.url\n });\n } catch (error) {\n console.error('Error connecting to shard:', error);\n return res.serverError();\n }\n }\n \n private async findOptimalShard(\n roomId: string, \n autoCreate: boolean = true\n ): Promise<{ shardId: string; url: string } | { error: string }> {\n // Ensure room exists\n let room = this.rooms()[roomId];\n if (!room) {\n if (autoCreate) {\n const mockRequest = {\n json: async () => ({\n name: roomId,\n balancingStrategy: 'round-robin',\n public: true,\n maxPlayersPerShard: this.defaultMaxConnectionsPerShard(),\n minShards: 1,\n maxShards: undefined\n })\n } as Party.Request;\n \n await this.registerRoom(mockRequest);\n \n room = this.rooms()[roomId];\n \n if (!room) {\n return { error: `Failed to create room ${roomId}` };\n }\n } else {\n return { error: `Room ${roomId} does not exist` };\n }\n }\n \n // Get shards for this room\n const roomShards = Object.values(this.shards())\n .filter(shard => shard.roomId() === roomId);\n \n if (roomShards.length === 0) {\n if (autoCreate) {\n // Auto-create a shard\n const newShard = await this.createShard(roomId);\n if (newShard) {\n return {\n shardId: newShard.id,\n url: newShard.url()\n };\n } else {\n return { error: `Failed to create shard for room ${roomId}` };\n }\n } else {\n return { error: `No shards available for room ${roomId}` };\n }\n }\n \n // Get active shards\n const activeShards = roomShards\n .filter(shard => shard && shard.status() === 'active');\n \n if (activeShards.length === 0) {\n return { error: `No active shards available for room ${roomId}` };\n }\n \n // Apply balancing strategy\n const balancingStrategy = room.balancingStrategy();\n let selectedShard: ShardInfo;\n \n switch (balancingStrategy) {\n case 'least-connections':\n // Choose shard with fewest connections\n selectedShard = activeShards.reduce(\n (min, shard) => \n shard.currentConnections() < min.currentConnections() ? shard : min,\n activeShards[0]\n );\n break;\n \n case 'random':\n // Choose random shard\n selectedShard = activeShards[Math.floor(Math.random() * activeShards.length)];\n break;\n \n case 'round-robin':\n default:\n // Round-robin selection\n const counter = this.rrCounters()[roomId] || 0;\n const nextCounter = (counter + 1) % activeShards.length;\n this.rrCounters()[roomId] = nextCounter;\n \n selectedShard = activeShards[counter];\n break;\n }\n \n return {\n shardId: selectedShard.id,\n url: selectedShard.url()\n };\n }\n \n // Private methods\n private async createShard(\n roomId: string,\n urlTemplate?: string,\n maxConnections?: number\n ): Promise<ShardInfo | null> {\n const room = this.rooms()[roomId];\n if (!room) {\n console.error(`Cannot create shard for non-existent room: ${roomId}`);\n return null;\n }\n \n // Generate shard ID\n const shardId = `${roomId}:${Date.now()}-${Math.floor(Math.random() * 10000)}`;\n \n // Generate URL from template\n const template = urlTemplate || this.defaultShardUrlTemplate();\n const url = template.replace('{shardId}', shardId).replace('{roomId}', roomId);\n\n // Set max connections\n const max = maxConnections || room.maxPlayersPerShard();\n \n // Create the shard\n const newShard = new ShardInfo();\n newShard.id = shardId;\n newShard.roomId.set(roomId);\n newShard.url.set(url);\n newShard.maxConnections.set(max);\n newShard.currentConnections.set(0);\n newShard.status.set(\"active\");\n newShard.lastHeartbeat.set(Date.now());\n \n // Update shards collection\n this.shards()[shardId] = newShard;\n return newShard;\n }\n}","import type {\n AnalyticsEngineDataset,\n ExecutionContext as CFExecutionContext,\n Request as CFRequest,\n DurableObjectState,\n DurableObjectStorage,\n KVNamespace,\n R2Bucket,\n ScheduledController,\n VectorizeIndex,\n WebSocket\n} from \"@cloudflare/workers-types\";\n \n export type StaticAssetsManifestType = {\n devServer: string;\n browserTTL: number | null | undefined;\n edgeTTL: number | null | undefined;\n singlePageApp: boolean | undefined;\n assets: Record<string, string>;\n assetInfo?: Record<\n string,\n {\n fileSize: number;\n fileHash: string;\n fileName: string;\n }\n >;\n };\n \n type AssetFetcher = {\n fetch(path: string): Promise<Response | null>;\n };\n \n type StandardRequest = globalThis.Request;\n \n // Types with PartyKit* prefix are used in module workers, i.e.\n // `export default {} satisfies PartyKitServer;`\n \n // Types with Party.* prefix are used in class workers, i.e.\n // `export default class Room implements Party.Server {}`\n \n // Extend type so that when language server (e.g. vscode) autocompletes,\n // it will import this type instead of the underlying type directly.\n export interface Request extends CFRequest {}\n \n // Because when you construct a `new Request()` in a user script,\n // it's assumed to be a standards-based Fetch API Response, unless overridden.\n // This is fine by us, let user return whichever request type\n type ReturnRequest = StandardRequest | CFRequest;\n \n /** Per-party key-value storage */\n export interface Storage extends DurableObjectStorage {}\n \n /** Connection metadata only available when the connection is made */\n export type ConnectionContext = { request: CFRequest };\n \n export type Stub = {\n /** @deprecated Use `await socket()` instead */\n connect: () => WebSocket;\n socket(pathOrInit?: string | RequestInit): Promise<WebSocket>;\n socket(path: string, init?: RequestInit): Promise<WebSocket>;\n fetch(pathOrInit?: string | RequestInit | ReturnRequest): Promise<Response>;\n fetch(path: string, init?: RequestInit | ReturnRequest): Promise<Response>;\n };\n \n /** Additional information about other resources in the current project */\n export type Context = {\n /** Access other parties in this project */\n parties: Record<\n string,\n {\n get(id: string): Stub;\n }\n >;\n /**\n * A binding to the Cloudflare AI service.\n */\n ai: AI;\n /**\n * A binding to the Cloudflare Vectorize service.\n */\n vectorize: Record<string, VectorizeIndex>;\n \n /**\n * A binding to fetch static assets\n */\n assets: AssetFetcher;\n \n /**\n * Custom bindings\n */\n bindings: CustomBindings;\n };\n \n export type AI = Record<string, never>;\n \n export type FetchLobby = {\n env: Record<string, unknown>;\n ai: AI;\n parties: Context[\"parties\"];\n vectorize: Context[\"vectorize\"];\n analytics: AnalyticsEngineDataset;\n assets: AssetFetcher;\n bindings: CustomBindings;\n };\n \n export type CronLobby = {\n env: Record<string, unknown>;\n ai: AI;\n parties: Context[\"parties\"];\n vectorize: Context[\"vectorize\"];\n analytics: AnalyticsEngineDataset;\n assets: AssetFetcher;\n bindings: CustomBindings;\n };\n \n export type Lobby = {\n id: string;\n env: Record<string, unknown>;\n ai: AI;\n parties: Context[\"parties\"];\n vectorize: Context[\"vectorize\"];\n analytics: AnalyticsEngineDataset;\n assets: AssetFetcher;\n bindings: CustomBindings;\n };\n \n export type ExecutionContext = CFExecutionContext;\n \n // https://stackoverflow.com/a/58993872\n type ImmutablePrimitive = undefined | null | boolean | string | number;\n type Immutable<T> = T extends ImmutablePrimitive\n ? T\n : T extends Array<infer U>\n ? ImmutableArray<U>\n : T extends Map<infer K, infer V>\n ? ImmutableMap<K, V>\n : T extends Set<infer M>\n ? ImmutableSet<M>\n : ImmutableObject<T>;\n type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;\n type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;\n type ImmutableSet<T> = ReadonlySet<Immutable<T>>;\n type ImmutableObject<T> = { readonly [K in keyof T]: Immutable<T[K]> };\n \n export type ConnectionState<T> = ImmutableObject<T> | null;\n export type ConnectionSetStateFn<T> = (prevState: ConnectionState<T>) => T;\n \n /** A WebSocket connected to the Room */\n export type Connection<TState = unknown> = WebSocket & {\n /** Connection identifier */\n id: string;\n \n /** @deprecated You can access the socket properties directly on the connection*/\n socket: WebSocket;\n // We would have been able to use Websocket::url\n // but it's not available in the Workers runtime\n // (rather, url is `null` when using WebSocketPair)\n // It's also set as readonly, so we can't set it ourselves.\n // Instead, we'll use the `uri` property.\n uri: string;\n \n /**\n * Arbitrary state associated with this connection.\n * Read-only, use Connection.setState to update the state.\n */\n state: ConnectionState<TState>;\n \n setState(\n state: TState | ConnectionSetStateFn<TState> | null\n ): ConnectionState<TState>;\n \n /** @deprecated use Connection.setState instead */\n serializeAttachment<T = unknown>(attachment: T): void;\n \n /** @deprecated use Connection.state instead */\n deserializeAttachment<T = unknown>(): T | null;\n };\n \n type CustomBindings = {\n r2: Record<string, R2Bucket>;\n kv: Record<string, KVNamespace>;\n };\n \n /** Room represents a single, self-contained, long-lived session. */\n export type Room = {\n /** Room ID defined in the Party URL, e.g. /parties/:name/:id */\n id: string;\n \n /** Internal ID assigned by the platform. Use Party.id instead. */\n internalID: string;\n \n /** Party name defined in the Party URL, e.g. /parties/:name/:id */\n name: string;\n \n /** Environment variables (--var, partykit.json#vars, or .env) */\n env: Record<string, unknown>;\n \n /** A per-room key-value storage */\n storage: Storage;\n \n /** `blockConcurrencyWhile()` ensures no requests are delivered until */\n blockConcurrencyWhile: DurableObjectState[\"blockConcurrencyWhile\"];\n \n /** Additional information about other resources in the current project */\n context: Context;\n \n /** @deprecated Use `room.getConnections` instead */\n connections: Map<string, Connection>;\n \n /** @deprecated Use `room.context.parties` instead */\n parties: Context[\"parties\"];\n \n /** Send a message to all connected clients, except connection ids listed `without` */\n broadcast: (\n msg: string | ArrayBuffer | ArrayBufferView,\n without?: string[] | undefined\n ) => void;\n \n /** Get a connection by connection id */\n getConnection<TState = unknown>(id: string): Connection<TState> | undefined;\n \n /**\n * Get all connections. Optionally, you can provide a tag to filter returned connections.\n * Use `Party.Server#getConnectionTags` to tag the connection on connect.\n */\n getConnections<TState = unknown>(tag?: string): Iterable<Connection<TState>>;\n \n /**\n * Cloudflare Analytics Engine dataset. Use this to log custom events and metrics.\n */\n analytics: AnalyticsEngineDataset;\n };\n \n /**\n * Interface defining the lifecycle hooks available in a Room\n */\n export interface RoomHooks<TUser = any> {\n /**\n * Called when a user joins the room\n * @param user The user object that joined\n * @param conn The connection object\n * @param ctx The connection context\n */\n onJoin?(user: TUser, conn: Connection, ctx: ConnectionContext): void | Promise<void>;\n \n /**\n * Called when a user leaves the room\n * @param user The user object that left\n * @param conn The connection object\n */\n onLeave?(user: TUser, conn: Connection): void | Promise<void>;\n }\n \n /** @deprecated Use `Party.Room` instead */\n export type Party = Room;\n \n /* Party.Server defines what happens when someone connects to and sends messages or HTTP requests to your party\n *\n * @example\n * export default class Room implements Party.Server {\n * constructor(readonly room: Party) {}\n * onConnect(connection: Party.Connection) {\n * this.room.broadcast(\"Someone connected with id \" + connection.id);\n * }\n * }\n */\n export type Server = {\n /**\n * You can define an `options` field to customise the Party.Server behaviour.\n */\n readonly options?: ServerOptions;\n \n /**\n * You can tag a connection to filter them in Party#getConnections.\n * Each connection supports up to 9 tags, each tag max length is 256 characters.\n */\n getConnectionTags?(\n connection: Connection,\n context: ConnectionContext\n ): string[] | Promise<string[]>;\n \n /**\n * Called when the server is started, before first `onConnect` or `onRequest`.\n * Useful for loading data from storage.\n *\n * You can use this to load data from storage and perform other asynchronous\n * initialization, such as retrieving data or configuration from other\n * services or databases.\n */\n onStart?(): void | Promise<void>;\n \n /**\n * Called when a new incoming WebSocket connection is opened.\n */\n onConnect?(\n connection: Connection,\n ctx: ConnectionContext\n ): void | Promise<void>;\n \n /**\n * Called when a WebSocket connection receives a message from a client, or another connected party.\n */\n onMessage?(\n message: string | ArrayBuffer | ArrayBufferView,\n sender: Connection\n ): void | Promise<void>;\n \n /**\n * Called when a WebSocket connection is closed by the client.\n */\n onClose?(connection: Connection): void | Promise<void>;\n \n /**\n * Called when a WebSocket connection is closed due to a connection error.\n */\n onError?(connection: Connection, error: Error): void | Promise<void>;\n \n /**\n * Called when a HTTP request is made to the room URL.\n */\n onRequest?(req: Request): Response | Promise<Response>;\n \n /**\n * Called when an alarm is triggered. Use Party.storage.setAlarm to set an alarm.\n *\n * Alarms have access to most Party resources such as storage, but not Party.id\n * and Party.context.parties properties. Attempting to access them will result in a\n * runtime error.\n */\n onAlarm?(): void | Promise<void>;\n };\n \n export type FetchSocket = WebSocket & {\n request: Request;\n };\n \n export type Cron = ScheduledController & {\n name: string;\n };\n \n type ServerConstructor = {\n new (room: Room): Server;\n };\n \n /**\n * Party.Worker allows you to customise the behaviour of the Edge worker that routes\n * connections to your party.\n *\n * The Party.Worker methods can be defined as static methods on the Party.Server constructor.\n * @example\n * export default class Room implements Party.Server {\n * static onBeforeConnect(req: Party.Request) {\n * return new Response(\"Access denied\", { status: 403 })\n * }\n * constructor(readonly room: Room) {}\n * }\n *\n * Room satisfies Party.Worker;\n */\n \n export type Worker = ServerConstructor & {\n /**\n * Runs on any HTTP request that does not match a Party URL or a static asset.\n * Useful for running lightweight HTTP endpoints that don't need access to the Party\n * state.\n **/\n onFetch?(\n req: Request,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ): Response | undefined | null | Promise<Response | null | undefined>;\n \n /**\n * Runs on any WebSocket connection that does not match a Party URL or a static asset.\n * Useful for running lightweight WebSocket endpoints that don't need access to the Party\n * state.\n */\n onSocket?(\n socket: FetchSocket,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ): void | Promise<void>;\n \n /**\n * Runs before any HTTP request is made to the party. You can modify the request\n * before it is forwarded to the party, or return a Response to short-circuit it.\n */\n onBeforeRequest?(\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ): Request | Response | Promise<Request | Response>;\n \n /**\n * Runs before any WebSocket connection is made to the party. You can modify the request\n * before opening a connection, or return a Response to prevent the connection.\n */\n onBeforeConnect?(\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ): Request | Response | Promise<Request | Response>;\n \n /**\n * Runs on a schedule. You can use this to perform periodic tasks, such as\n * sending a heartbeat to a third-party service.\n */\n onCron?(\n controller: Cron,\n lobby: CronLobby,\n ctx: ExecutionContext\n ): Response | Promise<Response>;\n };\n \n /**\n * PartyKitServer is allows you to customise the behaviour of your Party.\n *\n * @note If you're starting a new project, we recommend using the newer\n * Party.Server API instead.\n *\n * @example\n * export default {\n * onConnect(connection, room) {\n * room.broadcast(\"Someone connected with id \" + connection.id);\n * }\n * }\n */\n export type PartyKitServer = {\n /** @deprecated Use `onFetch` instead */\n unstable_onFetch?: (\n req: Request,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ) => Response | null | undefined | Promise<Response | null | undefined>;\n onFetch?: (\n req: Request,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ) => Response | null | undefined | Promise<Response | null | undefined>;\n onSocket?(\n socket: FetchSocket,\n lobby: FetchLobby,\n ctx: ExecutionContext\n ): void | Promise<void>;\n onBeforeRequest?: (\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ) => ReturnRequest | Response | Promise<ReturnRequest | Response>;\n \n onCron?: (\n controller: Cron,\n lobby: CronLobby,\n ctx: ExecutionContext\n ) => void | Promise<void>;\n \n onRequest?: (req: Request, room: Room) => Response | Promise<Response>;\n onAlarm?: (room: Omit<Room, \"id\" | \"parties\">) => void | Promise<void>;\n onConnect?: (\n connection: Connection,\n room: Room,\n ctx: ConnectionContext\n ) => void | Promise<void>;\n onBeforeConnect?: (\n req: Request,\n lobby: Lobby,\n ctx: ExecutionContext\n ) => ReturnRequest | Response | Promise<ReturnRequest | Response>;\n \n /**\n * PartyKitServer may opt into being hibernated between WebSocket\n * messages, which enables a single server to handle more connections.\n */\n onMessage?: (\n message: string | ArrayBuffer | ArrayBufferView,\n connection: Connection,\n room: Room\n ) => void | Promise<void>;\n onClose?: (connection: Connection, room: Room) => void | Promise<void>;\n onError?: (\n connection: Connection,\n err: Error,\n room: Room\n ) => void | Promise<void>;\n };\n \n export type ServerOptions = {\n /**\n * Whether the PartyKit platform should remove the server from memory\n * between HTTP requests and WebSocket messages.\n *\n * The default value is `false`.\n */\n hibernate?: boolean;\n };\n \n //\n // ---\n // DEPRECATIONS\n // ---\n //\n \n /** @deprecated Use Party.Request instead */\n export type PartyRequest = Request;\n \n /** @deprecated Use Party.Storage instead */\n export type PartyStorage = Storage;\n \n /** @deprecated Use Party.Storage instead */\n export type PartyKitStorage = Storage;\n \n /** @deprecated Use Party.ConnectionContext instead */\n export type PartyConnectionContext = ConnectionContext;\n \n /** @deprecated Use Party.ConnectionContext instead */\n export type PartyKitContext = ConnectionContext;\n \n /** @deprecated Use Party.Stub instead */\n export type PartyStub = Stub;\n \n /** Additional information about other resources in the current project */\n /** @deprecated Use Party.Context instead */\n export type PartyContext = Context;\n \n /** @deprecated Use Party.FetchLobby instead */\n export type PartyFetchLobby = FetchLobby;\n \n /** @deprecated Use Party.Lobby instead */\n export type PartyLobby = Lobby;\n \n /** @deprecated Use Party.ExecutionContext instead */\n export type PartyExecutionContext = ExecutionContext;\n \n /** @deprecated Use Party.Connection instead */\n export type PartyConnection = Connection;\n \n /** @deprecated Use Party.Server instead */\n export type PartyServer = Server;\n \n /** @deprecated Use Party.Worker instead */\n export type PartyWorker = Worker;\n \n /** @deprecated Use `Room` instead */\n export type PartyKitRoom = Room;\n \n /** @deprecated Use `Party.Connection` instead */\n export type PartyKitConnection = Connection;\n \n /** @deprecated Use `Party.ServerOptions` instead */\n export type PartyServerOptions = ServerOptions;","/**\n * JWT Authentication Class for Cloudflare Workers\n * Uses Web Crypto API for JWT signing and verification\n */\n\ninterface JWTOptions {\n expiresIn?: string | number;\n}\n\ninterface JWTHeader {\n alg: string;\n typ: string;\n}\n\ninterface JWTPayload {\n [key: string]: unknown;\n iat?: number;\n exp?: number;\n}\n\nexport class JWTAuth {\n private secret: string;\n private encoder: TextEncoder;\n private decoder: TextDecoder;\n\n /**\n * Constructor for the JWTAuth class\n * @param {string} secret - The secret key used for signing and verifying tokens\n */\n constructor(secret: string) {\n if (!secret || typeof secret !== 'string') {\n throw new Error('Secret is required and must be a string');\n }\n this.secret = secret;\n this.encoder = new TextEncoder();\n this.decoder = new TextDecoder();\n }\n\n /**\n * Convert the secret to a CryptoKey for HMAC operations\n * @returns {Promise<CryptoKey>} - The CryptoKey for HMAC operations\n */\n private async getSecretKey(): Promise<CryptoKey> {\n const keyData: Uint8Array = this.encoder.encode(this.secret);\n return await crypto.subtle.importKey(\n 'raw', // format\n keyData, // key data\n {\n name: 'HMAC',\n hash: { name: 'SHA-256' },\n },\n false, // extractable\n ['sign', 'verify'] // key usages\n );\n }\n\n /**\n * Base64Url encode a buffer\n * @param {ArrayBuffer} buffer - The buffer to encode\n * @returns {string} - The base64url encoded string\n */\n private base64UrlEncode(buffer: ArrayBuffer): string {\n const base64: string = btoa(String.fromCharCode(...new Uint8Array(buffer)));\n return base64.replace(/=/g, '').replace(/\\+/g, '-').replace(/\\//g, '_');\n }\n\n /**\n * Base64Url decode a string\n * @param {string} base64Url - The base64url encoded string\n * @returns {ArrayBuffer} - The decoded buffer\n */\n private base64UrlDecode(base64Url: string) {\n const padding: string = '='.repeat((4 - (base64Url.length % 4)) % 4);\n const base64: string = base64Url.replace(/-/g, '+').replace(/_/g, '/') + padding;\n const rawData: string = atob(base64);\n const buffer: Uint8Array = new Uint8Array(rawData.length);\n\n for (let i = 0; i < rawData.length; i++) {\n buffer[i] = rawData.charCodeAt(i);\n }\n\n return buffer.buffer;\n }\n\n /**\n * Sign a payload and create a JWT token\n * @param {JWTPayload} payload - The payload to include in the token\n * @param {JWTOptions} [options={}] - Options for the token\n * @param {string | number} [options.expiresIn='1h'] - Token expiration time\n * @returns {Promise<string>} - The JWT token\n */\n public async sign(payload: JWTPayload, options: JWTOptions = {}): Promise<string> {\n if (!payload || typeof payload !== 'object') {\n throw new Error('Payload must be an object');\n }\n\n // Set default expiration if not provided\n const expiresIn: string | number = options.expiresIn || '1h';\n\n // Calculate expiration time\n let exp: number | undefined;\n if (typeof expiresIn === 'number') {\n exp = Math.floor(Date.now() / 1000) + expiresIn;\n } else if (typeof expiresIn === 'string') {\n const match: RegExpMatchArray | null = expiresIn.match(/^(\\d+)([smhd])$/);\n if (match) {\n const value: number = parseInt(match[1]);\n const unit: string = match[2];\n const seconds: number = {\n 's': value,\n 'm': value * 60,\n 'h': value * 60 * 60,\n 'd': value * 60 * 60 * 24\n }[unit]!;\n exp = Math.floor(Date.now() / 1000) + seconds;\n } else {\n throw new Error('Invalid expiresIn format. Use a number (seconds) or a string like \"1h\", \"30m\", etc.');\n }\n }\n\n // Create full payload with claims\n const fullPayload: JWTPayload = {\n ...payload,\n iat: Math.floor(Date.now() / 1000),\n exp\n };\n\n // Create header\n const header: JWTHeader = {\n alg: 'HS256',\n typ: 'JWT'\n };\n\n // Encode header and payload\n // @ts-expect-error - TS doesn't have a built-in TextEncoder\n const encodedHeader: string = this.base64UrlEncode(this.encoder.encode(JSON.stringify(header)));\n // @ts-expect-error - TS doesn't have a built-in TextEncoder\n const encodedPayload: string = this.base64UrlEncode(this.encoder.encode(JSON.stringify(fullPayload)));\n\n // Create signature base\n const signatureBase: string = `${encodedHeader}.${encodedPayload}`;\n\n // Get key and sign\n const key: CryptoKey = await this.getSecretKey();\n const signature: ArrayBuffer = await crypto.subtle.sign(\n { name: 'HMAC' },\n key,\n this.encoder.encode(signatureBase)\n );\n\n // Encode signature and create token\n const encodedSignature: string = this.base64UrlEncode(signature);\n return `${signatureBase}.${encodedSignature}`;\n }\n\n /**\n * Verify a JWT token and return the decoded payload\n * @param {string} token - The JWT token to verify\n * @returns {Promise<JWTPayload>} - The decoded payload if verification succeeds\n * @throws {Error} - If verification fails\n */\n public async verify(token: string): Promise<JWTPayload> {\n if (!token || typeof token !== 'string') {\n throw new Error('Token is required and must be a string');\n }\n\n // Split token into parts\n const parts: string[] = token.split('.');\n if (parts.length !== 3) {\n throw new Error('Invalid token format');\n }\n\n const [encodedHeader, encodedPayload, encodedSignature] = parts;\n\n // Decode header and payload\n try {\n // @ts-expect-error - TS doesn't have a built-in TextDecoder\n const header: JWTHeader = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedHeader)));\n // @ts-expect-error - TS doesn't have a built-in TextDecoder\n const payload: JWTPayload = JSON.parse(this.decoder.decode(this.base64UrlDecode(encodedPayload)));\n\n // Check algorithm\n if (header.alg !== 'HS256') {\n throw new Error(`Unsupported algorithm: ${header.alg}`);\n }\n\n // Check expiration\n const now: number = Math.floor(Date.now() / 1000);\n if (payload.exp && payload.exp < now) {\n throw new Error('Token has expired');\n }\n\n // Verify signature\n const key: CryptoKey = await this.getSecretKey();\n const signatureBase: string = `${encodedHeader}.${encodedPayload}`;\n const signature: ArrayBuffer = this.base64UrlDecode(encodedSignature) as ArrayBuffer;\n\n const isValid: boolean = await crypto.subtle.verify(\n { name: 'HMAC' },\n key,\n signature,\n this.encoder.encode(signatureBase)\n );\n\n if (!isValid) {\n throw new Error('Invalid signature');\n }\n\n return payload;\n } catch (error) {\n if (error instanceof Error) {\n throw new Error(`Token verification failed: ${error.message}`);\n }\n throw new Error('Token verification failed: Unknown error');\n }\n }\n}","import * as Party from \"./types/party\";\nimport { JWTAuth } from \"./jwt\";\nimport { response } from \"./utils\";\n\nexport const guardManageWorld = async (_, req: Party.Request, room: Party.Room): Promise<boolean> => {\n const tokenShard = req.headers.get(\"x-access-shard\");\n if (tokenShard) {\n if (tokenShard !== room.env.SHARD_SECRET) {\n return false\n }\n return true\n }\n const url = new URL(req.url);\n const token = req.headers.get(\"Authorization\") ?? url.searchParams.get(\"world-auth-token\");\n if (!token) {\n return false;\n }\n const jwt = new JWTAuth(room.env.AUTH_JWT_SECRET as string);\n try {\n const payload = await jwt.verify(token);\n if (!payload) {\n return false;\n }\n } catch (error) {\n return false;\n }\n return true;\n}","import type * as Party from \"./types/party\";\n\n/**\n * @description Factory function that creates a session guard with access to room storage\n * @param {Party.Storage} storage - The room storage instance\n * @returns {Function} - The guard function\n * \n * @example\n * ```typescript\n * import { createRequireSessionGuard } from \"./session.guard\";\n * \n * export class GameRoom {\n * constructor(private room: Party.Room) {}\n * \n * @Action(\"sendMessage\")\n * @Guard([createRequireSessionGuard(this.room.storage)])\n * async sendMessage(user: User, message: string, conn: Party.Connection) {\n * // This action will only execute if the user has a valid session\n * this.$broadcast({ type: \"message\", user, message });\n * }\n * }\n * ```\n */\nexport function createRequireSessionGuard(storage: Party.Storage) {\n return async (sender: Party.Connection, value: any): Promise<boolean> => {\n if (!sender || !sender.id) {\n return false;\n }\n\n try {\n // Check if session exists in storage\n const session = await storage.get(`session:${sender.id}`);\n \n // Return false if no session found\n if (!session) {\n return false;\n }\n\n // Verify session has required properties\n const typedSession = session as { publicId: string, created?: number, connected?: boolean };\n if (!typedSession.publicId) {\n return false;\n }\n\n // Session exists and is valid\n return true;\n } catch (error) {\n // If there's an error accessing storage, deny access\n console.error('Error checking session in requireSession guard:', error);\n return false;\n }\n };\n}\n\n/**\n * @description Guard function that verifies if a user session exists (for room and request guards)\n * @param {Party.Connection} sender - The connection object of the sender\n * @param {any} value - The value/payload sent with the action or request\n * @param {Party.Room} room - The room instance\n * @returns {Promise<boolean>} - Returns true if session exists, false otherwise\n * \n * @example\n * ```typescript\n * import { requireSession } from \"./session.guard\";\n * \n * // For room guards\n * @Room({\n * path: \"game-{id}\",\n * guards: [requireSession]\n * })\n * export class GameRoom {\n * // Room implementation\n * }\n * \n * // For request guards\n * @Request({ path: '/api/data', method: 'GET' })\n * @Guard([requireSession])\n * async getData(req: Party.Request, res: ServerResponse) {\n * // This request will only execute if the user has a valid session\n * return res.success({ data: \"protected data\" });\n * }\n * ```\n */\nexport const requireSession = async (sender: Party.Connection, value: any, room: Party.Room): Promise<boolean> => {\n if (!sender || !sender.id) {\n return false;\n }\n\n try {\n // Check if session exists in storage\n const session = await room.storage.get(`session:${sender.id}`);\n \n // Return false if no session found\n if (!session) {\n return false;\n }\n\n // Verify session has required properties\n const typedSession = session as { publicId: string, created?: number, connected?: boolean };\n if (!typedSession.publicId) {\n return false;\n }\n\n // Session exists and is valid\n return true;\n } catch (error) {\n // If there's an error accessing storage, deny access\n console.error('Error checking session in requireSession guard:', error);\n return false;\n }\n};\n"],"mappings":"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","names":["Action","name","bodyValidation","target","propertyKey","constructor","_actionMetadata","Map","set","key","Request","options","_requestMetadata","path","startsWith","method","routeKey","Room","prototype","maxUsers","throttleStorage","throttleSync","sessionExpiryTime","guards","RoomGuard","Guard","descriptor","Array","isArray","generateShortUUID","chars","uuid","i","randomIndex","Math","floor","random","length","Storage","memory","Map","put","key","value","set","get","delete","list","dset","z","createStatesSnapshot","getByPath","load","syncClass","DELETE_TOKEN","generateShortUUID","dset","isPromise","value","Promise","awaitReturn","val","isClass","obj","prototype","constructor","throttle","func","wait","timeout","lastArgs","args","setTimeout","extractParams","pattern","str","regexPattern","replace","regex","RegExp","match","exec","groups","dremove","keys","split","i","l","length","t","k","buildObject","valuesMap","allMemory","memoryObj","path","get","dset","response","status","body","Response","JSON","stringify","headers","ServerResponse","interceptors","statusCode","responseBody","responseHeaders","constructor","status","code","body","header","name","value","setHeaders","headers","use","interceptor","push","buildResponse","response","Response","JSON","stringify","interceptedResponse","Promise","error","console","json","send","undefined","text","redirect","url","success","badRequest","message","details","notPermitted","unauthorized","notFound","serverError","cors","res","options","newHeaders","Headers","headers","requestOrigin","origin","set","credentials","exposedHeaders","length","join","methods","allowedHeaders","maxAge","toString","Response","body","status","createCorsInterceptor","Message","z","object","action","string","value","any","Server","subRoom","rooms","constructor","room","isHibernate","hibernate","roomStorage","storage","send","conn","obj","structuredClone","interceptorPacket","signal","getUsersProperty","publicId","state","user","awaitReturn","JSON","stringify","broadcast","getConnections","onStart","createRoom","runGarbageCollector","garbageCollector","sessionExpiryTime","options","getSubRoom","activeConnections","activePrivateIds","Set","map","id","sessions","list","users","usersPropName","getUsersPropName","validPublicIds","expiredPublicIds","SESSION_EXPIRY_TIME","now","Date","key","session","startsWith","privateId","replace","typedSession","has","connected","created","deleteSession","add","currentUsers","delete","error","console","instance","init","initPersist","params","extractParams","path","loadMemory","root","get","memory","tmpObject","dset","load","$memoryAll","$send","$broadcast","$sessionTransfer","targetRoomId","userSession","userSnapshot","createStatesSnapshot","transferData","sessionState","targetRoomParty","context","parties","main","response","fetch","method","body","headers","ok","Error","text","transferToken","json","syncCb","values","getMemoryAll","buildObject","packet","type","clear","persistCb","_instance","getByPath","itemValue","DELETE_TOKEN","put","syncClass","onSync","throttle","onPersist","meta","propId","metadata","_propertyMetadata","getUserConnectionProperty","connectedPropName","updateUserConnectionStatus","isConnected","connectionSignal","set","getSession","e","saveSession","data","sessionData","undefined","updateSessionConnection","onConnectClient","ctx","close","roomGuards","guard","isAuthorized","request","url","URL","searchParams","existingSession","generateShortUUID","classType","restored","isClass","snapshot","sessionPrivateId","setState","pId","onConnect","onConnectShard","shardId","shard","clients","Map","onMessage","message","sender","handleShardMessage","parse","result","safeParse","success","warn","actions","actionName","guards","bodyValidation","bodyResult","shardConnection","parsedMessage","shardState","handleShardClientConnect","handleShardClientMessage","handleShardClientDisconnect","requestInfo","virtualContext","Headers","virtualConnection","targetClientId","currentState","mergedState","Object","assign","payload","payloadString","clientState","onClose","connectionUpdated","onAlarm","onError","connection","onRequest","req","isFromShard","res","ServerResponse","createCorsInterceptor","status","handleShardRequest","handleDirectRequest","handleSessionRestore","badRequest","serverError","notFound","pathname","endsWith","tryMatchRequestHandler","legacyResponse","Response","requestHandlers","split","slice","join","routeKey","handler","entries","firstColonIndex","indexOf","handlerMethod","substring","handlerPath","pathMatches","extractPathParams","notPermitted","bodyData","includes","contentType","validation","details","requestPath","pathRegexString","pathRegex","RegExp","test","paramNames","forEach","segment","push","matches","match","length","i","originalClientIp","enhancedReq","createEnhancedRequest","originalReq","clonedReq","clone","viaShard","Shard","ws","connectionMap","mainServerStub","worldUrl","worldId","lastReportedConnections","statsInterval","statsIntervalId","constructor","room","Map","onStart","roomId","id","split","roomStub","context","parties","main","get","console","warn","socket","headers","addEventListener","event","message","JSON","parse","data","targetClientId","clientConn","send","broadcast","error","updateWorldStats","startPeriodicStatsUpdates","clearInterval","setInterval","catch","stopPeriodicStatsUpdates","onConnect","conn","ctx","set","request","forEach","value","key","requestInfo","url","method","stringify","type","privateId","onMessage","sender","parsedMessage","wrappedMessage","publicId","state","payload","onClose","delete","currentConnections","size","worldRoom","world","response","fetch","env","SHARD_SECRET","body","shardId","connections","ok","errorData","json","status","onRequest","req","URL","path","pathname","text","Headers","clientIp","requestInit","onAlarm","testRoom","Room","options","createServer","io","server","Server","rooms","isShard","shard","ServerIo","path","parties","game","partyFn","env","prototype","throttleSync","throttleStorage","shardServer","Shard","subRoom","context","main","Map","lobby","values","onStart","room","createClient","id","opts","client","connection","getServerUser","prop","privateId","conn","session","getSession","publicId","request","method","url","URL","Request","toString","response","onRequest","tick","ms","Promise","resolve","setTimeout","MockPartyClient","events","id","conn","constructor","server","Map","generateShortUUID","MockConnection","addEventListener","event","cb","has","set","get","push","removeEventListener","callbacks","index","indexOf","splice","length","delete","_trigger","data","send","onMessage","JSON","stringify","MockLobby","lobbyId","socket","_init","connection","idOrOptions","maybeOptions","options","room","fetch","url","baseUrl","includes","request","MockContext","parties","main","partyFn","serverCache","MockPartyRoom","clients","storage","context","env","Storage","opts","URL","query","key","value","Object","entries","searchParams","String","Request","toString","method","headers","onConnect","broadcast","forEach","client","getConnection","getConnections","Array","from","values","map","clear","state","setState","close","onClose","ServerIo","ClientIo","signal","sync","id","persist","z","JWTAuth","secret","encoder","decoder","constructor","Error","TextEncoder","TextDecoder","getSecretKey","keyData","encode","crypto","subtle","importKey","name","hash","base64UrlEncode","buffer","base64","btoa","String","fromCharCode","Uint8Array","replace","base64UrlDecode","base64Url","padding","repeat","length","rawData","atob","i","charCodeAt","sign","payload","options","expiresIn","exp","Math","floor","Date","now","match","value","parseInt","unit","seconds","fullPayload","iat","header","alg","typ","encodedHeader","JSON","stringify","encodedPayload","signatureBase","key","signature","encodedSignature","verify","token","parts","split","parse","decode","isValid","error","message","guardManageWorld","_","req","room","tokenShard","headers","get","env","SHARD_SECRET","url","URL","token","searchParams","jwt","JWTAuth","AUTH_JWT_SECRET","payload","verify","error","MAX_PLAYERS_PER_SHARD","RoomConfigSchema","z","object","name","string","balancingStrategy","enum","public","boolean","maxPlayersPerShard","number","int","positive","minShards","min","maxShards","optional","RegisterShardSchema","shardId","roomId","url","maxConnections","UpdateShardStatsSchema","connections","status","ScaleRoomSchema","targetShardCount","shardTemplate","urlTemplate","RoomConfig","id","signal","undefined","ShardInfo","currentConnections","lastHeartbeat","persist","WorldRoom","rooms","shards","rrCounters","defaultShardUrlTemplate","defaultMaxConnectionsPerShard","constructor","room","AUTH_JWT_SECRET","SHARD_SECRET","env","Error","onJoin","user","conn","ctx","canConnect","guardManageWorld","request","setState","state","isAdmin","interceptorPacket","_","obj","cleanupInactiveShards","now","Date","timeout","shardsValue","hasChanges","Object","values","forEach","shard","setTimeout","registerRoom","req","roomConfig","json","newRoom","set","i","createShard","updateShardStats","res","body","notFound","scaleRoom","data","roomShards","filter","previousShardCount","length","badRequest","currentShardCount","shardsToRemove","sort","a","b","slice","shardsToKeep","some","s","newShards","newShard","push","connect","text","trim","JSON","parse","parseError","autoCreate","result","findOptimalShard","error","success","console","serverError","mockRequest","activeShards","selectedShard","reduce","Math","floor","random","counter","nextCounter","template","replace","max","path","method","maxUsers","throttleStorage","throttleSync","createRequireSessionGuard","storage","sender","value","id","session","get","typedSession","publicId","error","console","requireSession","room"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signe/room",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"keywords": [],
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dset": "^3.1.3",
|
|
18
18
|
"partysocket": "^1.0.1",
|
|
19
19
|
"zod": "^3.23.8",
|
|
20
|
-
"@signe/sync": "2.
|
|
20
|
+
"@signe/sync": "2.5.1"
|
|
21
21
|
},
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|
package/src/testing.ts
CHANGED
|
@@ -90,6 +90,11 @@ export async function testRoom(Room, options: {
|
|
|
90
90
|
createClient: async (id?: string, opts?: { query?: Record<string, string>, headers?: Record<string, string> }) => {
|
|
91
91
|
const client = await io.connection(server as Server, id, opts)
|
|
92
92
|
return client
|
|
93
|
+
},
|
|
94
|
+
getServerUser: async (client: any, prop = 'users') => {
|
|
95
|
+
const privateId = client.conn.id;
|
|
96
|
+
const session = await (server as Server).getSession(privateId);
|
|
97
|
+
return (server as any).subRoom[prop]()[session?.publicId];
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
100
|
}
|
|
@@ -105,4 +110,8 @@ export async function request(room: Server | Shard, path: string, options: {
|
|
|
105
110
|
const request = new Request(url.toString(), options)
|
|
106
111
|
const response = await room.onRequest(request as any)
|
|
107
112
|
return response
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function tick(ms: number = 0) {
|
|
116
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
108
117
|
}
|