@signe/room 1.4.1 → 2.0.0

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/decorators.ts","../../sync/src/utils.ts","../src/storage.ts","../src/mock.ts","../src/server.ts","../src/utils.ts","../src/testing.ts"],"sourcesContent":["import type * as Party from \"./types/party\"\nimport type { z } from \"zod\"\ntype GuardFn = (sender: Party.Connection, value: any) => boolean | Promise<boolean>;\ntype RoomGuardFn = (conn: Party.Connection, ctx: Party.ConnectionContext) => boolean | Promise<boolean>;\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\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 { generateShortUUID } from \"../../sync/src/utils\";\nimport { Server } from \"./server\";\nimport { Storage } from \"./storage\";\n\nexport class MockPartyClient {\n private events: Map<string, Function> = new Map();\n id = generateShortUUID()\n conn: MockConnection;\n\n constructor(public server: Server) {\n this.conn = new MockConnection(this)\n }\n \n addEventListener(event, cb) {\n this.events.set(event, cb);\n }\n\n removeEventListener(event, cb) {\n this.events.delete(event);\n }\n\n _trigger(event, data) {\n this.events.get(event)?.(data);\n }\n\n send(data) {\n return this.server.onMessage(JSON.stringify(data), this.conn as any)\n }\n}\n\nclass MockPartyRoom {\n clients: Map<string, MockPartyClient> = new Map();\n storage = new Storage();\n env = {}\n\n constructor(public id?: string) {\n this.id = id || generateShortUUID()\n }\n\n async connection(server: Server) {\n const socket = new MockPartyClient(server);\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 this.clients; \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 { 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\";\n\nconst Message = z.object({\n action: z.string(),\n value: z.any(),\n});\n\ntype CreateRoomOptions = {\n getMemoryAll?: boolean;\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 /**\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 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\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.room.broadcast(\n JSON.stringify({\n type: \"sync\",\n value: packet,\n })\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 const meta = subRoom.constructor[\"_propertyMetadata\"];\n return meta?.get(\"users\")\n }\n\n private 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 private async deleteSession(privateId: string) {\n await this.room.storage.delete(`session:${privateId}`);\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 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);\n if (!isAuthorized) {\n conn.close();\n return;\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 || 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 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 // Only store new session if it doesn't exist\n if (!existingSession) {\n await this.saveSession(conn.id, {\n publicId\n });\n }\n else {\n await this.updateSessionConnection(conn.id, true);\n }\n }\n\n // Call the room's onJoin method if it exists\n await awaitReturn(subRoom[\"onJoin\"]?.(user, conn, ctx));\n \n // Store both IDs in connection state\n conn.setState({ publicId });\n\n // Send initial sync data with both IDs to the new connection\n conn.send(\n JSON.stringify({\n type: \"sync\",\n value: {\n pId: publicId,\n ...subRoom.$memoryAll,\n },\n })\n );\n }\n\n /**\n * @method onMessage\n * @async\n * @param {string} message - The message received from a user.\n * @param {Party.Connection} sender - The connection object of the sender.\n * @description Processes incoming messages and triggers corresponding actions in the sub-room.\n * @returns {Promise<void>}\n * \n * @example\n * ```typescript\n * server.onMessage = async (message, sender) => {\n * await server.onMessage(message, sender);\n * console.log(\"Message processed from:\", sender.id);\n * };\n * ```\n */\n\n async onMessage(message: string, sender: Party.Connection) {\n let json\n try {\n json = JSON.parse(message)\n }\n catch (e) {\n return;\n }\n // Validate incoming messages\n const result = Message.safeParse(json);\n if (!result.success) {\n return;\n }\n const subRoom = await this.getSubRoom()\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);\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 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 await awaitReturn(subRoom[\"onLeave\"]?.(user, conn));\n\n // Mark session as disconnected instead of deleting it\n await this.updateSessionConnection(privateId, false);\n\n // Broadcast user disconnection\n this.room.broadcast(\n JSON.stringify({\n type: \"user_disconnected\",\n value: { publicId }\n })\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 async onRequest(req: Party.Request) {\n const subRoom = await this.getSubRoom()\n const res = (body: any, status: number) => {\n return new Response(JSON.stringify(body), { status });\n }\n if (!subRoom) {\n return res({\n error: \"Not found\"\n }, 404);\n }\n\n const response = await awaitReturn(subRoom[\"onRequest\"]?.(req, this.room));\n if (!response) {\n return res({\n error: \"Not found\"\n }, 404);\n }\n if (response instanceof Response) {\n return response;\n }\n return res(response, 200);\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}","import { ServerIo } from \"./mock\"\nimport { Server } from \"./server\"\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} = {}) {\n const io = new ServerIo(Room.path)\n Room.prototype.throttleSync = 0\n Room.prototype.throttleStorage = 0\n Room.prototype.options = options\n const server = new Server(io as any)\n server.rooms = [Room]\n await server.onStart()\n return {\n server,\n room: server.subRoom,\n createClient: async () => {\n const client = await io.connection(server)\n return client\n }\n }\n}\n"],"mappings":"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","names":["Action","name","bodyValidation","target","propertyKey","constructor","_actionMetadata","Map","set","key","Room","options","path","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","MockPartyClient","events","id","conn","constructor","server","Map","generateShortUUID","MockConnection","addEventListener","event","cb","set","removeEventListener","delete","_trigger","data","get","send","onMessage","JSON","stringify","MockPartyRoom","clients","storage","env","Storage","connection","socket","onConnect","request","broadcast","forEach","client","getConnection","getConnections","clear","state","setState","value","close","onClose","ServerIo","ClientIo","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","Message","z","object","action","string","value","any","Server","subRoom","rooms","constructor","room","isHibernate","hibernate","roomStorage","storage","onStart","createRoom","garbageCollector","options","getSubRoom","activeConnections","getConnections","activePrivateIds","Set","map","conn","id","sessions","list","users","getUsersProperty","usersPropName","getUsersPropName","validPublicIds","expiredPublicIds","SESSION_EXPIRY_TIME","sessionExpiryTime","now","Date","key","session","startsWith","privateId","replace","typedSession","has","connected","created","deleteSession","add","publicId","currentUsers","delete","error","console","instance","init","initPersist","params","extractParams","path","loadMemory","root","get","memory","tmpObject","dset","load","$memoryAll","syncCb","values","getMemoryAll","buildObject","packet","broadcast","JSON","stringify","type","clear","persistCb","_instance","getByPath","itemValue","createStatesSnapshot","DELETE_TOKEN","put","syncClass","onSync","throttle","onPersist","meta","propId","getSession","e","saveSession","data","sessionData","undefined","updateSessionConnection","onConnect","ctx","close","roomGuards","guard","isAuthorized","existingSession","generateShortUUID","user","signal","classType","isClass","snapshot","awaitReturn","setState","send","pId","onMessage","message","sender","json","parse","result","safeParse","success","actions","state","actionName","guards","bodyValidation","bodyResult","onClose","onAlarm","onError","connection","onRequest","req","res","body","status","Response","response","testRoom","Room","options","io","ServerIo","path","prototype","throttleSync","throttleStorage","server","Server","rooms","onStart","room","subRoom","createClient","client","connection"]}
1
+ {"version":3,"sources":["../src/decorators.ts","../../sync/src/utils.ts","../src/storage.ts","../src/mock.ts","../src/server.ts","../src/utils.ts","../src/shard.ts","../src/testing.ts","../src/world.ts","../src/types/party.ts","../src/jwt.ts","../src/world.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 { generateShortUUID } from \"../../sync/src/utils\";\nimport { Server } from \"./server\";\nimport { Storage } from \"./storage\";\n\nexport class MockPartyClient {\n private events: Map<string, Function> = new Map();\n id = generateShortUUID()\n conn: MockConnection;\n\n constructor(public server: Server) {\n this.conn = new MockConnection(this)\n }\n \n addEventListener(event, cb) {\n this.events.set(event, cb);\n }\n\n removeEventListener(event, cb) {\n this.events.delete(event);\n }\n\n _trigger(event, data) {\n this.events.get(event)?.(data);\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) {}\n\n socket() {\n return new MockPartyClient(this.server)\n }\n}\n\nclass MockContext {\n parties: {\n main: Map<string, any>\n } = {\n main: new Map()\n }\n\n constructor(public room: MockPartyRoom, options: any = {}) {\n const parties = options.parties || {}\n for (let lobbyId in parties) {\n this.parties.main.set(lobbyId, new MockLobby(parties[lobbyId](room)))\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 })\n this.env = options.env || {}\n }\n\n async connection(server: Server) {\n const socket = new MockPartyClient(server);\n const url = new URL('http://localhost')\n const request = new Request(url.toString(), {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json'\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 { 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\";\n\nconst Message = z.object({\n action: z.string(),\n value: z.any(),\n});\n\ntype CreateRoomOptions = {\n getMemoryAll?: boolean;\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 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\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 const meta = subRoom.constructor[\"_propertyMetadata\"];\n return meta?.get(\"users\")\n }\n\n private 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 private 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);\n if (!isAuthorized) {\n conn.close();\n return;\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 || 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 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 // Only store new session if it doesn't exist\n if (!existingSession) {\n await this.saveSession(conn.id, {\n publicId\n });\n }\n else {\n await this.updateSessionConnection(conn.id, true);\n }\n }\n\n // Call the room's onJoin method if it exists\n await awaitReturn(subRoom[\"onJoin\"]?.(user, conn, ctx));\n\n // Store both IDs in connection state\n conn.setState({\n ...conn.state,\n publicId\n });\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 // 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, connectionInfo } = 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: {\n headers: new Headers({\n 'x-forwarded-for': connectionInfo.ip,\n 'user-agent': connectionInfo.userAgent,\n // Add other headers as needed\n }),\n method: 'GET',\n url: ''\n } as unknown as Party.Request\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 await awaitReturn(subRoom[\"onLeave\"]?.(user, conn));\n\n // Mark session as disconnected instead of deleting it\n await this.updateSessionConnection(privateId, false);\n\n // Broadcast user disconnection\n this.broadcast({\n type: \"user_disconnected\",\n value: { publicId }\n }, subRoom);\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 if (isFromShard) {\n return this.handleShardRequest(req, shardId);\n }\n\n // Handle regular client request\n return this.handleDirectRequest(req);\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): Promise<Response> {\n const subRoom = await this.getSubRoom();\n const res = (body: any, status: number) => {\n return new Response(JSON.stringify(body), { status });\n };\n\n if (!subRoom) {\n return res({\n error: \"Not found\"\n }, 404);\n }\n\n // First try to match using the registered @Request handlers\n const response = await this.tryMatchRequestHandler(req, 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, this.room));\n if (!legacyResponse) {\n return res({\n error: \"Not found\"\n }, 404);\n }\n if (legacyResponse instanceof Response) {\n return legacyResponse;\n }\n return res(legacyResponse, 200);\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, 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 new Response(JSON.stringify({ error: \"Unauthorized\" }), { status: 403 });\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 new Response(\n JSON.stringify({ error: \"Invalid request body\", details: validation.error }),\n { status: 400 }\n );\n }\n bodyData = validation.data;\n }\n } catch (error) {\n return new Response(\n JSON.stringify({ error: \"Failed to parse request body\" }),\n { status: 400 }\n );\n }\n }\n\n // Execute the handler method\n try {\n const result = await awaitReturn(\n subRoom[handler.key](req, bodyData, params, this.room)\n );\n\n if (result instanceof Response) {\n return result;\n }\n\n return new Response(\n typeof result === 'string' ? result : JSON.stringify(result),\n {\n status: 200,\n headers: { 'Content-Type': typeof result === 'string' ? 'text/plain' : 'application/json' }\n }\n );\n } catch (error) {\n console.error('Error executing request handler:', error);\n return new Response(\n JSON.stringify({ error: \"Internal server error\" }),\n { status: 500 }\n );\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, shardId: string | null): Promise<Response> {\n const subRoom = await this.getSubRoom();\n\n if (!subRoom) {\n return new Response(JSON.stringify({ error: \"Not found\" }), { status: 404 });\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, 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, this.room));\n\n if (!legacyResponse) {\n return new Response(JSON.stringify({ error: \"Not found\" }), { status: 404 });\n }\n\n if (legacyResponse instanceof Response) {\n return legacyResponse;\n }\n\n return new Response(JSON.stringify(legacyResponse), { status: 200 });\n } catch (error) {\n console.error(`Error processing request from shard ${shardId}:`, error);\n return new Response(JSON.stringify({ error: \"Internal server error\" }), { status: 500 });\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}","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 // Notify the main server about the new connection with connection metadata\n this.ws.send(JSON.stringify({\n type: 'shard.clientConnected',\n privateId: conn.id,\n connectionInfo: {\n ip: ctx.request?.headers.get('x-forwarded-for') || 'unknown',\n userAgent: ctx.request?.headers.get('user-agent') || 'unknown',\n // Add any other relevant connection info\n }\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\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} = {}) {\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 },\n env: options.env\n } : {\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 } else {\n server = await createServer(io as any);\n }\n \n await server.onStart()\n \n return {\n server,\n room: (server as any).subRoom,\n createClient: async () => {\n const client = await io.connection(server as Server)\n return client\n }\n }\n}\n\nexport async function request(room: Server, 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 { 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\";\n\n// Types definitions\ntype BalancingStrategy = 'round-robin' | 'least-connections' | 'random';\ntype ShardStatus = 'active' | 'maintenance' | 'draining';\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(100);\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(100);\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(100);\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 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) {\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 { error: `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) {\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 { error: `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 {\n error: `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) {\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 response(400, { error: \"Request body is empty\" });\n }\n \n data = JSON.parse(body);\n } catch (parseError) {\n return response(400, { error: \"Invalid JSON in request body\" });\n }\n \n // Verify roomId is provided\n if (!data.roomId) {\n return response(400, { error: \"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 response(404, { error: result.error });\n }\n \n // Return shard information to the client\n return response(200, {\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 response(500, { error: \"Internal server error\", details: error instanceof Error ? error.message : String(error) });\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}"],"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","MockPartyClient","events","id","conn","constructor","server","Map","generateShortUUID","MockConnection","addEventListener","event","cb","set","removeEventListener","delete","_trigger","data","get","send","onMessage","JSON","stringify","MockLobby","socket","MockContext","parties","room","options","main","lobbyId","MockPartyRoom","clients","storage","context","env","Storage","connection","url","URL","request","Request","toString","method","headers","onConnect","broadcast","forEach","client","getConnection","getConnections","Array","from","values","map","clear","state","setState","value","close","onClose","ServerIo","ClientIo","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","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","garbageCollector","options","getSubRoom","activeConnections","activePrivateIds","Set","map","id","sessions","list","users","usersPropName","getUsersPropName","validPublicIds","expiredPublicIds","SESSION_EXPIRY_TIME","sessionExpiryTime","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","syncCb","values","getMemoryAll","buildObject","packet","type","clear","persistCb","_instance","getByPath","itemValue","createStatesSnapshot","DELETE_TOKEN","put","syncClass","onSync","throttle","onPersist","meta","propId","getSession","e","saveSession","data","sessionData","undefined","updateSessionConnection","onConnectClient","ctx","close","roomGuards","guard","isAuthorized","existingSession","generateShortUUID","classType","isClass","snapshot","setState","pId","onConnect","request","headers","onConnectShard","shardId","shard","clients","Map","onMessage","message","sender","handleShardMessage","json","parse","result","safeParse","success","actions","actionName","guards","bodyValidation","bodyResult","shardConnection","parsedMessage","shardState","handleShardClientConnect","handleShardClientMessage","handleShardClientDisconnect","warn","connectionInfo","virtualContext","Headers","ip","userAgent","method","url","virtualConnection","targetClientId","currentState","mergedState","Object","assign","set","payload","payloadString","clientState","onClose","onAlarm","onError","connection","onRequest","req","isFromShard","handleShardRequest","handleDirectRequest","res","body","status","Response","response","tryMatchRequestHandler","legacyResponse","requestHandlers","URL","pathname","split","slice","join","routeKey","handler","entries","firstColonIndex","indexOf","handlerMethod","substring","handlerPath","pathMatches","extractPathParams","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","stringify","type","privateId","connectionInfo","ip","request","userAgent","onMessage","sender","parsedMessage","wrappedMessage","publicId","state","payload","onClose","delete","currentConnections","size","worldRoom","world","response","fetch","method","env","SHARD_SECRET","body","shardId","connections","ok","errorData","json","status","onRequest","req","url","URL","path","pathname","text","Headers","forEach","value","key","clientIp","requestInit","onAlarm","testRoom","Room","options","createServer","io","server","Server","rooms","isShard","shard","ServerIo","path","parties","game","env","prototype","throttleSync","throttleStorage","shardServer","Shard","subRoom","onStart","room","createClient","client","connection","request","method","url","URL","Request","toString","response","onRequest","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","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","setTimeout","cleanupInactiveShards","onJoin","user","conn","ctx","canConnect","guardManageWorld","request","setState","state","isAdmin","interceptorPacket","_","obj","now","Date","timeout","shardsValue","hasChanges","Object","values","forEach","shard","registerRoom","req","roomConfig","json","newRoom","set","i","createShard","updateShardStats","body","error","scaleRoom","data","roomShards","filter","previousShardCount","length","currentShardCount","shardsToRemove","sort","a","b","slice","shardsToKeep","some","s","newShards","newShard","push","connect","text","trim","response","JSON","parse","parseError","autoCreate","result","findOptimalShard","success","console","details","message","String","mockRequest","activeShards","selectedShard","reduce","Math","floor","random","counter","nextCounter","template","replace","max","path","method","maxUsers","throttleStorage","throttleSync"]}
@@ -1,12 +1,12 @@
1
1
  import { createRoot } from "react-dom/client";
2
- import Counter from "./components/Counter";
2
+ import Admin from "./components/Admin";
3
3
  import "./styles.css";
4
4
 
5
5
 
6
6
  function App() {
7
7
  return (
8
8
  <main>
9
- <Counter />
9
+ <Admin />
10
10
  </main>
11
11
  );
12
12
  }